summaryrefslogtreecommitdiffstats
path: root/xbmc/video/dialogs
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/video/dialogs')
-rw-r--r--xbmc/video/dialogs/CMakeLists.txt26
-rw-r--r--xbmc/video/dialogs/GUIDialogAudioSettings.cpp437
-rw-r--r--xbmc/video/dialogs/GUIDialogAudioSettings.h82
-rw-r--r--xbmc/video/dialogs/GUIDialogCMSSettings.cpp233
-rw-r--r--xbmc/video/dialogs/GUIDialogCMSSettings.h39
-rw-r--r--xbmc/video/dialogs/GUIDialogFullScreenInfo.cpp30
-rw-r--r--xbmc/video/dialogs/GUIDialogFullScreenInfo.h21
-rw-r--r--xbmc/video/dialogs/GUIDialogSubtitleSettings.cpp426
-rw-r--r--xbmc/video/dialogs/GUIDialogSubtitleSettings.h70
-rw-r--r--xbmc/video/dialogs/GUIDialogSubtitles.cpp703
-rw-r--r--xbmc/video/dialogs/GUIDialogSubtitles.h72
-rw-r--r--xbmc/video/dialogs/GUIDialogTeletext.cpp197
-rw-r--r--xbmc/video/dialogs/GUIDialogTeletext.h39
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoBookmarks.cpp590
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoBookmarks.h76
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoInfo.cpp2398
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoInfo.h115
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoOSD.cpp101
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoOSD.h25
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoSettings.cpp573
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoSettings.h55
21 files changed, 6308 insertions, 0 deletions
diff --git a/xbmc/video/dialogs/CMakeLists.txt b/xbmc/video/dialogs/CMakeLists.txt
new file mode 100644
index 0000000..b2ec11f
--- /dev/null
+++ b/xbmc/video/dialogs/CMakeLists.txt
@@ -0,0 +1,26 @@
+set(SOURCES GUIDialogAudioSettings.cpp
+ GUIDialogFullScreenInfo.cpp
+ GUIDialogSubtitles.cpp
+ GUIDialogSubtitleSettings.cpp
+ GUIDialogTeletext.cpp
+ GUIDialogVideoBookmarks.cpp
+ GUIDialogVideoInfo.cpp
+ GUIDialogVideoOSD.cpp
+ GUIDialogVideoSettings.cpp)
+
+set(HEADERS GUIDialogAudioSettings.h
+ GUIDialogFullScreenInfo.h
+ GUIDialogSubtitles.h
+ GUIDialogSubtitleSettings.h
+ GUIDialogTeletext.h
+ GUIDialogVideoBookmarks.h
+ GUIDialogVideoInfo.h
+ GUIDialogVideoOSD.h
+ GUIDialogVideoSettings.h)
+
+if(OPENGL_FOUND OR CORE_SYSTEM_NAME STREQUAL windows OR CORE_SYSTEM_NAME STREQUAL windowsstore)
+ list(APPEND SOURCES GUIDialogCMSSettings.cpp)
+ list(APPEND HEADERS GUIDialogCMSSettings.h)
+endif()
+
+core_add_library(video_dialogs)
diff --git a/xbmc/video/dialogs/GUIDialogAudioSettings.cpp b/xbmc/video/dialogs/GUIDialogAudioSettings.cpp
new file mode 100644
index 0000000..83b888b
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogAudioSettings.cpp
@@ -0,0 +1,437 @@
+/*
+ * 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 "GUIDialogAudioSettings.h"
+
+#include "GUIPassword.h"
+#include "ServiceBroker.h"
+#include "addons/Skin.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPlayer.h"
+#include "application/ApplicationVolumeHandling.h"
+#include "cores/AudioEngine/Utils/AEUtil.h"
+#include "cores/IPlayer.h"
+#include "dialogs/GUIDialogYesNo.h"
+#include "guilib/GUIMessage.h"
+#include "guilib/LocalizeStrings.h"
+#include "profiles/ProfileManager.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/MediaSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "settings/lib/SettingDefinitions.h"
+#include "settings/lib/SettingsManager.h"
+#include "utils/LangCodeExpander.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+#include "video/VideoDatabase.h"
+
+#include <string>
+#include <vector>
+
+#define SETTING_AUDIO_VOLUME "audio.volume"
+#define SETTING_AUDIO_VOLUME_AMPLIFICATION "audio.volumeamplification"
+#define SETTING_AUDIO_CENTERMIXLEVEL "audio.centermixlevel"
+#define SETTING_AUDIO_DELAY "audio.delay"
+#define SETTING_AUDIO_STREAM "audio.stream"
+#define SETTING_AUDIO_PASSTHROUGH "audio.digitalanalog"
+#define SETTING_AUDIO_MAKE_DEFAULT "audio.makedefault"
+
+CGUIDialogAudioSettings::CGUIDialogAudioSettings()
+ : CGUIDialogSettingsManualBase(WINDOW_DIALOG_AUDIO_OSD_SETTINGS, "DialogSettings.xml")
+{ }
+
+CGUIDialogAudioSettings::~CGUIDialogAudioSettings() = default;
+
+void CGUIDialogAudioSettings::FrameMove()
+{
+ // update the volume setting if necessary
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appVolume = components.GetComponent<CApplicationVolumeHandling>();
+ float newVolume = appVolume->GetVolumeRatio();
+ if (newVolume != m_volume)
+ GetSettingsManager()->SetNumber(SETTING_AUDIO_VOLUME, static_cast<double>(newVolume));
+
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (appPlayer->HasPlayer())
+ {
+ const CVideoSettings videoSettings = appPlayer->GetVideoSettings();
+
+ // these settings can change on the fly
+ //! @todo (needs special handling): m_settingsManager->SetInt(SETTING_AUDIO_STREAM, g_application.GetAppPlayer().GetAudioStream());
+ GetSettingsManager()->SetNumber(SETTING_AUDIO_DELAY,
+ static_cast<double>(videoSettings.m_AudioDelay));
+ GetSettingsManager()->SetBool(SETTING_AUDIO_PASSTHROUGH, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_AUDIOOUTPUT_PASSTHROUGH));
+ }
+
+ CGUIDialogSettingsManualBase::FrameMove();
+}
+
+std::string CGUIDialogAudioSettings::FormatDelay(float value, float interval)
+{
+ if (fabs(value) < 0.5f * interval)
+ return StringUtils::Format(g_localizeStrings.Get(22003), 0.0);
+ if (value < 0)
+ return StringUtils::Format(g_localizeStrings.Get(22004), fabs(value));
+
+ return StringUtils::Format(g_localizeStrings.Get(22005), value);
+}
+
+std::string CGUIDialogAudioSettings::FormatDecibel(float value)
+{
+ return StringUtils::Format(g_localizeStrings.Get(14054), value);
+}
+
+std::string CGUIDialogAudioSettings::FormatPercentAsDecibel(float value)
+{
+ return StringUtils::Format(g_localizeStrings.Get(14054), CAEUtil::PercentToGain(value));
+}
+
+void CGUIDialogAudioSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingChanged(setting);
+
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == SETTING_AUDIO_VOLUME)
+ {
+ m_volume = static_cast<float>(std::static_pointer_cast<const CSettingNumber>(setting)->GetValue());
+ const auto appVolume = components.GetComponent<CApplicationVolumeHandling>();
+ appVolume->SetVolume(m_volume, false); // false - value is not in percent
+ }
+ else if (settingId == SETTING_AUDIO_VOLUME_AMPLIFICATION)
+ {
+ float value = static_cast<float>(std::static_pointer_cast<const CSettingNumber>(setting)->GetValue());
+ appPlayer->SetDynamicRangeCompression((long)(value * 100));
+ }
+ else if (settingId == SETTING_AUDIO_CENTERMIXLEVEL)
+ {
+ CVideoSettings vs = appPlayer->GetVideoSettings();
+ vs.m_CenterMixLevel = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
+ appPlayer->SetVideoSettings(vs);
+ }
+ else if (settingId == SETTING_AUDIO_DELAY)
+ {
+ float value = static_cast<float>(std::static_pointer_cast<const CSettingNumber>(setting)->GetValue());
+ appPlayer->SetAVDelay(value);
+ }
+ else if (settingId == SETTING_AUDIO_STREAM)
+ {
+ m_audioStream = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
+ // only change the audio stream if a different one has been asked for
+ if (appPlayer->GetAudioStream() != m_audioStream)
+ {
+ appPlayer->SetAudioStream(m_audioStream); // Set the audio stream to the one selected
+ }
+ }
+ else if (settingId == SETTING_AUDIO_PASSTHROUGH)
+ {
+ m_passthrough = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ CServiceBroker::GetSettingsComponent()->GetSettings()->SetBool(CSettings::SETTING_AUDIOOUTPUT_PASSTHROUGH, m_passthrough);
+ }
+}
+
+void CGUIDialogAudioSettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingAction(setting);
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == SETTING_AUDIO_MAKE_DEFAULT)
+ Save();
+}
+
+bool CGUIDialogAudioSettings::Save()
+{
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ if (!g_passwordManager.CheckSettingLevelLock(SettingLevel::Expert) &&
+ profileManager->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE)
+ return true;
+
+ // prompt user if they are sure
+ if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{12376}, CVariant{12377}))
+ return true;
+
+ // reset the settings
+ CVideoDatabase db;
+ if (!db.Open())
+ return true;
+
+ db.EraseAllVideoSettings();
+ db.Close();
+
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ CMediaSettings::GetInstance().GetDefaultVideoSettings() = appPlayer->GetVideoSettings();
+ CMediaSettings::GetInstance().GetDefaultVideoSettings().m_AudioStream = -1;
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+
+ return true;
+}
+
+void CGUIDialogAudioSettings::SetupView()
+{
+ CGUIDialogSettingsManualBase::SetupView();
+
+ SetHeading(13396);
+ SET_CONTROL_HIDDEN(CONTROL_SETTINGS_OKAY_BUTTON);
+ SET_CONTROL_HIDDEN(CONTROL_SETTINGS_CUSTOM_BUTTON);
+ SET_CONTROL_LABEL(CONTROL_SETTINGS_CANCEL_BUTTON, 15067);
+}
+
+void CGUIDialogAudioSettings::InitializeSettings()
+{
+ CGUIDialogSettingsManualBase::InitializeSettings();
+
+ const std::shared_ptr<CSettingCategory> category = AddCategory("audiosubtitlesettings", -1);
+ if (category == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogAudioSettings: unable to setup settings");
+ return;
+ }
+
+ // get all necessary setting groups
+ const std::shared_ptr<CSettingGroup> groupAudio = AddGroup(category);
+ if (groupAudio == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogAudioSettings: unable to setup settings");
+ return;
+ }
+ const std::shared_ptr<CSettingGroup> groupSubtitles = AddGroup(category);
+ if (groupSubtitles == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogAudioSettings: unable to setup settings");
+ return;
+ }
+ const std::shared_ptr<CSettingGroup> groupSaveAsDefault = AddGroup(category);
+ if (groupSaveAsDefault == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogAudioSettings: unable to setup settings");
+ return;
+ }
+
+ bool usePopup = g_SkinInfo->HasSkinFile("DialogSlider.xml");
+
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ const CVideoSettings videoSettings = appPlayer->GetVideoSettings();
+ if (appPlayer->HasPlayer())
+ {
+ appPlayer->GetAudioCapabilities(m_audioCaps);
+ }
+
+ // register IsPlayingPassthrough condition
+ GetSettingsManager()->AddDynamicCondition("IsPlayingPassthrough", IsPlayingPassthrough);
+
+ CSettingDependency dependencyAudioOutputPassthroughDisabled(SettingDependencyType::Enable, GetSettingsManager());
+ dependencyAudioOutputPassthroughDisabled.Or()
+ ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_AUDIO_PASSTHROUGH, "false", SettingDependencyOperator::Equals, false, GetSettingsManager())))
+ ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition("IsPlayingPassthrough", "", "", true, GetSettingsManager())));
+ SettingDependencies depsAudioOutputPassthroughDisabled;
+ depsAudioOutputPassthroughDisabled.push_back(dependencyAudioOutputPassthroughDisabled);
+
+ // audio settings
+ // audio volume setting
+ const auto appVolume = components.GetComponent<CApplicationVolumeHandling>();
+ m_volume = appVolume->GetVolumeRatio();
+ std::shared_ptr<CSettingNumber> settingAudioVolume =
+ AddSlider(groupAudio, SETTING_AUDIO_VOLUME, 13376, SettingLevel::Basic, m_volume, 14054,
+ CApplicationVolumeHandling::VOLUME_MINIMUM,
+ CApplicationVolumeHandling::VOLUME_MAXIMUM / 100.0f,
+ CApplicationVolumeHandling::VOLUME_MAXIMUM);
+ settingAudioVolume->SetDependencies(depsAudioOutputPassthroughDisabled);
+ std::static_pointer_cast<CSettingControlSlider>(settingAudioVolume->GetControl())->SetFormatter(SettingFormatterPercentAsDecibel);
+
+ // audio volume amplification setting
+ if (SupportsAudioFeature(IPC_AUD_AMP))
+ {
+ std::shared_ptr<CSettingNumber> settingAudioVolumeAmplification = AddSlider(groupAudio, SETTING_AUDIO_VOLUME_AMPLIFICATION, 660, SettingLevel::Basic, videoSettings.m_VolumeAmplification, 14054, VOLUME_DRC_MINIMUM * 0.01f, (VOLUME_DRC_MAXIMUM - VOLUME_DRC_MINIMUM) / 6000.0f, VOLUME_DRC_MAXIMUM * 0.01f);
+ settingAudioVolumeAmplification->SetDependencies(depsAudioOutputPassthroughDisabled);
+ }
+
+ // downmix: center mix level
+ {
+ AddSlider(groupAudio, SETTING_AUDIO_CENTERMIXLEVEL, 39112, SettingLevel::Basic,
+ videoSettings.m_CenterMixLevel, 14050, -10, 1, 30,
+ -1, false, false, true, 39113);
+ }
+
+ // audio delay setting
+ if (SupportsAudioFeature(IPC_AUD_OFFSET))
+ {
+ std::shared_ptr<CSettingNumber> settingAudioDelay = AddSlider(
+ groupAudio, SETTING_AUDIO_DELAY, 297, SettingLevel::Basic, videoSettings.m_AudioDelay, 0,
+ -CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoAudioDelayRange,
+ AUDIO_DELAY_STEP,
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoAudioDelayRange, 297,
+ usePopup);
+ std::static_pointer_cast<CSettingControlSlider>(settingAudioDelay->GetControl())->SetFormatter(SettingFormatterDelay);
+ }
+
+ // audio stream setting
+ if (SupportsAudioFeature(IPC_AUD_SELECT_STREAM))
+ AddAudioStreams(groupAudio, SETTING_AUDIO_STREAM);
+
+ // audio digital/analog setting
+ if (SupportsAudioFeature(IPC_AUD_SELECT_OUTPUT))
+ {
+ m_passthrough = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_AUDIOOUTPUT_PASSTHROUGH);
+ AddToggle(groupAudio, SETTING_AUDIO_PASSTHROUGH, 348, SettingLevel::Basic, m_passthrough);
+ }
+
+ // subtitle stream setting
+ AddButton(groupSaveAsDefault, SETTING_AUDIO_MAKE_DEFAULT, 12376, SettingLevel::Basic);
+}
+
+bool CGUIDialogAudioSettings::SupportsAudioFeature(int feature)
+{
+ for (Features::iterator itr = m_audioCaps.begin(); itr != m_audioCaps.end(); ++itr)
+ {
+ if (*itr == feature || *itr == IPC_AUD_ALL)
+ return true;
+ }
+
+ return false;
+}
+
+void CGUIDialogAudioSettings::AddAudioStreams(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& settingId)
+{
+ if (group == NULL || settingId.empty())
+ return;
+
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ m_audioStream = appPlayer->GetAudioStream();
+ if (m_audioStream < 0)
+ m_audioStream = 0;
+
+ AddList(group, settingId, 460, SettingLevel::Basic, m_audioStream, AudioStreamsOptionFiller, 460);
+}
+
+bool CGUIDialogAudioSettings::IsPlayingPassthrough(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ return appPlayer->IsPassthrough();
+}
+
+void CGUIDialogAudioSettings::AudioStreamsOptionFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ int audioStreamCount = appPlayer->GetAudioStreamCount();
+
+ std::string strFormat = "{:s} - {:s} - {:d} " + g_localizeStrings.Get(10127);
+ std::string strUnknown = "[" + g_localizeStrings.Get(13205) + "]";
+
+ // cycle through each audio stream and add it to our list control
+ for (int i = 0; i < audioStreamCount; ++i)
+ {
+ std::string strItem;
+ std::string strLanguage;
+
+ AudioStreamInfo info;
+ appPlayer->GetAudioStreamInfo(i, info);
+
+ if (!g_LangCodeExpander.Lookup(info.language, strLanguage))
+ strLanguage = strUnknown;
+
+ if (info.name.length() == 0)
+ info.name = strUnknown;
+
+ strItem = StringUtils::Format(strFormat, strLanguage, info.name, info.channels);
+
+ strItem += FormatFlags(info.flags);
+ strItem += StringUtils::Format(" ({}/{})", i + 1, audioStreamCount);
+ list.emplace_back(strItem, i);
+ }
+
+ if (list.empty())
+ {
+ list.emplace_back(g_localizeStrings.Get(231), -1);
+ current = -1;
+ }
+}
+
+std::string CGUIDialogAudioSettings::SettingFormatterDelay(
+ const std::shared_ptr<const CSettingControlSlider>& control,
+ const CVariant& value,
+ const CVariant& minimum,
+ const CVariant& step,
+ const CVariant& maximum)
+{
+ if (!value.isDouble())
+ return "";
+
+ float fValue = value.asFloat();
+ float fStep = step.asFloat();
+
+ if (fabs(fValue) < 0.5f * fStep)
+ return StringUtils::Format(g_localizeStrings.Get(22003), 0.0);
+ if (fValue < 0)
+ return StringUtils::Format(g_localizeStrings.Get(22004), fabs(fValue));
+
+ return StringUtils::Format(g_localizeStrings.Get(22005), fValue);
+}
+
+std::string CGUIDialogAudioSettings::SettingFormatterPercentAsDecibel(
+ const std::shared_ptr<const CSettingControlSlider>& control,
+ const CVariant& value,
+ const CVariant& minimum,
+ const CVariant& step,
+ const CVariant& maximum)
+{
+ if (control == NULL || !value.isDouble())
+ return "";
+
+ std::string formatString = control->GetFormatString();
+ if (control->GetFormatLabel() > -1)
+ formatString = g_localizeStrings.Get(control->GetFormatLabel());
+
+ return StringUtils::Format(formatString, CAEUtil::PercentToGain(value.asFloat()));
+}
+
+std::string CGUIDialogAudioSettings::FormatFlags(StreamFlags flags)
+{
+ std::vector<std::string> localizedFlags;
+ if (flags & StreamFlags::FLAG_DEFAULT)
+ localizedFlags.emplace_back(g_localizeStrings.Get(39105));
+ if (flags & StreamFlags::FLAG_FORCED)
+ localizedFlags.emplace_back(g_localizeStrings.Get(39106));
+ if (flags & StreamFlags::FLAG_HEARING_IMPAIRED)
+ localizedFlags.emplace_back(g_localizeStrings.Get(39107));
+ if (flags & StreamFlags::FLAG_VISUAL_IMPAIRED)
+ localizedFlags.emplace_back(g_localizeStrings.Get(39108));
+ if (flags & StreamFlags::FLAG_ORIGINAL)
+ localizedFlags.emplace_back(g_localizeStrings.Get(39111));
+
+ std::string formated = StringUtils::Join(localizedFlags, ", ");
+
+ if (!formated.empty())
+ formated = StringUtils::Format(" [{}]", formated);
+
+ return formated;
+}
diff --git a/xbmc/video/dialogs/GUIDialogAudioSettings.h b/xbmc/video/dialogs/GUIDialogAudioSettings.h
new file mode 100644
index 0000000..de69b77
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogAudioSettings.h
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "cores/VideoPlayer/Interface/StreamInfo.h"
+#include "settings/dialogs/GUIDialogSettingsManualBase.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+class CVariant;
+struct IntegerSettingOption;
+
+class CGUIDialogAudioSettings : public CGUIDialogSettingsManualBase
+{
+public:
+ CGUIDialogAudioSettings();
+ ~CGUIDialogAudioSettings() override;
+
+ // specialization of CGUIWindow
+ void FrameMove() override;
+
+ static std::string FormatDelay(float value, float interval);
+ static std::string FormatDecibel(float value);
+ static std::string FormatPercentAsDecibel(float value);
+
+protected:
+ // implementations of ISettingCallback
+ void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+ void OnSettingAction(const std::shared_ptr<const CSetting>& setting) override;
+
+ // specialization of CGUIDialogSettingsBase
+ bool AllowResettingSettings() const override { return false; }
+ bool Save() override;
+ void SetupView() override;
+
+ // specialization of CGUIDialogSettingsManualBase
+ void InitializeSettings() override;
+
+ bool SupportsAudioFeature(int feature);
+
+ void AddAudioStreams(const std::shared_ptr<CSettingGroup>& group, const std::string& settingId);
+
+ static bool IsPlayingPassthrough(const std::string& condition,
+ const std::string& value,
+ const std::shared_ptr<const CSetting>& setting,
+ void* data);
+
+ static void AudioStreamsOptionFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data);
+
+ static std::string SettingFormatterDelay(
+ const std::shared_ptr<const CSettingControlSlider>& control,
+ const CVariant& value,
+ const CVariant& minimum,
+ const CVariant& step,
+ const CVariant& maximum);
+ static std::string SettingFormatterPercentAsDecibel(
+ const std::shared_ptr<const CSettingControlSlider>& control,
+ const CVariant& value,
+ const CVariant& minimum,
+ const CVariant& step,
+ const CVariant& maximum);
+
+ float m_volume;
+ int m_audioStream;
+ bool m_passthrough = false;
+
+ typedef std::vector<int> Features;
+ Features m_audioCaps;
+private:
+ static std::string FormatFlags(StreamFlags flags);
+};
diff --git a/xbmc/video/dialogs/GUIDialogCMSSettings.cpp b/xbmc/video/dialogs/GUIDialogCMSSettings.cpp
new file mode 100644
index 0000000..ac1a7ad
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogCMSSettings.cpp
@@ -0,0 +1,233 @@
+/*
+ * 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 "GUIDialogCMSSettings.h"
+
+#include "FileItem.h"
+#include "GUIPassword.h"
+#include "ServiceBroker.h"
+#include "addons/Skin.h"
+#include "cores/VideoPlayer/VideoRenderers/ColorManager.h"
+#include "cores/VideoPlayer/VideoRenderers/RenderManager.h"
+#include "filesystem/Directory.h"
+#include "guilib/GUIWindowManager.h"
+#include "profiles/ProfileManager.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "settings/lib/SettingDefinitions.h"
+#include "settings/lib/SettingsManager.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+#include "video/VideoDatabase.h"
+
+#include <vector>
+
+#define SETTING_VIDEO_CMSENABLE "videoscreen.cmsenabled"
+#define SETTING_VIDEO_CMSMODE "videoscreen.cmsmode"
+#define SETTING_VIDEO_CMS3DLUT "videoscreen.cms3dlut"
+#define SETTING_VIDEO_CMSWHITEPOINT "videoscreen.cmswhitepoint"
+#define SETTING_VIDEO_CMSPRIMARIES "videoscreen.cmsprimaries"
+#define SETTING_VIDEO_CMSGAMMAMODE "videoscreen.cmsgammamode"
+#define SETTING_VIDEO_CMSGAMMA "videoscreen.cmsgamma"
+#define SETTING_VIDEO_CMSLUTSIZE "videoscreen.cmslutsize"
+
+CGUIDialogCMSSettings::CGUIDialogCMSSettings()
+ : CGUIDialogSettingsManualBase(WINDOW_DIALOG_CMS_OSD_SETTINGS, "DialogSettings.xml")
+{ }
+
+CGUIDialogCMSSettings::~CGUIDialogCMSSettings() = default;
+
+void CGUIDialogCMSSettings::SetupView()
+{
+ CGUIDialogSettingsManualBase::SetupView();
+
+ SetHeading(36560);
+ SET_CONTROL_HIDDEN(CONTROL_SETTINGS_OKAY_BUTTON);
+ SET_CONTROL_HIDDEN(CONTROL_SETTINGS_CUSTOM_BUTTON);
+ SET_CONTROL_LABEL(CONTROL_SETTINGS_CANCEL_BUTTON, 15067);
+}
+
+void CGUIDialogCMSSettings::InitializeSettings()
+{
+ CGUIDialogSettingsManualBase::InitializeSettings();
+
+ const std::shared_ptr<CSettingCategory> category = AddCategory("cms", -1);
+ if (category == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogCMSSettings: unable to setup settings");
+ return;
+ }
+
+ // get all necessary setting groups
+ const std::shared_ptr<CSettingGroup> groupColorManagement = AddGroup(category);
+ if (groupColorManagement == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogCMSSettings: unable to setup settings");
+ return;
+ }
+
+ bool usePopup = g_SkinInfo->HasSkinFile("DialogSlider.xml");
+
+ TranslatableIntegerSettingOptions entries;
+
+ // create "depsCmsEnabled" for settings depending on CMS being enabled
+ CSettingDependency dependencyCmsEnabled(SettingDependencyType::Enable, GetSettingsManager());
+ dependencyCmsEnabled.Or()
+ ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_VIDEO_CMSENABLE, "true", SettingDependencyOperator::Equals, false, GetSettingsManager())));
+ SettingDependencies depsCmsEnabled;
+ depsCmsEnabled.push_back(dependencyCmsEnabled);
+
+ // create "depsCms3dlut" for 3dlut settings
+ CSettingDependency dependencyCms3dlut(SettingDependencyType::Visible, GetSettingsManager());
+ dependencyCms3dlut.And()
+ ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_VIDEO_CMSMODE, std::to_string(CMS_MODE_3DLUT), SettingDependencyOperator::Equals, false, GetSettingsManager())));
+ SettingDependencies depsCms3dlut;
+ depsCms3dlut.push_back(dependencyCmsEnabled);
+ depsCms3dlut.push_back(dependencyCms3dlut);
+
+ // create "depsCmsIcc" for display settings with icc profile
+ CSettingDependency dependencyCmsIcc(SettingDependencyType::Visible, GetSettingsManager());
+ dependencyCmsIcc.And()
+ ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_VIDEO_CMSMODE, std::to_string(CMS_MODE_PROFILE), SettingDependencyOperator::Equals, false, GetSettingsManager())));
+ SettingDependencies depsCmsIcc;
+ depsCmsIcc.push_back(dependencyCmsEnabled);
+ depsCmsIcc.push_back(dependencyCmsIcc);
+
+ // create "depsCmsGamma" for effective gamma adjustment (not available with bt.1886)
+ CSettingDependency dependencyCmsGamma(SettingDependencyType::Visible, GetSettingsManager());
+ dependencyCmsGamma.And()
+ ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_VIDEO_CMSGAMMAMODE, std::to_string(CMS_TRC_BT1886), SettingDependencyOperator::Equals, true, GetSettingsManager())));
+ SettingDependencies depsCmsGamma;
+ depsCmsGamma.push_back(dependencyCmsEnabled);
+ depsCmsGamma.push_back(dependencyCmsIcc);
+ depsCmsGamma.push_back(dependencyCmsGamma);
+
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+
+ // color management settings
+ AddToggle(groupColorManagement, SETTING_VIDEO_CMSENABLE, 36560, SettingLevel::Basic, settings->GetBool(SETTING_VIDEO_CMSENABLE));
+
+ int currentMode = settings->GetInt(SETTING_VIDEO_CMSMODE);
+ entries.clear();
+ // entries.push_back(TranslatableIntegerSettingOption(16039, CMS_MODE_OFF)); // FIXME: get from CMS class
+ entries.push_back(TranslatableIntegerSettingOption(36580, CMS_MODE_3DLUT));
+#ifdef HAVE_LCMS2
+ entries.push_back(TranslatableIntegerSettingOption(36581, CMS_MODE_PROFILE));
+#endif
+ std::shared_ptr<CSettingInt> settingCmsMode = AddSpinner(groupColorManagement, SETTING_VIDEO_CMSMODE, 36562, SettingLevel::Basic, currentMode, entries);
+ settingCmsMode->SetDependencies(depsCmsEnabled);
+
+ std::string current3dLUT = settings->GetString(SETTING_VIDEO_CMS3DLUT);
+ std::shared_ptr<CSettingString> settingCms3dlut = AddList(groupColorManagement, SETTING_VIDEO_CMS3DLUT, 36564, SettingLevel::Basic, current3dLUT, Cms3dLutsFiller, 36564);
+ settingCms3dlut->SetDependencies(depsCms3dlut);
+
+ // display settings
+ int currentWhitepoint = settings->GetInt(SETTING_VIDEO_CMSWHITEPOINT);
+ entries.clear();
+ entries.push_back(TranslatableIntegerSettingOption(36586, CMS_WHITEPOINT_D65));
+ entries.push_back(TranslatableIntegerSettingOption(36587, CMS_WHITEPOINT_D93));
+ std::shared_ptr<CSettingInt> settingCmsWhitepoint = AddSpinner(groupColorManagement, SETTING_VIDEO_CMSWHITEPOINT, 36568, SettingLevel::Basic, currentWhitepoint, entries);
+ settingCmsWhitepoint->SetDependencies(depsCmsIcc);
+
+ int currentPrimaries = settings->GetInt(SETTING_VIDEO_CMSPRIMARIES);
+ entries.clear();
+ entries.push_back(TranslatableIntegerSettingOption(36588, CMS_PRIMARIES_AUTO));
+ entries.push_back(TranslatableIntegerSettingOption(36589, CMS_PRIMARIES_BT709));
+ entries.push_back(TranslatableIntegerSettingOption(36579, CMS_PRIMARIES_BT2020));
+ entries.push_back(TranslatableIntegerSettingOption(36590, CMS_PRIMARIES_170M));
+ entries.push_back(TranslatableIntegerSettingOption(36591, CMS_PRIMARIES_BT470M));
+ entries.push_back(TranslatableIntegerSettingOption(36592, CMS_PRIMARIES_BT470BG));
+ entries.push_back(TranslatableIntegerSettingOption(36593, CMS_PRIMARIES_240M));
+ std::shared_ptr<CSettingInt> settingCmsPrimaries = AddSpinner(groupColorManagement, SETTING_VIDEO_CMSPRIMARIES, 36570, SettingLevel::Basic, currentPrimaries, entries);
+ settingCmsPrimaries->SetDependencies(depsCmsIcc);
+
+ int currentGammaMode = settings->GetInt(SETTING_VIDEO_CMSGAMMAMODE);
+ entries.clear();
+ entries.push_back(TranslatableIntegerSettingOption(36582, CMS_TRC_BT1886));
+ entries.push_back(TranslatableIntegerSettingOption(36583, CMS_TRC_INPUT_OFFSET));
+ entries.push_back(TranslatableIntegerSettingOption(36584, CMS_TRC_OUTPUT_OFFSET));
+ entries.push_back(TranslatableIntegerSettingOption(36585, CMS_TRC_ABSOLUTE));
+ std::shared_ptr<CSettingInt> settingCmsGammaMode = AddSpinner(groupColorManagement, SETTING_VIDEO_CMSGAMMAMODE, 36572, SettingLevel::Basic, currentGammaMode, entries);
+ settingCmsGammaMode->SetDependencies(depsCmsIcc);
+
+ float currentGamma = settings->GetInt(SETTING_VIDEO_CMSGAMMA)/100.0f;
+ if (currentGamma == 0.0f)
+ currentGamma = 2.20f;
+ std::shared_ptr<CSettingNumber> settingCmsGamma = AddSlider(groupColorManagement, SETTING_VIDEO_CMSGAMMA, 36574, SettingLevel::Basic, currentGamma, 36597, 1.6, 0.05, 2.8, 36574, usePopup);
+ settingCmsGamma->SetDependencies(depsCmsGamma);
+
+ int currentLutSize = settings->GetInt(SETTING_VIDEO_CMSLUTSIZE);
+ entries.clear();
+ entries.push_back(TranslatableIntegerSettingOption(36594, 4));
+ entries.push_back(TranslatableIntegerSettingOption(36595, 6));
+ entries.push_back(TranslatableIntegerSettingOption(36596, 8));
+ std::shared_ptr<CSettingInt> settingCmsLutSize = AddSpinner(groupColorManagement, SETTING_VIDEO_CMSLUTSIZE, 36576, SettingLevel::Basic, currentLutSize, entries);
+ settingCmsLutSize->SetDependencies(depsCmsIcc);
+}
+
+void CGUIDialogCMSSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingChanged(setting);
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == SETTING_VIDEO_CMSENABLE)
+ CServiceBroker::GetSettingsComponent()->GetSettings()->SetBool(SETTING_VIDEO_CMSENABLE, (std::static_pointer_cast<const CSettingBool>(setting)->GetValue()));
+ else if (settingId == SETTING_VIDEO_CMSMODE)
+ CServiceBroker::GetSettingsComponent()->GetSettings()->SetInt(SETTING_VIDEO_CMSMODE, std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
+ else if (settingId == SETTING_VIDEO_CMS3DLUT)
+ CServiceBroker::GetSettingsComponent()->GetSettings()->SetString(SETTING_VIDEO_CMS3DLUT, std::static_pointer_cast<const CSettingString>(setting)->GetValue());
+ else if (settingId == SETTING_VIDEO_CMSWHITEPOINT)
+ CServiceBroker::GetSettingsComponent()->GetSettings()->SetInt(SETTING_VIDEO_CMSWHITEPOINT, std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
+ else if (settingId == SETTING_VIDEO_CMSPRIMARIES)
+ CServiceBroker::GetSettingsComponent()->GetSettings()->SetInt(SETTING_VIDEO_CMSPRIMARIES, std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
+ else if (settingId == SETTING_VIDEO_CMSGAMMAMODE)
+ CServiceBroker::GetSettingsComponent()->GetSettings()->SetInt(SETTING_VIDEO_CMSGAMMAMODE, std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
+ else if (settingId == SETTING_VIDEO_CMSGAMMA)
+ CServiceBroker::GetSettingsComponent()->GetSettings()->SetInt(SETTING_VIDEO_CMSGAMMA, static_cast<float>(std::static_pointer_cast<const CSettingNumber>(setting)->GetValue())*100);
+ else if (settingId == SETTING_VIDEO_CMSLUTSIZE)
+ CServiceBroker::GetSettingsComponent()->GetSettings()->SetInt(SETTING_VIDEO_CMSLUTSIZE, std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
+}
+
+bool CGUIDialogCMSSettings::OnBack(int actionID)
+{
+ Save();
+ return CGUIDialogSettingsBase::OnBack(actionID);
+}
+
+bool CGUIDialogCMSSettings::Save()
+{
+ CLog::Log(LOGINFO, "CGUIDialogCMSSettings: Save() called");
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+
+ return true;
+}
+
+void CGUIDialogCMSSettings::Cms3dLutsFiller(const SettingConstPtr& setting,
+ std::vector<StringSettingOption>& list,
+ std::string& current,
+ void* data)
+{
+ // get 3dLut directory from settings
+ CFileItemList items;
+
+ // list .3dlut files
+ std::string current3dlut = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(SETTING_VIDEO_CMS3DLUT);
+ if (!current3dlut.empty())
+ current3dlut = URIUtils::GetDirectory(current3dlut);
+ XFILE::CDirectory::GetDirectory(current3dlut, items, ".3dlut", XFILE::DIR_FLAG_DEFAULTS);
+
+ for (int i = 0; i < items.Size(); i++)
+ {
+ list.emplace_back(items[i]->GetLabel(), items[i]->GetPath());
+ }
+}
diff --git a/xbmc/video/dialogs/GUIDialogCMSSettings.h b/xbmc/video/dialogs/GUIDialogCMSSettings.h
new file mode 100644
index 0000000..e718b29
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogCMSSettings.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "settings/dialogs/GUIDialogSettingsManualBase.h"
+
+struct StringSettingOption;
+
+class CGUIDialogCMSSettings : public CGUIDialogSettingsManualBase
+{
+public:
+ CGUIDialogCMSSettings();
+ ~CGUIDialogCMSSettings() override;
+
+protected:
+ // implementations of ISettingCallback
+ void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+
+ // specialization of CGUIDialogSettingsBase
+ bool AllowResettingSettings() const override { return false; }
+ bool OnBack(int actionID) override;
+ bool Save() override;
+ void SetupView() override;
+
+ // specialization of CGUIDialogSettingsManualBase
+ void InitializeSettings() override;
+
+private:
+ static void Cms3dLutsFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<StringSettingOption>& list,
+ std::string& current,
+ void* data);
+};
diff --git a/xbmc/video/dialogs/GUIDialogFullScreenInfo.cpp b/xbmc/video/dialogs/GUIDialogFullScreenInfo.cpp
new file mode 100644
index 0000000..70ebafe
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogFullScreenInfo.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 "GUIDialogFullScreenInfo.h"
+
+#include "input/Key.h"
+
+CGUIDialogFullScreenInfo::CGUIDialogFullScreenInfo(void)
+ : CGUIDialog(WINDOW_DIALOG_FULLSCREEN_INFO, "DialogFullScreenInfo.xml")
+{
+ m_loadType = KEEP_IN_MEMORY;
+}
+
+CGUIDialogFullScreenInfo::~CGUIDialogFullScreenInfo(void) = default;
+
+bool CGUIDialogFullScreenInfo::OnAction(const CAction &action)
+{
+ if (action.GetID() == ACTION_SHOW_INFO)
+ {
+ Close();
+ return true;
+ }
+ return CGUIDialog::OnAction(action);
+}
+
diff --git a/xbmc/video/dialogs/GUIDialogFullScreenInfo.h b/xbmc/video/dialogs/GUIDialogFullScreenInfo.h
new file mode 100644
index 0000000..ca5b5e8
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogFullScreenInfo.h
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "guilib/GUIDialog.h"
+
+class CGUIDialogFullScreenInfo :
+ public CGUIDialog
+{
+public:
+ CGUIDialogFullScreenInfo(void);
+ ~CGUIDialogFullScreenInfo(void) override;
+ bool OnAction(const CAction &action) override;
+};
+
diff --git a/xbmc/video/dialogs/GUIDialogSubtitleSettings.cpp b/xbmc/video/dialogs/GUIDialogSubtitleSettings.cpp
new file mode 100644
index 0000000..24f2618
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogSubtitleSettings.cpp
@@ -0,0 +1,426 @@
+/*
+ * 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 "GUIDialogSubtitleSettings.h"
+
+#include "FileItem.h"
+#include "GUIDialogSubtitles.h"
+#include "GUIPassword.h"
+#include "ServiceBroker.h"
+#include "URL.h"
+#include "addons/Skin.h"
+#include "addons/VFSEntry.h"
+#include "application/Application.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPlayer.h"
+#include "cores/IPlayer.h"
+#include "dialogs/GUIDialogFileBrowser.h"
+#include "dialogs/GUIDialogYesNo.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "profiles/ProfileManager.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/MediaSettings.h"
+#include "settings/MediaSourceSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "settings/lib/SettingDefinitions.h"
+#include "settings/lib/SettingsManager.h"
+#include "utils/FileUtils.h"
+#include "utils/LangCodeExpander.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+#include "video/VideoDatabase.h"
+
+#include <string>
+#include <vector>
+
+#define SETTING_SUBTITLE_ENABLE "subtitles.enable"
+#define SETTING_SUBTITLE_DELAY "subtitles.delay"
+#define SETTING_SUBTITLE_STREAM "subtitles.stream"
+#define SETTING_SUBTITLE_BROWSER "subtitles.browser"
+#define SETTING_SUBTITLE_SEARCH "subtitles.search"
+#define SETTING_MAKE_DEFAULT "audio.makedefault"
+
+CGUIDialogSubtitleSettings::CGUIDialogSubtitleSettings()
+ : CGUIDialogSettingsManualBase(WINDOW_DIALOG_SUBTITLE_OSD_SETTINGS, "DialogSettings.xml")
+{ }
+
+CGUIDialogSubtitleSettings::~CGUIDialogSubtitleSettings() = default;
+
+void CGUIDialogSubtitleSettings::FrameMove()
+{
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (appPlayer->HasPlayer())
+ {
+ const CVideoSettings videoSettings = appPlayer->GetVideoSettings();
+
+ // these settings can change on the fly
+ //! @todo m_settingsManager->SetBool(SETTING_SUBTITLE_ENABLE, g_application.GetAppPlayer().GetSubtitleVisible());
+ // \-> Unless subtitle visibility can change on the fly, while Dialog is up, this code should be removed.
+ GetSettingsManager()->SetNumber(SETTING_SUBTITLE_DELAY,
+ static_cast<double>(videoSettings.m_SubtitleDelay));
+ //! @todo (needs special handling): m_settingsManager->SetInt(SETTING_SUBTITLE_STREAM, g_application.GetAppPlayer().GetSubtitle());
+ }
+
+ CGUIDialogSettingsManualBase::FrameMove();
+}
+
+bool CGUIDialogSubtitleSettings::OnMessage(CGUIMessage& message)
+{
+ if (message.GetMessage() == GUI_MSG_SUBTITLE_DOWNLOADED)
+ {
+ Close();
+ }
+ return CGUIDialogSettingsManualBase::OnMessage(message);
+}
+
+void CGUIDialogSubtitleSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ CGUIDialogSettingsManualBase::OnSettingChanged(setting);
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == SETTING_SUBTITLE_ENABLE)
+ {
+ bool value = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ if (value)
+ {
+ // Ensure that we use/store the subtitle stream the user currently sees in the dialog.
+ appPlayer->SetSubtitle(m_subtitleStream);
+ }
+ appPlayer->SetSubtitleVisible(value);
+ }
+ else if (settingId == SETTING_SUBTITLE_DELAY)
+ {
+ float value = static_cast<float>(std::static_pointer_cast<const CSettingNumber>(setting)->GetValue());
+ appPlayer->SetSubTitleDelay(value);
+ }
+ else if (settingId == SETTING_SUBTITLE_STREAM)
+ {
+ m_subtitleStream = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
+ appPlayer->SetSubtitle(m_subtitleStream);
+ }
+}
+
+std::string CGUIDialogSubtitleSettings::BrowseForSubtitle()
+{
+ std::string extras;
+ for (const auto& vfsAddon : CServiceBroker::GetVFSAddonCache().GetAddonInstances())
+ {
+ if (vfsAddon->ID() == "vfs.rar" || vfsAddon->ID() == "vfs.libarchive")
+ extras += '|' + vfsAddon->GetExtensions();
+ }
+
+ std::string strPath;
+ if (URIUtils::IsInRAR(g_application.CurrentFileItem().GetPath()) || URIUtils::IsInZIP(g_application.CurrentFileItem().GetPath()))
+ {
+ strPath = CURL(g_application.CurrentFileItem().GetPath()).GetHostName();
+ }
+ else if (!URIUtils::IsPlugin(g_application.CurrentFileItem().GetPath()))
+ {
+ strPath = g_application.CurrentFileItem().GetPath();
+ }
+
+ std::string strMask =
+ ".utf|.utf8|.utf-8|.sub|.srt|.smi|.rt|.txt|.ssa|.aqt|.jss|.ass|.vtt|.idx|.zip|.sup";
+
+ if (g_application.GetCurrentPlayer() == "VideoPlayer")
+ strMask = ".srt|.zip|.ifo|.smi|.sub|.idx|.ass|.ssa|.vtt|.txt|.sup";
+
+ strMask += extras;
+
+ VECSOURCES shares(*CMediaSourceSettings::GetInstance().GetSources("video"));
+ if (CMediaSettings::GetInstance().GetAdditionalSubtitleDirectoryChecked() != -1 && !CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SUBTITLES_CUSTOMPATH).empty())
+ {
+ CMediaSource share;
+ std::vector<std::string> paths;
+ if (!strPath.empty())
+ {
+ paths.push_back(URIUtils::GetDirectory(strPath));
+ }
+ paths.push_back(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SUBTITLES_CUSTOMPATH));
+ share.FromNameAndPaths("video",g_localizeStrings.Get(21367),paths);
+ shares.push_back(share);
+ strPath = share.strPath;
+ URIUtils::AddSlashAtEnd(strPath);
+ }
+
+ if (CGUIDialogFileBrowser::ShowAndGetFile(shares, strMask, g_localizeStrings.Get(293), strPath, false, true)) // "subtitles"
+ {
+ if (URIUtils::HasExtension(strPath, ".sub"))
+ {
+ if (CFileUtils::Exists(URIUtils::ReplaceExtension(strPath, ".idx")))
+ strPath = URIUtils::ReplaceExtension(strPath, ".idx");
+ }
+
+ return strPath;
+ }
+
+ return "";
+}
+
+void CGUIDialogSubtitleSettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingAction(setting);
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == SETTING_SUBTITLE_BROWSER)
+ {
+ std::string strPath = BrowseForSubtitle();
+ if (!strPath.empty())
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ appPlayer->AddSubtitle(strPath);
+ Close();
+ }
+ }
+ else if (settingId == SETTING_SUBTITLE_SEARCH)
+ {
+ auto dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSubtitles>(WINDOW_DIALOG_SUBTITLES);
+ if (dialog)
+ {
+ dialog->Open();
+ m_subtitleStreamSetting->UpdateDynamicOptions();
+ }
+ }
+ else if (settingId == SETTING_MAKE_DEFAULT)
+ Save();
+}
+
+bool CGUIDialogSubtitleSettings::Save()
+{
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ if (!g_passwordManager.CheckSettingLevelLock(SettingLevel::Expert) &&
+ profileManager->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE)
+ return true;
+
+ // prompt user if they are sure
+ if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{12376}, CVariant{12377}))
+ return true;
+
+ // reset the settings
+ CVideoDatabase db;
+ if (!db.Open())
+ return true;
+
+ db.EraseAllVideoSettings();
+ db.Close();
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ CMediaSettings::GetInstance().GetDefaultVideoSettings() = appPlayer->GetVideoSettings();
+ CMediaSettings::GetInstance().GetDefaultVideoSettings().m_SubtitleStream = -1;
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+
+ return true;
+}
+
+void CGUIDialogSubtitleSettings::SetupView()
+{
+ CGUIDialogSettingsManualBase::SetupView();
+
+ SetHeading(24133);
+ SET_CONTROL_HIDDEN(CONTROL_SETTINGS_OKAY_BUTTON);
+ SET_CONTROL_HIDDEN(CONTROL_SETTINGS_CUSTOM_BUTTON);
+ SET_CONTROL_LABEL(CONTROL_SETTINGS_CANCEL_BUTTON, 15067);
+}
+
+void CGUIDialogSubtitleSettings::InitializeSettings()
+{
+ CGUIDialogSettingsManualBase::InitializeSettings();
+
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ const std::shared_ptr<CSettingCategory> category = AddCategory("audiosubtitlesettings", -1);
+ if (category == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogSubtitleSettings: unable to setup settings");
+ return;
+ }
+
+ // get all necessary setting groups
+ const std::shared_ptr<CSettingGroup> groupAudio = AddGroup(category);
+ if (groupAudio == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogSubtitleSettings: unable to setup settings");
+ return;
+ }
+ const std::shared_ptr<CSettingGroup> groupSubtitles = AddGroup(category);
+ if (groupSubtitles == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogSubtitleSettings: unable to setup settings");
+ return;
+ }
+ const std::shared_ptr<CSettingGroup> groupSaveAsDefault = AddGroup(category);
+ if (groupSaveAsDefault == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogSubtitleSettings: unable to setup settings");
+ return;
+ }
+
+ bool usePopup = g_SkinInfo->HasSkinFile("DialogSlider.xml");
+
+ const CVideoSettings videoSettings = appPlayer->GetVideoSettings();
+
+ if (appPlayer->HasPlayer())
+ {
+ appPlayer->GetSubtitleCapabilities(m_subtitleCapabilities);
+ }
+
+ // subtitle settings
+ m_subtitleVisible = appPlayer->GetSubtitleVisible();
+
+ // subtitle enabled setting
+ AddToggle(groupSubtitles, SETTING_SUBTITLE_ENABLE, 13397, SettingLevel::Basic, m_subtitleVisible);
+
+ // subtitle delay setting
+ if (SupportsSubtitleFeature(IPC_SUBS_OFFSET))
+ {
+ std::shared_ptr<CSettingNumber> settingSubtitleDelay = AddSlider(groupSubtitles, SETTING_SUBTITLE_DELAY, 22006, SettingLevel::Basic, videoSettings.m_SubtitleDelay, 0, -CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoSubsDelayRange, 0.1f, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoSubsDelayRange, 22006, usePopup);
+ std::static_pointer_cast<CSettingControlSlider>(settingSubtitleDelay->GetControl())->SetFormatter(SettingFormatterDelay);
+ }
+
+ // subtitle stream setting
+ if (SupportsSubtitleFeature(IPC_SUBS_SELECT))
+ AddSubtitleStreams(groupSubtitles, SETTING_SUBTITLE_STREAM);
+
+ // subtitle browser setting
+ if (SupportsSubtitleFeature(IPC_SUBS_EXTERNAL))
+ AddButton(groupSubtitles, SETTING_SUBTITLE_BROWSER, 13250, SettingLevel::Basic);
+
+ AddButton(groupSubtitles, SETTING_SUBTITLE_SEARCH, 24134, SettingLevel::Basic);
+
+ // subtitle stream setting
+ AddButton(groupSaveAsDefault, SETTING_MAKE_DEFAULT, 12376, SettingLevel::Basic);
+}
+
+bool CGUIDialogSubtitleSettings::SupportsSubtitleFeature(int feature)
+{
+ for (auto item : m_subtitleCapabilities)
+ {
+ if (item == feature || item == IPC_SUBS_ALL)
+ return true;
+ }
+ return false;
+}
+
+void CGUIDialogSubtitleSettings::AddSubtitleStreams(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& settingId)
+{
+ if (group == NULL || settingId.empty())
+ return;
+
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ m_subtitleStream = appPlayer->GetSubtitle();
+ if (m_subtitleStream < 0)
+ m_subtitleStream = 0;
+
+ m_subtitleStreamSetting = AddList(group, settingId, 462, SettingLevel::Basic, m_subtitleStream, SubtitleStreamsOptionFiller, 462);
+}
+
+void CGUIDialogSubtitleSettings::SubtitleStreamsOptionFiller(
+ const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ int subtitleStreamCount = appPlayer->GetSubtitleCount();
+
+ // cycle through each subtitle and add it to our entry list
+ for (int i = 0; i < subtitleStreamCount; ++i)
+ {
+ SubtitleStreamInfo info;
+ appPlayer->GetSubtitleStreamInfo(i, info);
+
+ std::string strItem;
+ std::string strLanguage;
+
+ if (!g_LangCodeExpander.Lookup(info.language, strLanguage))
+ strLanguage = g_localizeStrings.Get(13205); // Unknown
+
+ if (info.name.length() == 0)
+ strItem = strLanguage;
+ else
+ strItem = StringUtils::Format("{} - {}", strLanguage, info.name);
+
+ strItem += FormatFlags(info.flags);
+ strItem += StringUtils::Format(" ({}/{})", i + 1, subtitleStreamCount);
+
+ list.emplace_back(strItem, i);
+ }
+
+ // no subtitle streams - just add a "None" entry
+ if (list.empty())
+ {
+ list.emplace_back(g_localizeStrings.Get(231), -1);
+ current = -1;
+ }
+}
+
+std::string CGUIDialogSubtitleSettings::SettingFormatterDelay(
+ const std::shared_ptr<const CSettingControlSlider>& control,
+ const CVariant& value,
+ const CVariant& minimum,
+ const CVariant& step,
+ const CVariant& maximum)
+{
+ if (!value.isDouble())
+ return "";
+
+ float fValue = value.asFloat();
+ float fStep = step.asFloat();
+
+ if (fabs(fValue) < 0.5f * fStep)
+ return StringUtils::Format(g_localizeStrings.Get(22003), 0.0);
+ if (fValue < 0)
+ return StringUtils::Format(g_localizeStrings.Get(22004), fabs(fValue));
+
+ return StringUtils::Format(g_localizeStrings.Get(22005), fValue);
+}
+
+std::string CGUIDialogSubtitleSettings::FormatFlags(StreamFlags flags)
+{
+ std::vector<std::string> localizedFlags;
+ if (flags & StreamFlags::FLAG_DEFAULT)
+ localizedFlags.emplace_back(g_localizeStrings.Get(39105));
+ if (flags & StreamFlags::FLAG_FORCED)
+ localizedFlags.emplace_back(g_localizeStrings.Get(39106));
+ if (flags & StreamFlags::FLAG_HEARING_IMPAIRED)
+ localizedFlags.emplace_back(g_localizeStrings.Get(39107));
+ if (flags & StreamFlags::FLAG_VISUAL_IMPAIRED)
+ localizedFlags.emplace_back(g_localizeStrings.Get(39108));
+
+ std::string formated = StringUtils::Join(localizedFlags, ", ");
+
+ if (!formated.empty())
+ formated = StringUtils::Format(" [{}]", formated);
+
+ return formated;
+}
diff --git a/xbmc/video/dialogs/GUIDialogSubtitleSettings.h b/xbmc/video/dialogs/GUIDialogSubtitleSettings.h
new file mode 100644
index 0000000..65216ed
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogSubtitleSettings.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "cores/VideoPlayer/Interface/StreamInfo.h"
+#include "settings/dialogs/GUIDialogSettingsManualBase.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+class CVariant;
+struct IntegerSettingOption;
+
+class CGUIDialogSubtitleSettings : public CGUIDialogSettingsManualBase
+{
+public:
+ CGUIDialogSubtitleSettings();
+ ~CGUIDialogSubtitleSettings() override;
+ bool OnMessage(CGUIMessage& message) override;
+
+ // specialization of CGUIWindow
+ void FrameMove() override;
+
+ static std::string BrowseForSubtitle();
+
+protected:
+ // implementations of ISettingCallback
+ void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+ void OnSettingAction(const std::shared_ptr<const CSetting>& setting) override;
+
+ // specialization of CGUIDialogSettingsBase
+ bool AllowResettingSettings() const override { return false; }
+ bool Save() override;
+ void SetupView() override;
+
+ // specialization of CGUIDialogSettingsManualBase
+ void InitializeSettings() override;
+
+private:
+ bool SupportsSubtitleFeature(int feature);
+
+ void AddSubtitleStreams(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& settingId);
+
+ int m_subtitleStream;
+ bool m_subtitleVisible;
+ std::shared_ptr<CSettingInt> m_subtitleStreamSetting;
+
+ std::vector<int> m_subtitleCapabilities;
+ static std::string FormatFlags(StreamFlags flags);
+
+ static void SubtitleStreamsOptionFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data);
+
+ static std::string SettingFormatterDelay(
+ const std::shared_ptr<const CSettingControlSlider>& control,
+ const CVariant& value,
+ const CVariant& minimum,
+ const CVariant& step,
+ const CVariant& maximum);
+};
diff --git a/xbmc/video/dialogs/GUIDialogSubtitles.cpp b/xbmc/video/dialogs/GUIDialogSubtitles.cpp
new file mode 100644
index 0000000..600c6da
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogSubtitles.cpp
@@ -0,0 +1,703 @@
+/*
+ * 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 "GUIDialogSubtitles.h"
+
+#include "FileItem.h"
+#include "LangInfo.h"
+#include "ServiceBroker.h"
+#include "URL.h"
+#include "Util.h"
+#include "addons/AddonManager.h"
+#include "addons/addoninfo/AddonInfo.h"
+#include "addons/addoninfo/AddonType.h"
+#include "addons/gui/GUIDialogAddonSettings.h"
+#include "application/Application.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPlayer.h"
+#include "cores/IPlayer.h"
+#include "dialogs/GUIDialogContextMenu.h"
+#include "dialogs/GUIDialogKaiToast.h"
+#include "filesystem/AddonsDirectory.h"
+#include "filesystem/Directory.h"
+#include "filesystem/File.h"
+#include "filesystem/SpecialProtocol.h"
+#include "filesystem/StackDirectory.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIKeyboardFactory.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "input/actions/ActionIDs.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "utils/JobManager.h"
+#include "utils/LangCodeExpander.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+#include "video/VideoDatabase.h"
+
+#include <mutex>
+
+using namespace ADDON;
+using namespace XFILE;
+
+namespace
+{
+constexpr int CONTROL_NAMELABEL = 100;
+constexpr int CONTROL_NAMELOGO = 110;
+constexpr int CONTROL_SUBLIST = 120;
+constexpr int CONTROL_SUBSEXIST = 130;
+constexpr int CONTROL_SUBSTATUS = 140;
+constexpr int CONTROL_SERVICELIST = 150;
+constexpr int CONTROL_MANUALSEARCH = 160;
+
+enum class SUBTITLE_SERVICE_CONTEXT_BUTTONS
+{
+ ADDON_SETTINGS,
+ ADDON_DISABLE
+};
+} // namespace
+
+/*! \brief simple job to retrieve a directory and store a string (language)
+ */
+class CSubtitlesJob: public CJob
+{
+public:
+ CSubtitlesJob(const CURL &url, const std::string &language) : m_url(url), m_language(language)
+ {
+ m_items = new CFileItemList;
+ }
+ ~CSubtitlesJob() override
+ {
+ delete m_items;
+ }
+ bool DoWork() override
+ {
+ CDirectory::GetDirectory(m_url.Get(), *m_items, "", DIR_FLAG_DEFAULTS);
+ return true;
+ }
+ bool operator==(const CJob *job) const override
+ {
+ if (strcmp(job->GetType(),GetType()) == 0)
+ {
+ const CSubtitlesJob* rjob = dynamic_cast<const CSubtitlesJob*>(job);
+ if (rjob)
+ {
+ return m_url.Get() == rjob->m_url.Get() &&
+ m_language == rjob->m_language;
+ }
+ }
+ return false;
+ }
+ const CFileItemList *GetItems() const { return m_items; }
+ const CURL &GetURL() const { return m_url; }
+ const std::string &GetLanguage() const { return m_language; }
+private:
+ CURL m_url;
+ CFileItemList *m_items;
+ std::string m_language;
+};
+
+CGUIDialogSubtitles::CGUIDialogSubtitles(void)
+ : CGUIDialog(WINDOW_DIALOG_SUBTITLES, "DialogSubtitles.xml")
+ , m_subtitles(new CFileItemList)
+ , m_serviceItems(new CFileItemList)
+{
+ m_loadType = KEEP_IN_MEMORY;
+}
+
+CGUIDialogSubtitles::~CGUIDialogSubtitles(void)
+{
+ CancelJobs();
+ delete m_subtitles;
+ delete m_serviceItems;
+}
+
+bool CGUIDialogSubtitles::OnMessage(CGUIMessage& message)
+{
+ if (message.GetMessage() == GUI_MSG_CLICKED)
+ {
+ int iControl = message.GetSenderId();
+ bool selectAction = (message.GetParam1() == ACTION_SELECT_ITEM ||
+ message.GetParam1() == ACTION_MOUSE_LEFT_CLICK);
+
+ bool contextMenuAction = (message.GetParam1() == ACTION_CONTEXT_MENU ||
+ message.GetParam1() == ACTION_MOUSE_RIGHT_CLICK);
+
+ if (selectAction && iControl == CONTROL_SUBLIST)
+ {
+ CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_SUBLIST);
+ OnMessage(msg);
+
+ int item = msg.GetParam1();
+ if (item >= 0 && item < m_subtitles->Size())
+ Download(*m_subtitles->Get(item));
+ return true;
+ }
+ else if (selectAction && iControl == CONTROL_SERVICELIST)
+ {
+ CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_SERVICELIST);
+ OnMessage(msg);
+
+ int item = msg.GetParam1();
+ if (item >= 0 && item < m_serviceItems->Size())
+ {
+ SetService(m_serviceItems->Get(item)->GetProperty("Addon.ID").asString());
+ Search();
+ }
+ return true;
+ }
+ else if (contextMenuAction && iControl == CONTROL_SERVICELIST)
+ {
+ CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_SERVICELIST);
+ OnMessage(msg);
+
+ const int itemIdx = msg.GetParam1();
+ if (itemIdx >= 0 && itemIdx < m_serviceItems->Size())
+ {
+ OnSubtitleServiceContextMenu(itemIdx);
+ }
+ }
+ else if (iControl == CONTROL_MANUALSEARCH)
+ {
+ //manual search
+ if (CGUIKeyboardFactory::ShowAndGetInput(m_strManualSearch, CVariant{g_localizeStrings.Get(24121)}, true))
+ {
+ Search(m_strManualSearch);
+ return true;
+ }
+ }
+ }
+ else if (message.GetMessage() == GUI_MSG_WINDOW_DEINIT)
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ // Resume the video if the user has requested it
+ if (appPlayer->IsPaused() && m_pausedOnRun)
+ appPlayer->Pause();
+
+ CGUIDialog::OnMessage(message);
+
+ ClearSubtitles();
+ ClearServices();
+ return true;
+ }
+ return CGUIDialog::OnMessage(message);
+}
+
+void CGUIDialogSubtitles::OnInitWindow()
+{
+ // Pause the video if the user has requested it
+ m_pausedOnRun = false;
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
+ CSettings::SETTING_SUBTITLES_PAUSEONSEARCH) &&
+ !appPlayer->IsPaused())
+ {
+ appPlayer->Pause();
+ m_pausedOnRun = true;
+ }
+
+ FillServices();
+ CGUIWindow::OnInitWindow();
+ Search();
+}
+
+void CGUIDialogSubtitles::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
+{
+ if (m_bInvalidated)
+ {
+ // take copies of our variables to ensure we don't hold the lock for long.
+ std::string status;
+ CFileItemList subs;
+ {
+ std::unique_lock<CCriticalSection> lock(m_critsection);
+ status = m_status;
+ subs.Assign(*m_subtitles);
+ }
+ SET_CONTROL_LABEL(CONTROL_SUBSTATUS, status);
+
+ if (m_updateSubsList)
+ {
+ CGUIMessage message(GUI_MSG_LABEL_BIND, GetID(), CONTROL_SUBLIST, 0, 0, &subs);
+ OnMessage(message);
+ if (!subs.IsEmpty())
+ {
+ // focus subtitles list
+ CGUIMessage msg(GUI_MSG_SETFOCUS, GetID(), CONTROL_SUBLIST);
+ OnMessage(msg);
+ }
+ m_updateSubsList = false;
+ }
+
+ int control = GetFocusedControlID();
+ // nothing has focus
+ if (!control)
+ {
+ CGUIMessage msg(GUI_MSG_SETFOCUS, GetID(), m_subtitles->IsEmpty() ?
+ CONTROL_SERVICELIST : CONTROL_SUBLIST);
+ OnMessage(msg);
+ }
+ // subs list is focused but we have no subs
+ else if (control == CONTROL_SUBLIST && m_subtitles->IsEmpty())
+ {
+ CGUIMessage msg(GUI_MSG_SETFOCUS, GetID(), CONTROL_SERVICELIST);
+ OnMessage(msg);
+ }
+ }
+ CGUIDialog::Process(currentTime, dirtyregions);
+}
+
+void CGUIDialogSubtitles::FillServices()
+{
+ ClearServices();
+
+ VECADDONS addons;
+ CServiceBroker::GetAddonMgr().GetAddons(addons, AddonType::SUBTITLE_MODULE);
+
+ if (addons.empty())
+ {
+ UpdateStatus(NO_SERVICES);
+ return;
+ }
+
+ std::string defaultService;
+ const CFileItem &item = g_application.CurrentUnstackedItem();
+ if (item.GetVideoContentType() == VideoDbContentType::TVSHOWS ||
+ item.GetVideoContentType() == VideoDbContentType::EPISODES)
+ // Set default service for tv shows
+ defaultService = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SUBTITLES_TV);
+ else
+ // Set default service for filemode and movies
+ defaultService = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SUBTITLES_MOVIE);
+
+ std::string service = addons.front()->ID();
+ for (VECADDONS::const_iterator addonIt = addons.begin(); addonIt != addons.end(); ++addonIt)
+ {
+ CFileItemPtr item(CAddonsDirectory::FileItemFromAddon(*addonIt, "plugin://" + (*addonIt)->ID(), false));
+ m_serviceItems->Add(item);
+ if ((*addonIt)->ID() == defaultService)
+ service = (*addonIt)->ID();
+ }
+
+ // Bind our services to the UI
+ CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), CONTROL_SERVICELIST, 0, 0, m_serviceItems);
+ OnMessage(msg);
+
+ SetService(service);
+}
+
+bool CGUIDialogSubtitles::SetService(const std::string &service)
+{
+ if (service != m_currentService)
+ {
+ m_currentService = service;
+ CLog::Log(LOGDEBUG, "New Service [{}] ", m_currentService);
+
+ CFileItemPtr currentService = GetService();
+ // highlight this item in the skin
+ for (int i = 0; i < m_serviceItems->Size(); i++)
+ {
+ CFileItemPtr pItem = m_serviceItems->Get(i);
+ pItem->Select(pItem == currentService);
+ }
+
+ SET_CONTROL_LABEL(CONTROL_NAMELABEL, currentService->GetLabel());
+
+ if (currentService->HasAddonInfo())
+ {
+ std::string icon = URIUtils::AddFileToFolder(currentService->GetAddonInfo()->Path(), "logo.png");
+ SET_CONTROL_FILENAME(CONTROL_NAMELOGO, icon);
+ }
+
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (appPlayer->GetSubtitleCount() == 0)
+ SET_CONTROL_HIDDEN(CONTROL_SUBSEXIST);
+ else
+ SET_CONTROL_VISIBLE(CONTROL_SUBSEXIST);
+
+ return true;
+ }
+ return false;
+}
+
+const CFileItemPtr CGUIDialogSubtitles::GetService() const
+{
+ for (int i = 0; i < m_serviceItems->Size(); i++)
+ {
+ if (m_serviceItems->Get(i)->GetProperty("Addon.ID") == m_currentService)
+ return m_serviceItems->Get(i);
+ }
+ return CFileItemPtr();
+}
+
+void CGUIDialogSubtitles::Search(const std::string &search/*=""*/)
+{
+ if (m_currentService.empty())
+ return; // no services available
+
+ UpdateStatus(SEARCHING);
+ ClearSubtitles();
+
+ CURL url("plugin://" + m_currentService + "/");
+ if (!search.empty())
+ {
+ url.SetOption("action", "manualsearch");
+ url.SetOption("searchstring", search);
+ }
+ else
+ url.SetOption("action", "search");
+
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ SettingConstPtr setting = settings->GetSetting(CSettings::SETTING_SUBTITLES_LANGUAGES);
+ if (setting)
+ url.SetOption("languages", setting->ToString());
+
+ // Check for stacking
+ if (g_application.CurrentFileItem().IsStack())
+ url.SetOption("stack", "1");
+
+ std::string preferredLanguage = settings->GetString(CSettings::SETTING_LOCALE_SUBTITLELANGUAGE);
+
+ if (StringUtils::EqualsNoCase(preferredLanguage, "original"))
+ {
+ AudioStreamInfo info;
+ std::string strLanguage;
+
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ appPlayer->GetAudioStreamInfo(CURRENT_STREAM, info);
+
+ if (!g_LangCodeExpander.Lookup(info.language, strLanguage))
+ strLanguage = "Unknown";
+
+ preferredLanguage = strLanguage;
+ }
+ else if (StringUtils::EqualsNoCase(preferredLanguage, "default"))
+ preferredLanguage = g_langInfo.GetEnglishLanguageName();
+
+ url.SetOption("preferredlanguage", preferredLanguage);
+
+ AddJob(new CSubtitlesJob(url, ""));
+}
+
+void CGUIDialogSubtitles::OnJobComplete(unsigned int jobID, bool success, CJob *job)
+{
+ const CURL &url = static_cast<CSubtitlesJob*>(job)->GetURL();
+ const CFileItemList *items = static_cast<CSubtitlesJob*>(job)->GetItems();
+ const std::string &language = static_cast<CSubtitlesJob*>(job)->GetLanguage();
+ if (url.GetOption("action") == "search" || url.GetOption("action") == "manualsearch")
+ OnSearchComplete(items);
+ else
+ OnDownloadComplete(items, language);
+ CJobQueue::OnJobComplete(jobID, success, job);
+}
+
+void CGUIDialogSubtitles::OnSearchComplete(const CFileItemList *items)
+{
+ std::unique_lock<CCriticalSection> lock(m_critsection);
+ m_subtitles->Assign(*items);
+ UpdateStatus(SEARCH_COMPLETE);
+ m_updateSubsList = true;
+ MarkDirtyRegion();
+
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (!items->IsEmpty() && appPlayer->GetSubtitleCount() == 0 &&
+ m_LastAutoDownloaded != g_application.CurrentFile() &&
+ CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
+ CSettings::SETTING_SUBTITLES_DOWNLOADFIRST))
+ {
+ CFileItemPtr item = items->Get(0);
+ CLog::Log(LOGDEBUG, "{} - Automatically download first subtitle: {}", __FUNCTION__,
+ item->GetLabel2());
+ m_LastAutoDownloaded = g_application.CurrentFile();
+ Download(*item);
+ }
+
+ SetInvalid();
+}
+
+void CGUIDialogSubtitles::OnSubtitleServiceContextMenu(int itemIdx)
+{
+ const auto service = m_serviceItems->Get(itemIdx);
+
+ CContextButtons buttons;
+ // Subtitle addon settings
+ buttons.Add(static_cast<int>(SUBTITLE_SERVICE_CONTEXT_BUTTONS::ADDON_SETTINGS),
+ g_localizeStrings.Get(21417));
+ // Disable addon
+ buttons.Add(static_cast<int>(SUBTITLE_SERVICE_CONTEXT_BUTTONS::ADDON_DISABLE),
+ g_localizeStrings.Get(24021));
+
+ auto idx = static_cast<SUBTITLE_SERVICE_CONTEXT_BUTTONS>(CGUIDialogContextMenu::Show(buttons));
+ switch (idx)
+ {
+ case SUBTITLE_SERVICE_CONTEXT_BUTTONS::ADDON_SETTINGS:
+ {
+ AddonPtr addon;
+ if (CServiceBroker::GetAddonMgr().GetAddon(service->GetProperty("Addon.ID").asString(), addon,
+ AddonType::SUBTITLE_MODULE,
+ OnlyEnabled::CHOICE_YES))
+ {
+ CGUIDialogAddonSettings::ShowForAddon(addon);
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "{} - Could not open settings for addon: {}", __FUNCTION__,
+ service->GetProperty("Addon.ID").asString());
+ }
+ break;
+ }
+ case SUBTITLE_SERVICE_CONTEXT_BUTTONS::ADDON_DISABLE:
+ {
+ CServiceBroker::GetAddonMgr().DisableAddon(service->GetProperty("Addon.ID").asString(),
+ AddonDisabledReason::USER);
+ const bool currentActiveServiceWasDisabled =
+ m_currentService == service->GetProperty("Addon.ID").asString();
+ FillServices();
+ // restart search if the current active service was disabled
+ if (currentActiveServiceWasDisabled && !m_serviceItems->IsEmpty())
+ {
+ Search();
+ }
+ // if no more services are available make sure the subtitle list is cleaned up
+ else if (m_serviceItems->IsEmpty())
+ {
+ ClearSubtitles();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void CGUIDialogSubtitles::UpdateStatus(STATUS status)
+{
+ std::unique_lock<CCriticalSection> lock(m_critsection);
+ std::string label;
+ switch (status)
+ {
+ case NO_SERVICES:
+ label = g_localizeStrings.Get(24114);
+ break;
+ case SEARCHING:
+ label = g_localizeStrings.Get(24107);
+ break;
+ case SEARCH_COMPLETE:
+ if (!m_subtitles->IsEmpty())
+ label = StringUtils::Format(g_localizeStrings.Get(24108), m_subtitles->Size());
+ else
+ label = g_localizeStrings.Get(24109);
+ break;
+ case DOWNLOADING:
+ label = g_localizeStrings.Get(24110);
+ break;
+ default:
+ break;
+ }
+ if (label != m_status)
+ {
+ m_status = label;
+ SetInvalid();
+ }
+}
+
+void CGUIDialogSubtitles::Download(const CFileItem &subtitle)
+{
+ UpdateStatus(DOWNLOADING);
+
+ // subtitle URL should be of the form plugin://<addonid>/?param=foo&param=bar
+ // we just append (if not already present) the action=download parameter.
+ CURL url(subtitle.GetURL());
+ if (url.GetOption("action").empty())
+ url.SetOption("action", "download");
+
+ AddJob(new CSubtitlesJob(url, subtitle.GetLabel()));
+}
+
+void CGUIDialogSubtitles::OnDownloadComplete(const CFileItemList *items, const std::string &language)
+{
+ if (items->IsEmpty())
+ {
+ CFileItemPtr service = GetService();
+ if (service)
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, service->GetLabel(), g_localizeStrings.Get(24113));
+ UpdateStatus(SEARCH_COMPLETE);
+ return;
+ }
+
+ SUBTITLE_STORAGEMODE storageMode = (SUBTITLE_STORAGEMODE) CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_SUBTITLES_STORAGEMODE);
+
+ // Get (unstacked) path
+ std::string strCurrentFile = g_application.CurrentUnstackedItem().GetDynPath();
+
+ std::string strDownloadPath = "special://temp";
+ std::string strDestPath;
+ std::vector<std::string> vecFiles;
+
+ std::string strCurrentFilePath;
+ if (StringUtils::StartsWith(strCurrentFilePath, "http://"))
+ {
+ strCurrentFile = "TempSubtitle";
+ vecFiles.push_back(strCurrentFile);
+ }
+ else
+ {
+ std::string subPath = CSpecialProtocol::TranslatePath("special://subtitles");
+ if (!subPath.empty())
+ strDownloadPath = subPath;
+
+ /** Get item's folder for sub storage, special case for RAR/ZIP items
+ * @todo We need some way to avoid special casing this all over the place
+ * for rar/zip (perhaps modify GetDirectory?)
+ */
+ if (URIUtils::IsInRAR(strCurrentFile) || URIUtils::IsInZIP(strCurrentFile))
+ strCurrentFilePath = URIUtils::GetDirectory(CURL(strCurrentFile).GetHostName());
+ else
+ strCurrentFilePath = URIUtils::GetDirectory(strCurrentFile);
+
+ // Handle stacks
+ if (g_application.CurrentFileItem().IsStack() && items->Size() > 1)
+ {
+ CStackDirectory::GetPaths(g_application.CurrentFileItem().GetPath(), vecFiles);
+ // Make sure (stack) size is the same as the size of the items handed to us, else fallback to single item
+ if (items->Size() != (int) vecFiles.size())
+ {
+ vecFiles.clear();
+ vecFiles.push_back(strCurrentFile);
+ }
+ }
+ else
+ {
+ vecFiles.push_back(strCurrentFile);
+ }
+
+ if (storageMode == SUBTITLE_STORAGEMODE_MOVIEPATH &&
+ CUtil::SupportsWriteFileOperations(strCurrentFilePath))
+ {
+ strDestPath = strCurrentFilePath;
+ }
+ }
+
+ // Use fallback?
+ if (strDestPath.empty())
+ strDestPath = strDownloadPath;
+
+ // Extract the language and appropriate extension
+ std::string strSubLang;
+ g_LangCodeExpander.ConvertToISO6391(language, strSubLang);
+
+ // Iterate over all items to transfer
+ for (unsigned int i = 0; i < vecFiles.size() && i < (unsigned int) items->Size(); i++)
+ {
+ std::string strUrl = items->Get(i)->GetPath();
+ std::string strFileName = URIUtils::GetFileName(vecFiles[i]);
+ URIUtils::RemoveExtension(strFileName);
+
+ // construct subtitle path
+ std::string strSubExt = URIUtils::GetExtension(strUrl);
+ std::string strSubName = StringUtils::Format("{}.{}{}", strFileName, strSubLang, strSubExt);
+
+ // Handle URL encoding:
+ std::string strDownloadFile = URIUtils::ChangeBasePath(strCurrentFilePath, strSubName, strDownloadPath);
+ std::string strDestFile = strDownloadFile;
+
+ if (!CFile::Copy(strUrl, strDownloadFile))
+ {
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, strSubName, g_localizeStrings.Get(24113));
+ CLog::Log(LOGERROR, "{} - Saving of subtitle {} to {} failed", __FUNCTION__, strUrl,
+ strDownloadFile);
+ }
+ else
+ {
+ if (strDestPath != strDownloadPath)
+ {
+ // Handle URL encoding:
+ std::string strTryDestFile = URIUtils::ChangeBasePath(strCurrentFilePath, strSubName, strDestPath);
+
+ /* Copy the file from temp to our final destination, if that fails fallback to download path
+ * (ie. special://subtitles or use special://temp). Note that after the first item strDownloadPath equals strDestpath
+ * so that all remaining items (including the .idx below) are copied directly to their final destination and thus all
+ * items end up in the same folder
+ */
+ CLog::Log(LOGDEBUG, "{} - Saving subtitle {} to {}", __FUNCTION__, strDownloadFile,
+ strTryDestFile);
+ if (CFile::Copy(strDownloadFile, strTryDestFile))
+ {
+ CFile::Delete(strDownloadFile);
+ strDestFile = strTryDestFile;
+ strDownloadPath = strDestPath; // Update download path so all the other items get directly downloaded to our final destination
+ }
+ else
+ {
+ CLog::Log(LOGWARNING, "{} - Saving of subtitle {} to {} failed. Falling back to {}",
+ __FUNCTION__, strDownloadFile, strTryDestFile, strDownloadPath);
+ strDestPath = strDownloadPath; // Copy failed, use fallback for the rest of the items
+ }
+ }
+ else
+ {
+ CLog::Log(LOGDEBUG, "{} - Saved subtitle {} to {}", __FUNCTION__, strUrl, strDownloadFile);
+ }
+
+ // for ".sub" subtitles we check if ".idx" counterpart exists and copy that as well
+ if (StringUtils::EqualsNoCase(strSubExt, ".sub"))
+ {
+ strUrl = URIUtils::ReplaceExtension(strUrl, ".idx");
+ if(CFile::Exists(strUrl))
+ {
+ std::string strSubNameIdx = StringUtils::Format("{}.{}.idx", strFileName, strSubLang);
+ // Handle URL encoding:
+ strDestFile = URIUtils::ChangeBasePath(strCurrentFilePath, strSubNameIdx, strDestPath);
+ CFile::Copy(strUrl, strDestFile);
+ }
+ }
+
+ // Set sub for currently playing (stack) item
+ if (vecFiles[i] == strCurrentFile)
+ SetSubtitles(strDestFile);
+ }
+ }
+
+ // Notify window manager that a subtitle was downloaded
+ CGUIMessage msg(GUI_MSG_SUBTITLE_DOWNLOADED, 0, 0);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
+
+ // Close the window
+ Close();
+}
+
+void CGUIDialogSubtitles::ClearSubtitles()
+{
+ CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_SUBLIST);
+ OnMessage(msg);
+ std::unique_lock<CCriticalSection> lock(m_critsection);
+ m_subtitles->Clear();
+}
+
+void CGUIDialogSubtitles::ClearServices()
+{
+ CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_SERVICELIST);
+ OnMessage(msg);
+ m_serviceItems->Clear();
+ m_currentService.clear();
+}
+
+void CGUIDialogSubtitles::SetSubtitles(const std::string &subtitle)
+{
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ appPlayer->AddSubtitle(subtitle);
+}
diff --git a/xbmc/video/dialogs/GUIDialogSubtitles.h b/xbmc/video/dialogs/GUIDialogSubtitles.h
new file mode 100644
index 0000000..043a613
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogSubtitles.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "guilib/GUIDialog.h"
+#include "threads/CriticalSection.h"
+#include "utils/JobManager.h"
+
+#include <string>
+
+enum SUBTITLE_STORAGEMODE
+{
+ SUBTITLE_STORAGEMODE_MOVIEPATH = 0,
+ SUBTITLE_STORAGEMODE_CUSTOMPATH
+};
+
+class CFileItem;
+class CFileItemList;
+
+class CGUIDialogSubtitles : public CGUIDialog, CJobQueue
+{
+public:
+ CGUIDialogSubtitles(void);
+ ~CGUIDialogSubtitles(void) override;
+ bool OnMessage(CGUIMessage& message) override;
+ void OnInitWindow() override;
+
+protected:
+ void Process(unsigned int currentTime, CDirtyRegionList &dirtyregions) override;
+ void OnJobComplete(unsigned int jobID, bool success, CJob *job) override;
+
+ bool SetService(const std::string &service);
+ const CFileItemPtr GetService() const;
+ void FillServices();
+ void ClearServices();
+ void ClearSubtitles();
+
+ enum STATUS { NO_SERVICES = 0, SEARCHING, SEARCH_COMPLETE, DOWNLOADING };
+ void UpdateStatus(STATUS status);
+
+ void Search(const std::string &search="");
+ void OnSearchComplete(const CFileItemList *items);
+
+ void Download(const CFileItem &subtitle);
+ void OnDownloadComplete(const CFileItemList *items, const std::string &language);
+
+
+ /*!
+ \brief Called when the context menu is requested on a subtitle service
+ present on the list of installed subtitle addons
+ \param itemIdx the index of the selected subtitle service on the list
+ */
+ void OnSubtitleServiceContextMenu(int itemIdx);
+
+ void SetSubtitles(const std::string &subtitle);
+
+ CCriticalSection m_critsection;
+ CFileItemList* m_subtitles;
+ CFileItemList* m_serviceItems;
+ std::string m_currentService;
+ std::string m_status;
+ std::string m_strManualSearch;
+ bool m_pausedOnRun = false;
+ bool m_updateSubsList = false; ///< true if we need to update our subs list
+ std::string m_LastAutoDownloaded; ///< Last video file path which automatically downloaded subtitle
+};
diff --git a/xbmc/video/dialogs/GUIDialogTeletext.cpp b/xbmc/video/dialogs/GUIDialogTeletext.cpp
new file mode 100644
index 0000000..01e86cb
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogTeletext.cpp
@@ -0,0 +1,197 @@
+/*
+ * 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 "GUIDialogTeletext.h"
+
+#include "ServiceBroker.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPlayer.h"
+#include "dialogs/GUIDialogKaiToast.h"
+#include "guilib/GUIMessage.h"
+#include "guilib/GUITexture.h"
+#include "guilib/LocalizeStrings.h"
+#include "guilib/Texture.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/ColorUtils.h"
+#include "utils/log.h"
+
+static int teletextFadeAmount = 0;
+
+CGUIDialogTeletext::CGUIDialogTeletext()
+ : CGUIDialog(WINDOW_DIALOG_OSD_TELETEXT, ""), m_pTxtTexture(nullptr)
+{
+ m_renderOrder = RENDER_ORDER_DIALOG_TELETEXT;
+}
+
+CGUIDialogTeletext::~CGUIDialogTeletext() = default;
+
+bool CGUIDialogTeletext::OnAction(const CAction& action)
+{
+ if (m_TextDecoder.HandleAction(action))
+ {
+ MarkDirtyRegion();
+ return true;
+ }
+
+ return CGUIDialog::OnAction(action);
+}
+
+bool CGUIDialogTeletext::OnBack(int actionID)
+{
+ m_bClose = true;
+ MarkDirtyRegion();
+ return true;
+}
+
+bool CGUIDialogTeletext::OnMessage(CGUIMessage& message)
+{
+ if (message.GetMessage() == GUI_MSG_WINDOW_INIT)
+ {
+ /* Do not open if no teletext is available */
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (!appPlayer->HasTeletextCache())
+ {
+ Close();
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(23049), "", 1500, false);
+ return true;
+ }
+ }
+ else if (message.GetMessage() == GUI_MSG_NOTIFY_ALL)
+ {
+ if (message.GetParam1() == GUI_MSG_WINDOW_RESIZE)
+ {
+ SetCoordinates();
+ }
+ }
+ return CGUIDialog::OnMessage(message);
+}
+
+void CGUIDialogTeletext::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
+{
+ CGUIDialog::Process(currentTime, dirtyregions);
+ m_renderRegion = m_vertCoords;
+}
+
+void CGUIDialogTeletext::Render()
+{
+ // Do not render if we have no texture
+ if (!m_pTxtTexture)
+ {
+ CLog::Log(LOGERROR, "CGUITeletextBox::Render called without texture");
+ return;
+ }
+
+ m_TextDecoder.RenderPage();
+
+ if (!m_bClose)
+ {
+ if (teletextFadeAmount < 100)
+ {
+ teletextFadeAmount = std::min(100, teletextFadeAmount + 5);
+ MarkDirtyRegion();
+ }
+ }
+ else
+ {
+ if (teletextFadeAmount > 0)
+ {
+ teletextFadeAmount = std::max(0, teletextFadeAmount - 10);
+ MarkDirtyRegion();
+ }
+
+ if (teletextFadeAmount == 0)
+ Close();
+ }
+
+ unsigned char* textureBuffer = (unsigned char*)m_TextDecoder.GetTextureBuffer();
+ if (!m_bClose && m_TextDecoder.NeedRendering() && textureBuffer)
+ {
+ m_pTxtTexture->Update(m_TextDecoder.GetWidth(), m_TextDecoder.GetHeight(), m_TextDecoder.GetWidth()*4, XB_FMT_A8R8G8B8, textureBuffer, false);
+ m_TextDecoder.RenderingDone();
+ MarkDirtyRegion();
+ }
+
+ UTILS::COLOR::Color color =
+ (static_cast<UTILS::COLOR::Color>(teletextFadeAmount * 2.55f) & 0xff) << 24 | 0xFFFFFF;
+ CGUITexture::DrawQuad(m_vertCoords, color, m_pTxtTexture.get());
+
+ CGUIDialog::Render();
+}
+
+void CGUIDialogTeletext::OnInitWindow()
+{
+ teletextFadeAmount = 0;
+ m_bClose = false;
+ m_windowLoaded = true;
+
+ SetCoordinates();
+
+ if (!m_TextDecoder.InitDecoder())
+ {
+ CLog::Log(LOGERROR, "{}: failed to init teletext decoder", __FUNCTION__);
+ Close();
+ }
+
+ m_pTxtTexture =
+ CTexture::CreateTexture(m_TextDecoder.GetWidth(), m_TextDecoder.GetHeight(), XB_FMT_A8R8G8B8);
+ if (!m_pTxtTexture)
+ {
+ CLog::Log(LOGERROR, "{}: failed to create texture", __FUNCTION__);
+ Close();
+ }
+
+ CGUIDialog::OnInitWindow();
+}
+
+void CGUIDialogTeletext::OnDeinitWindow(int nextWindowID)
+{
+ m_windowLoaded = false;
+ m_TextDecoder.EndDecoder();
+
+ m_pTxtTexture.reset();
+
+ CGUIDialog::OnDeinitWindow(nextWindowID);
+}
+
+void CGUIDialogTeletext::SetCoordinates()
+{
+ float left, right, top, bottom;
+
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetScalingResolution(m_coordsRes, m_needsScaling);
+
+ left = CServiceBroker::GetWinSystem()->GetGfxContext().ScaleFinalXCoord(0, 0);
+ right = CServiceBroker::GetWinSystem()->GetGfxContext().ScaleFinalXCoord((float)m_coordsRes.iWidth, 0);
+ top = CServiceBroker::GetWinSystem()->GetGfxContext().ScaleFinalYCoord(0, 0);
+ bottom = CServiceBroker::GetWinSystem()->GetGfxContext().ScaleFinalYCoord(0, (float)m_coordsRes.iHeight);
+
+ if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOPLAYER_TELETEXTSCALE))
+ {
+ /* Fixed aspect ratio to 4:3 for teletext */
+ float width = right - left;
+ float height = bottom - top;
+ if (width / 4 > height / 3)
+ {
+ left = (width - height * 4 / 3) / 2;
+ right = width - left;
+ }
+ else
+ {
+ top = (height - width * 3 / 4) / 2;
+ bottom = height - top;
+ }
+ }
+
+ m_vertCoords.SetRect(left,
+ top,
+ right,
+ bottom);
+
+ MarkDirtyRegion();
+}
diff --git a/xbmc/video/dialogs/GUIDialogTeletext.h b/xbmc/video/dialogs/GUIDialogTeletext.h
new file mode 100644
index 0000000..4a60ec0
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogTeletext.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "guilib/GUIDialog.h"
+#include "video/Teletext.h"
+
+#include <memory>
+
+class CTexture;
+
+class CGUIDialogTeletext : public CGUIDialog
+{
+public:
+ CGUIDialogTeletext(void);
+ ~CGUIDialogTeletext(void) override;
+ bool OnMessage(CGUIMessage& message) override;
+ bool OnAction(const CAction& action) override;
+ bool OnBack(int actionID) override;
+ void Process(unsigned int currentTime, CDirtyRegionList &dirtyregions) override;
+ void Render() override;
+ void OnInitWindow() override;
+ void OnDeinitWindow(int nextWindowID) override;
+
+protected:
+ bool m_bClose; /* Close sendet, needed for fade out */
+ std::unique_ptr<CTexture> m_pTxtTexture; /* Texture info class to render to screen */
+ CRect m_vertCoords; /* Coordinates of teletext field on screen */
+ CTeletextDecoder m_TextDecoder; /* Decoding class for teletext code */
+
+private:
+ void SetCoordinates();
+};
diff --git a/xbmc/video/dialogs/GUIDialogVideoBookmarks.cpp b/xbmc/video/dialogs/GUIDialogVideoBookmarks.cpp
new file mode 100644
index 0000000..f2669d3
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogVideoBookmarks.cpp
@@ -0,0 +1,590 @@
+/*
+ * 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 "GUIDialogVideoBookmarks.h"
+
+#include "FileItem.h"
+#include "ServiceBroker.h"
+#include "TextureCache.h"
+#include "Util.h"
+#include "application/Application.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPlayer.h"
+#include "dialogs/GUIDialogContextMenu.h"
+#include "dialogs/GUIDialogKaiToast.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "input/actions/Action.h"
+#include "input/actions/ActionIDs.h"
+#include "messaging/ApplicationMessenger.h"
+#include "pictures/Picture.h"
+#include "profiles/ProfileManager.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/Crc32.h"
+#include "utils/FileUtils.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+#include "video/VideoDatabase.h"
+#include "video/VideoThumbLoader.h"
+#include "view/ViewState.h"
+
+#include <mutex>
+#include <string>
+#include <vector>
+
+#define BOOKMARK_THUMB_WIDTH CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_imageRes
+
+#define CONTROL_ADD_BOOKMARK 2
+#define CONTROL_CLEAR_BOOKMARKS 3
+#define CONTROL_ADD_EPISODE_BOOKMARK 4
+
+#define CONTROL_THUMBS 11
+
+CGUIDialogVideoBookmarks::CGUIDialogVideoBookmarks()
+ : CGUIDialog(WINDOW_DIALOG_VIDEO_BOOKMARKS, "VideoOSDBookmarks.xml"),
+ CJobQueue(false, 1, CJob::PRIORITY_NORMAL)
+{
+ m_vecItems = new CFileItemList;
+ m_loadType = LOAD_EVERY_TIME;
+ m_jobsStarted = 0;
+}
+
+CGUIDialogVideoBookmarks::~CGUIDialogVideoBookmarks()
+{
+ delete m_vecItems;
+}
+
+bool CGUIDialogVideoBookmarks::OnMessage(CGUIMessage& message)
+{
+ switch ( message.GetMessage() )
+ {
+ case GUI_MSG_WINDOW_DEINIT:
+ {
+ CUtil::DeleteVideoDatabaseDirectoryCache();
+ Clear();
+ }
+ break;
+
+ case GUI_MSG_WINDOW_INIT:
+ {
+ // don't init this dialog if we don't playback a file
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (!appPlayer->IsPlaying())
+ return false;
+
+ CGUIWindow::OnMessage(message);
+ Update();
+ return true;
+ }
+ break;
+
+ case GUI_MSG_CLICKED:
+ {
+ int iControl = message.GetSenderId();
+ if (iControl == CONTROL_ADD_BOOKMARK)
+ {
+ AddBookmark();
+ Update();
+ }
+ else if (iControl == CONTROL_CLEAR_BOOKMARKS)
+ {
+ ClearBookmarks();
+ }
+ else if (iControl == CONTROL_ADD_EPISODE_BOOKMARK)
+ {
+ AddEpisodeBookmark();
+ Update();
+ }
+ else if (m_viewControl.HasControl(iControl)) // list/thumb control
+ {
+ int iItem = m_viewControl.GetSelectedItem();
+ int iAction = message.GetParam1();
+ if (iAction == ACTION_DELETE_ITEM)
+ {
+ Delete(iItem);
+ }
+ else if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
+ {
+ GotoBookmark(iItem);
+ }
+ }
+ }
+ break;
+ case GUI_MSG_SETFOCUS:
+ {
+ if (m_viewControl.HasControl(message.GetControlId()) && m_viewControl.GetCurrentControl() != message.GetControlId())
+ {
+ m_viewControl.SetFocused();
+ return true;
+ }
+ }
+ break;
+ case GUI_MSG_REFRESH_LIST:
+ {
+ switch (message.GetParam1())
+ {
+ case 0:
+ OnRefreshList();
+ break;
+ case 1:
+ UpdateItem(message.GetParam2());
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+
+ return CGUIDialog::OnMessage(message);
+}
+
+bool CGUIDialogVideoBookmarks::OnAction(const CAction &action)
+{
+ switch(action.GetID())
+ {
+ case ACTION_CONTEXT_MENU:
+ case ACTION_MOUSE_RIGHT_CLICK:
+ {
+ OnPopupMenu(m_viewControl.GetSelectedItem());
+ return true;
+ }
+ }
+ return CGUIDialog::OnAction(action);
+}
+
+
+void CGUIDialogVideoBookmarks::OnPopupMenu(int item)
+{
+ if (item < 0 || item >= (int) m_bookmarks.size())
+ return;
+
+ // highlight the item
+ (*m_vecItems)[item]->Select(true);
+
+ CContextButtons choices;
+ choices.Add(1, (m_bookmarks[item].type == CBookmark::EPISODE ? 20405 : 20404)); // "Remove episode bookmark" or "Remove bookmark"
+
+ int button = CGUIDialogContextMenu::ShowAndGetChoice(choices);
+
+ // unhighlight the item
+ (*m_vecItems)[item]->Select(false);
+
+ if (button == 1)
+ Delete(item);
+}
+
+void CGUIDialogVideoBookmarks::Delete(int item)
+{
+ if ( item>=0 && (unsigned)item < m_bookmarks.size() )
+ {
+ CVideoDatabase videoDatabase;
+ videoDatabase.Open();
+ std::string path(g_application.CurrentFile());
+ if (g_application.CurrentFileItem().HasProperty("original_listitem_url") &&
+ !URIUtils::IsVideoDb(g_application.CurrentFileItem().GetProperty("original_listitem_url").asString()))
+ path = g_application.CurrentFileItem().GetProperty("original_listitem_url").asString();
+ videoDatabase.ClearBookMarkOfFile(path, m_bookmarks[item], m_bookmarks[item].type);
+ videoDatabase.Close();
+ CUtil::DeleteVideoDatabaseDirectoryCache();
+ }
+ Update();
+}
+
+void CGUIDialogVideoBookmarks::UpdateItem(unsigned int chapterIdx)
+{
+ std::unique_lock<CCriticalSection> lock(m_refreshSection);
+
+ int itemPos = 0;
+ for (const auto& item : *m_vecItems)
+ {
+ if (chapterIdx == item->GetProperty("chapter").asInteger())
+ break;
+ itemPos++;
+ }
+
+ if (itemPos < m_vecItems->Size())
+ {
+ std::string time = StringUtils::Format("chapter://{}/{}", m_filePath, chapterIdx);
+ std::string cachefile = CServiceBroker::GetTextureCache()->GetCachedPath(
+ CServiceBroker::GetTextureCache()->GetCacheFile(time) + ".jpg");
+ if (CFileUtils::Exists(cachefile))
+ {
+ (*m_vecItems)[itemPos]->SetArt("thumb", cachefile);
+ }
+ }
+}
+
+void CGUIDialogVideoBookmarks::OnRefreshList()
+{
+ m_bookmarks.clear();
+ std::vector<CFileItemPtr> items;
+
+ // open the d/b and retrieve the bookmarks for the current movie
+ m_filePath = g_application.CurrentFile();
+ if (g_application.CurrentFileItem().HasProperty("original_listitem_url") &&
+ !URIUtils::IsVideoDb(g_application.CurrentFileItem().GetProperty("original_listitem_url").asString()))
+ m_filePath = g_application.CurrentFileItem().GetProperty("original_listitem_url").asString();
+
+ CVideoDatabase videoDatabase;
+ videoDatabase.Open();
+ videoDatabase.GetBookMarksForFile(m_filePath, m_bookmarks);
+ videoDatabase.GetBookMarksForFile(m_filePath, m_bookmarks, CBookmark::EPISODE, true);
+ videoDatabase.Close();
+
+ std::unique_lock<CCriticalSection> lock(m_refreshSection);
+ m_vecItems->Clear();
+
+ // cycle through each stored bookmark and add it to our list control
+ for (unsigned int i = 0; i < m_bookmarks.size(); ++i)
+ {
+ std::string bookmarkTime;
+ if (m_bookmarks[i].type == CBookmark::EPISODE)
+ bookmarkTime = StringUtils::Format("{} {} {} {}", g_localizeStrings.Get(20373),
+ m_bookmarks[i].seasonNumber, g_localizeStrings.Get(20359),
+ m_bookmarks[i].episodeNumber);
+ else
+ bookmarkTime = StringUtils::SecondsToTimeString((long)m_bookmarks[i].timeInSeconds, TIME_FORMAT_HH_MM_SS);
+
+ CFileItemPtr item(new CFileItem(StringUtils::Format(g_localizeStrings.Get(299), i + 1)));
+ item->SetLabel2(bookmarkTime);
+ item->SetArt("thumb", m_bookmarks[i].thumbNailImage);
+ item->SetProperty("resumepoint", m_bookmarks[i].timeInSeconds);
+ item->SetProperty("playerstate", m_bookmarks[i].playerState);
+ item->SetProperty("isbookmark", "true");
+ items.push_back(item);
+ }
+
+ // add chapters if around
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ for (int i = 1; i <= appPlayer->GetChapterCount(); ++i)
+ {
+ std::string chapterName;
+ appPlayer->GetChapterName(chapterName, i);
+
+ int64_t pos = appPlayer->GetChapterPos(i);
+ std::string time = StringUtils::SecondsToTimeString((long) pos, TIME_FORMAT_HH_MM_SS);
+
+ if (chapterName.empty() ||
+ StringUtils::StartsWithNoCase(chapterName, time) ||
+ StringUtils::IsNaturalNumber(chapterName))
+ chapterName = StringUtils::Format(g_localizeStrings.Get(25010), i);
+
+ CFileItemPtr item(new CFileItem(chapterName));
+ item->SetLabel2(time);
+
+ std::string chapterPath = StringUtils::Format("chapter://{}/{}", m_filePath, i);
+ std::string cachefile = CServiceBroker::GetTextureCache()->GetCachedPath(
+ CServiceBroker::GetTextureCache()->GetCacheFile(chapterPath) + ".jpg");
+ if (CFileUtils::Exists(cachefile))
+ item->SetArt("thumb", cachefile);
+ else if (i > m_jobsStarted && CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MYVIDEOS_EXTRACTCHAPTERTHUMBS))
+ {
+ CFileItem item(m_filePath, false);
+ CJob* job = new CThumbExtractor(item, m_filePath, true, chapterPath, pos * 1000, false);
+ AddJob(job);
+ m_mapJobsChapter[job] = i;
+ m_jobsStarted++;
+ }
+
+ item->SetProperty("chapter", i);
+ item->SetProperty("resumepoint", static_cast<double>(pos));
+ item->SetProperty("ischapter", "true");
+ items.push_back(item);
+ }
+
+ // sort items by resume point
+ std::sort(items.begin(), items.end(), [](const CFileItemPtr &item1, const CFileItemPtr &item2) {
+ return item1->GetProperty("resumepoint").asDouble() < item2->GetProperty("resumepoint").asDouble();
+ });
+
+ // add items to file list and mark the proper item as selected if the current playtime is above
+ int selectedItemIndex = 0;
+ double playTime = g_application.GetTime();
+ for (auto& item : items)
+ {
+ m_vecItems->Add(item);
+ if (playTime >= item->GetProperty("resumepoint").asDouble())
+ selectedItemIndex = m_vecItems->Size() - 1;
+ }
+
+ m_viewControl.SetItems(*m_vecItems);
+ m_viewControl.SetSelectedItem(selectedItemIndex);
+}
+
+void CGUIDialogVideoBookmarks::Update()
+{
+ CVideoDatabase videoDatabase;
+ videoDatabase.Open();
+
+ if (g_application.CurrentFileItem().HasVideoInfoTag() && g_application.CurrentFileItem().GetVideoInfoTag()->m_iEpisode > -1)
+ {
+ std::vector<CVideoInfoTag> episodes;
+ videoDatabase.GetEpisodesByFile(g_application.CurrentFile(),episodes);
+ if (episodes.size() > 1)
+ {
+ CONTROL_ENABLE(CONTROL_ADD_EPISODE_BOOKMARK);
+ }
+ else
+ {
+ CONTROL_DISABLE(CONTROL_ADD_EPISODE_BOOKMARK);
+ }
+ }
+ else
+ {
+ CONTROL_DISABLE(CONTROL_ADD_EPISODE_BOOKMARK);
+ }
+
+
+ m_viewControl.SetCurrentView(DEFAULT_VIEW_ICONS);
+
+ // empty the list ready for population
+ Clear();
+
+ OnRefreshList();
+
+ videoDatabase.Close();
+}
+
+void CGUIDialogVideoBookmarks::Clear()
+{
+ m_viewControl.Clear();
+ m_vecItems->Clear();
+}
+
+void CGUIDialogVideoBookmarks::GotoBookmark(int item)
+{
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (item < 0 || item >= m_vecItems->Size() || !appPlayer->HasPlayer())
+ return;
+
+ CFileItemPtr fileItem = m_vecItems->Get(item);
+ int chapter = static_cast<int>(fileItem->GetProperty("chapter").asInteger());
+ if (chapter <= 0)
+ {
+ appPlayer->SetPlayerState(fileItem->GetProperty("playerstate").asString());
+ g_application.SeekTime(fileItem->GetProperty("resumepoint").asDouble());
+ }
+ else
+ appPlayer->SeekChapter(chapter);
+
+ Close();
+}
+
+void CGUIDialogVideoBookmarks::ClearBookmarks()
+{
+ CVideoDatabase videoDatabase;
+ videoDatabase.Open();
+ std::string path = g_application.CurrentFile();
+ if (g_application.CurrentFileItem().HasProperty("original_listitem_url") &&
+ !URIUtils::IsVideoDb(g_application.CurrentFileItem().GetProperty("original_listitem_url").asString()))
+ path = g_application.CurrentFileItem().GetProperty("original_listitem_url").asString();
+ videoDatabase.ClearBookMarksOfFile(path, CBookmark::STANDARD);
+ videoDatabase.ClearBookMarksOfFile(path, CBookmark::RESUME);
+ videoDatabase.ClearBookMarksOfFile(path, CBookmark::EPISODE);
+ videoDatabase.Close();
+ Update();
+}
+
+bool CGUIDialogVideoBookmarks::AddBookmark(CVideoInfoTag* tag)
+{
+ CVideoDatabase videoDatabase;
+ CBookmark bookmark;
+ bookmark.timeInSeconds = (int)g_application.GetTime();
+ bookmark.totalTimeInSeconds = (int)g_application.GetTotalTime();
+
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ if (appPlayer->HasPlayer())
+ bookmark.playerState = appPlayer->GetPlayerState();
+ else
+ bookmark.playerState.clear();
+
+ bookmark.player = g_application.GetCurrentPlayer();
+
+ // create the thumbnail image
+ float aspectRatio = appPlayer->GetRenderAspectRatio();
+ int width = BOOKMARK_THUMB_WIDTH;
+ int height = (int)(BOOKMARK_THUMB_WIDTH / aspectRatio);
+ if (height > (int)BOOKMARK_THUMB_WIDTH)
+ {
+ height = BOOKMARK_THUMB_WIDTH;
+ width = (int)(BOOKMARK_THUMB_WIDTH * aspectRatio);
+ }
+
+
+ uint8_t *pixels = (uint8_t*)malloc(height * width * 4);
+ unsigned int captureId = appPlayer->RenderCaptureAlloc();
+
+ appPlayer->RenderCapture(captureId, width, height, CAPTUREFLAG_IMMEDIATELY);
+ bool hasImage = appPlayer->RenderCaptureGetPixels(captureId, 1000, pixels, height * width * 4);
+
+ if (hasImage)
+ {
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ auto crc = Crc32::ComputeFromLowerCase(g_application.CurrentFile());
+ bookmark.thumbNailImage =
+ StringUtils::Format("{:08x}_{}.jpg", crc, (int)bookmark.timeInSeconds);
+ bookmark.thumbNailImage = URIUtils::AddFileToFolder(profileManager->GetBookmarksThumbFolder(), bookmark.thumbNailImage);
+
+ if (!CPicture::CreateThumbnailFromSurface(pixels, width, height, width * 4,
+ bookmark.thumbNailImage))
+ {
+ bookmark.thumbNailImage.clear();
+ }
+ else
+ CLog::Log(LOGERROR,"CGUIDialogVideoBookmarks: failed to create thumbnail");
+
+ appPlayer->RenderCaptureRelease(captureId);
+ }
+ else
+ CLog::Log(LOGERROR,"CGUIDialogVideoBookmarks: failed to create thumbnail 2");
+
+ free(pixels);
+
+ videoDatabase.Open();
+ if (tag)
+ videoDatabase.AddBookMarkForEpisode(*tag, bookmark);
+ else
+ {
+ std::string path = g_application.CurrentFile();
+ if (g_application.CurrentFileItem().HasProperty("original_listitem_url") &&
+ !URIUtils::IsVideoDb(g_application.CurrentFileItem().GetProperty("original_listitem_url").asString()))
+ path = g_application.CurrentFileItem().GetProperty("original_listitem_url").asString();
+ videoDatabase.AddBookMarkToFile(path, bookmark, CBookmark::STANDARD);
+ }
+ videoDatabase.Close();
+ return true;
+}
+
+void CGUIDialogVideoBookmarks::OnWindowLoaded()
+{
+ CGUIDialog::OnWindowLoaded();
+ m_viewControl.Reset();
+ m_viewControl.SetParentWindow(GetID());
+ m_viewControl.AddView(GetControl(CONTROL_THUMBS));
+ m_jobsStarted = 0;
+ m_mapJobsChapter.clear();
+ m_vecItems->Clear();
+}
+
+void CGUIDialogVideoBookmarks::OnWindowUnload()
+{
+ //stop running thumb extraction jobs
+ CancelJobs();
+ m_mapJobsChapter.clear();
+ m_vecItems->Clear();
+ CGUIDialog::OnWindowUnload();
+ m_viewControl.Reset();
+}
+
+CGUIControl *CGUIDialogVideoBookmarks::GetFirstFocusableControl(int id)
+{
+ if (m_viewControl.HasControl(id))
+ id = m_viewControl.GetCurrentControl();
+ return CGUIWindow::GetFirstFocusableControl(id);
+}
+
+bool CGUIDialogVideoBookmarks::AddEpisodeBookmark()
+{
+ std::vector<CVideoInfoTag> episodes;
+ CVideoDatabase videoDatabase;
+ videoDatabase.Open();
+ videoDatabase.GetEpisodesByFile(g_application.CurrentFile(), episodes);
+ videoDatabase.Close();
+ if (!episodes.empty())
+ {
+ CContextButtons choices;
+ for (unsigned int i=0; i < episodes.size(); ++i)
+ {
+ std::string strButton =
+ StringUtils::Format("{} {}, {} {}", g_localizeStrings.Get(20373), episodes[i].m_iSeason,
+ g_localizeStrings.Get(20359), episodes[i].m_iEpisode);
+ choices.Add(i, strButton);
+ }
+
+ int pressed = CGUIDialogContextMenu::ShowAndGetChoice(choices);
+ if (pressed >= 0)
+ {
+ AddBookmark(&episodes[pressed]);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+bool CGUIDialogVideoBookmarks::OnAddBookmark()
+{
+ if (!g_application.CurrentFileItem().IsVideo())
+ return false;
+
+ if (CGUIDialogVideoBookmarks::AddBookmark())
+ {
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_REFRESH_LIST, 0, WINDOW_DIALOG_VIDEO_BOOKMARKS);
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info,
+ g_localizeStrings.Get(298), // "Bookmarks"
+ g_localizeStrings.Get(21362));// "Bookmark created"
+ return true;
+ }
+ return false;
+}
+
+bool CGUIDialogVideoBookmarks::OnAddEpisodeBookmark()
+{
+ bool bReturn = false;
+ if (g_application.CurrentFileItem().HasVideoInfoTag() && g_application.CurrentFileItem().GetVideoInfoTag()->m_iEpisode > -1)
+ {
+ CVideoDatabase videoDatabase;
+ videoDatabase.Open();
+ std::vector<CVideoInfoTag> episodes;
+ videoDatabase.GetEpisodesByFile(g_application.CurrentFile(),episodes);
+ if (episodes.size() > 1)
+ {
+ bReturn = CGUIDialogVideoBookmarks::AddEpisodeBookmark();
+ if(bReturn)
+ {
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_REFRESH_LIST, 0, WINDOW_DIALOG_VIDEO_BOOKMARKS);
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info,
+ g_localizeStrings.Get(298), // "Bookmarks"
+ g_localizeStrings.Get(21363));// "Episode Bookmark created"
+
+ }
+ }
+ videoDatabase.Close();
+ }
+ return bReturn;
+}
+
+void CGUIDialogVideoBookmarks::OnJobComplete(unsigned int jobID,
+ bool success, CJob* job)
+{
+ if (success && IsActive())
+ {
+ MAPJOBSCHAPS::iterator iter = m_mapJobsChapter.find(job);
+ if (iter != m_mapJobsChapter.end())
+ {
+ unsigned int chapterIdx = (*iter).second;
+ CGUIMessage m(GUI_MSG_REFRESH_LIST, GetID(), 0, 1, chapterIdx);
+ CServiceBroker::GetAppMessenger()->SendGUIMessage(m);
+ m_mapJobsChapter.erase(iter);
+ }
+ }
+ CJobQueue::OnJobComplete(jobID, success, job);
+}
diff --git a/xbmc/video/dialogs/GUIDialogVideoBookmarks.h b/xbmc/video/dialogs/GUIDialogVideoBookmarks.h
new file mode 100644
index 0000000..801afcd
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogVideoBookmarks.h
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "guilib/GUIDialog.h"
+#include "utils/JobManager.h"
+#include "video/VideoDatabase.h"
+#include "view/GUIViewControl.h"
+
+class CFileItemList;
+
+class CGUIDialogVideoBookmarks : public CGUIDialog, public CJobQueue
+{
+ typedef std::map<CJob*, unsigned int> MAPJOBSCHAPS;
+
+public:
+ CGUIDialogVideoBookmarks(void);
+ ~CGUIDialogVideoBookmarks(void) override;
+ bool OnMessage(CGUIMessage& message) override;
+ void OnWindowLoaded() override;
+ void OnWindowUnload() override;
+ bool OnAction(const CAction &action) override;
+
+ /*!
+ \brief Creates a bookmark of the currently playing video file.
+
+ NOTE: sends a GUI_MSG_REFRESH_LIST message to DialogVideoBookmark on success
+ \return True if creation of bookmark was successful
+ \sa OnAddEpisodeBookmark
+ */
+ static bool OnAddBookmark();
+
+ /*!
+ \brief Creates an episode bookmark of the currently playing file
+
+ An episode bookmark specifies the end/beginning of episodes on files like: S01E01E02
+ Fails if the current video isn't a multi-episode file
+ NOTE: sends a GUI_MSG_REFRESH_LIST message to DialogVideoBookmark on success
+ \return True, if bookmark was successfully created
+ \sa OnAddBookmark
+ **/
+ static bool OnAddEpisodeBookmark();
+
+
+ void Update();
+protected:
+ void GotoBookmark(int iItem);
+ void ClearBookmarks();
+ static bool AddEpisodeBookmark();
+ static bool AddBookmark(CVideoInfoTag *tag=NULL);
+ void Delete(int item);
+ void Clear();
+ void OnRefreshList();
+ void OnPopupMenu(int item);
+ CGUIControl *GetFirstFocusableControl(int id) override;
+
+ void OnJobComplete(unsigned int jobID, bool success, CJob* job) override;
+
+ CFileItemList* m_vecItems;
+ CGUIViewControl m_viewControl;
+ VECBOOKMARKS m_bookmarks;
+
+private:
+ void UpdateItem(unsigned int chapterIdx);
+
+ int m_jobsStarted;
+ std::string m_filePath;
+ CCriticalSection m_refreshSection;
+ MAPJOBSCHAPS m_mapJobsChapter;
+};
diff --git a/xbmc/video/dialogs/GUIDialogVideoInfo.cpp b/xbmc/video/dialogs/GUIDialogVideoInfo.cpp
new file mode 100644
index 0000000..240d672
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogVideoInfo.cpp
@@ -0,0 +1,2398 @@
+/*
+ * 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 "GUIDialogVideoInfo.h"
+
+#include "ContextMenuManager.h"
+#include "FileItem.h"
+#include "GUIPassword.h"
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "TextureCache.h"
+#include "Util.h"
+#include "dialogs/GUIDialogFileBrowser.h"
+#include "dialogs/GUIDialogProgress.h"
+#include "dialogs/GUIDialogSelect.h"
+#include "dialogs/GUIDialogYesNo.h"
+#include "filesystem/Directory.h"
+#include "filesystem/VideoDatabaseDirectory.h"
+#include "filesystem/VideoDatabaseDirectory/QueryParams.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIImage.h"
+#include "guilib/GUIKeyboardFactory.h"
+#include "guilib/GUIWindow.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "input/Key.h"
+#include "messaging/helpers/DialogOKHelper.h"
+#include "music/MusicDatabase.h"
+#include "music/dialogs/GUIDialogMusicInfo.h"
+#include "playlists/PlayListTypes.h"
+#include "profiles/ProfileManager.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/MediaSourceSettings.h"
+#include "settings/SettingUtils.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "storage/MediaManager.h"
+#include "utils/FileExtensionProvider.h"
+#include "utils/FileUtils.h"
+#include "utils/SortUtils.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "video/VideoDbUrl.h"
+#include "video/VideoInfoScanner.h"
+#include "video/VideoInfoTag.h"
+#include "video/VideoLibraryQueue.h"
+#include "video/VideoThumbLoader.h"
+#include "video/tags/VideoTagLoaderFFmpeg.h"
+#include "video/windows/GUIWindowVideoNav.h"
+
+#include <iterator>
+#include <string>
+
+using namespace XFILE::VIDEODATABASEDIRECTORY;
+using namespace XFILE;
+using namespace KODI::MESSAGING;
+
+#define CONTROL_IMAGE 3
+#define CONTROL_TEXTAREA 4
+#define CONTROL_BTN_TRACKS 5
+#define CONTROL_BTN_REFRESH 6
+#define CONTROL_BTN_USERRATING 7
+#define CONTROL_BTN_PLAY 8
+#define CONTROL_BTN_RESUME 9
+#define CONTROL_BTN_GET_THUMB 10
+#define CONTROL_BTN_PLAY_TRAILER 11
+#define CONTROL_BTN_GET_FANART 12
+#define CONTROL_BTN_DIRECTOR 13
+
+#define CONTROL_LIST 50
+
+// predicate used by sorting and set_difference
+bool compFileItemsByDbId(const CFileItemPtr& lhs, const CFileItemPtr& rhs)
+{
+ return lhs->HasVideoInfoTag() && rhs->HasVideoInfoTag() && lhs->GetVideoInfoTag()->m_iDbId < rhs->GetVideoInfoTag()->m_iDbId;
+}
+
+CGUIDialogVideoInfo::CGUIDialogVideoInfo(void)
+ : CGUIDialog(WINDOW_DIALOG_VIDEO_INFO, "DialogVideoInfo.xml"),
+ m_movieItem(new CFileItem),
+ m_castList(new CFileItemList)
+{
+ m_loadType = KEEP_IN_MEMORY;
+}
+
+CGUIDialogVideoInfo::~CGUIDialogVideoInfo(void)
+{
+ delete m_castList;
+}
+
+bool CGUIDialogVideoInfo::OnMessage(CGUIMessage& message)
+{
+ switch ( message.GetMessage() )
+ {
+ case GUI_MSG_WINDOW_DEINIT:
+ {
+ ClearCastList();
+
+ if (m_startUserrating != m_movieItem->GetVideoInfoTag()->m_iUserRating)
+ {
+ CVideoDatabase db;
+ if (db.Open())
+ {
+ m_hasUpdatedUserrating = true;
+ db.SetVideoUserRating(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_iUserRating, m_movieItem->GetVideoInfoTag()->m_type);
+ db.Close();
+ }
+ }
+ }
+ break;
+
+ case GUI_MSG_CLICKED:
+ {
+ int iControl = message.GetSenderId();
+ if (iControl == CONTROL_BTN_REFRESH)
+ {
+ if (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeTvShow)
+ {
+ bool bCanceled=false;
+ if (CGUIDialogYesNo::ShowAndGetInput(CVariant{20377}, CVariant{20378}, bCanceled, CVariant{ "" }, CVariant{ "" }, CGUIDialogYesNo::NO_TIMEOUT))
+ {
+ m_bRefreshAll = true;
+ CVideoDatabase db;
+ if (db.Open())
+ {
+ db.SetPathHash(m_movieItem->GetVideoInfoTag()->m_strPath,"");
+ db.Close();
+ }
+ }
+ else
+ m_bRefreshAll = false;
+
+ if (bCanceled)
+ return false;
+ }
+ m_bRefresh = true;
+ Close();
+ return true;
+ }
+ else if (iControl == CONTROL_BTN_TRACKS)
+ {
+ m_bViewReview = !m_bViewReview;
+ Update();
+ }
+ else if (iControl == CONTROL_BTN_PLAY)
+ {
+ Play();
+ }
+ else if (iControl == CONTROL_BTN_USERRATING)
+ {
+ OnSetUserrating();
+ }
+ else if (iControl == CONTROL_BTN_RESUME)
+ {
+ Play(true);
+ }
+ else if (iControl == CONTROL_BTN_GET_THUMB)
+ {
+ OnGetArt();
+ }
+ else if (iControl == CONTROL_BTN_PLAY_TRAILER)
+ {
+ PlayTrailer();
+ }
+ else if (iControl == CONTROL_BTN_GET_FANART)
+ {
+ OnGetFanart();
+ }
+ else if (iControl == CONTROL_BTN_DIRECTOR)
+ {
+ auto directors = m_movieItem->GetVideoInfoTag()->m_director;
+ if (directors.size() == 0)
+ return true;
+ if (directors.size() == 1)
+ OnSearch(directors[0]);
+ else
+ {
+ auto pDlgSelect = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
+ if (pDlgSelect)
+ {
+ pDlgSelect->Reset();
+ pDlgSelect->SetHeading(CVariant{22080});
+ for (const auto &director: directors)
+ pDlgSelect->Add(director);
+ pDlgSelect->Open();
+
+ int iItem = pDlgSelect->GetSelectedItem();
+ if (iItem < 0)
+ return true;
+ OnSearch(directors[iItem]);
+ }
+ }
+ }
+ else if (iControl == CONTROL_LIST)
+ {
+ int iAction = message.GetParam1();
+ if (ACTION_SELECT_ITEM == iAction || ACTION_MOUSE_LEFT_CLICK == iAction)
+ {
+ CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl);
+ OnMessage(msg);
+ int iItem = msg.GetParam1();
+ if (iItem < 0 || iItem >= m_castList->Size())
+ break;
+ std::string strItem = m_castList->Get(iItem)->GetLabel();
+ if (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeVideoCollection)
+ {
+ SetMovie(m_castList->Get(iItem).get());
+ Close();
+ Open();
+ }
+ else
+ OnSearch(strItem);
+ }
+ }
+ }
+ break;
+ case GUI_MSG_NOTIFY_ALL:
+ {
+ if (IsActive() && message.GetParam1() == GUI_MSG_UPDATE_ITEM && message.GetItem())
+ {
+ CFileItemPtr item = std::static_pointer_cast<CFileItem>(message.GetItem());
+ if (item && m_movieItem->IsPath(item->GetPath()))
+ { // Just copy over the stream details and the thumb if we don't already have one
+ if (!m_movieItem->HasArt("thumb"))
+ m_movieItem->SetArt("thumb", item->GetArt("thumb"));
+ m_movieItem->GetVideoInfoTag()->m_streamDetails = item->GetVideoInfoTag()->m_streamDetails;
+ }
+ return true;
+ }
+ }
+ }
+
+ return CGUIDialog::OnMessage(message);
+}
+
+void CGUIDialogVideoInfo::OnInitWindow()
+{
+ m_bRefresh = false;
+ m_bRefreshAll = true;
+ m_hasUpdatedThumb = false;
+ m_hasUpdatedUserrating = false;
+ m_bViewReview = true;
+
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ const std::string uniqueId = m_movieItem->GetProperty("xxuniqueid").asString();
+ if (uniqueId.empty() || !StringUtils::StartsWithNoCase(uniqueId.c_str(), "xx"))
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_REFRESH,
+ (profileManager->GetCurrentProfile().canWriteDatabases() ||
+ g_passwordManager.bMasterUser));
+ else
+ CONTROL_DISABLE(CONTROL_BTN_REFRESH);
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_GET_THUMB,
+ (profileManager->GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser) &&
+ !StringUtils::StartsWithNoCase(m_movieItem->GetVideoInfoTag()->
+ GetUniqueID().c_str(), "plugin"));
+ // Disable video user rating button for plugins and sets as they don't have tables to save this
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_USERRATING, !m_movieItem->IsPlugin() && m_movieItem->GetVideoInfoTag()->m_type != MediaTypeVideoCollection);
+
+ VideoDbContentType type = m_movieItem->GetVideoContentType();
+ if (type == VideoDbContentType::TVSHOWS || type == VideoDbContentType::MOVIES)
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_GET_FANART, (profileManager->
+ GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser) &&
+ !StringUtils::StartsWithNoCase(m_movieItem->GetVideoInfoTag()->
+ GetUniqueID().c_str(), "plugin"));
+ else
+ CONTROL_DISABLE(CONTROL_BTN_GET_FANART);
+
+ Update();
+
+ CGUIDialog::OnInitWindow();
+}
+
+bool CGUIDialogVideoInfo::OnAction(const CAction &action)
+{
+ int userrating = m_movieItem->GetVideoInfoTag()->m_iUserRating;
+ if (action.GetID() == ACTION_INCREASE_RATING)
+ {
+ SetUserrating(userrating + 1);
+ return true;
+ }
+ else if (action.GetID() == ACTION_DECREASE_RATING)
+ {
+ SetUserrating(userrating - 1);
+ return true;
+ }
+ else if (action.GetID() == ACTION_SHOW_INFO)
+ {
+ Close();
+ return true;
+ }
+ return CGUIDialog::OnAction(action);
+}
+
+void CGUIDialogVideoInfo::SetUserrating(int userrating) const
+{
+ userrating = std::max(userrating, 0);
+ userrating = std::min(userrating, 10);
+ if (userrating != m_movieItem->GetVideoInfoTag()->m_iUserRating)
+ {
+ m_movieItem->GetVideoInfoTag()->SetUserrating(userrating);
+
+ // send a message to all windows to tell them to update the fileitem (eg playlistplayer, media windows)
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_movieItem);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+ }
+}
+
+void CGUIDialogVideoInfo::SetMovie(const CFileItem *item)
+{
+ *m_movieItem = *item;
+
+ // setup cast list
+ ClearCastList();
+
+ // When the scraper throws an error, the video tag can be null here
+ if (!item->HasVideoInfoTag())
+ return;
+
+ MediaType type = item->GetVideoInfoTag()->m_type;
+
+ m_startUserrating = m_movieItem->GetVideoInfoTag()->m_iUserRating;
+
+ if (type == MediaTypeMusicVideo)
+ { // music video
+ CMusicDatabase database;
+ database.Open();
+ const std::vector<std::string> &artists = m_movieItem->GetVideoInfoTag()->m_artist;
+ for (std::vector<std::string>::const_iterator it = artists.begin(); it != artists.end(); ++it)
+ {
+ int idArtist = database.GetArtistByName(*it);
+ std::string thumb = database.GetArtForItem(idArtist, MediaTypeArtist, "thumb");
+ CFileItemPtr item(new CFileItem(*it));
+ if (!thumb.empty())
+ item->SetArt("thumb", thumb);
+ item->SetArt("icon", "DefaultArtist.png");
+ item->SetLabel2(g_localizeStrings.Get(29904));
+ m_castList->Add(item);
+ }
+ // get performers in the music video (added as actors)
+ for (CVideoInfoTag::iCast it = m_movieItem->GetVideoInfoTag()->m_cast.begin();
+ it != m_movieItem->GetVideoInfoTag()->m_cast.end(); ++it)
+ {
+ // Check to see if we have already added this performer as the artist and skip adding if so
+ auto haveArtist = std::find(std::begin(artists), std::end(artists), it->strName);
+ if (haveArtist == artists.end()) // artist or performer not already in the list
+ {
+ CFileItemPtr item(new CFileItem(it->strName));
+ if (!it->thumb.empty())
+ item->SetArt("thumb", it->thumb);
+ else if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
+ CSettings::SETTING_VIDEOLIBRARY_ACTORTHUMBS))
+ { // backward compatibility
+ std::string thumb = CScraperUrl::GetThumbUrl(it->thumbUrl.GetFirstUrlByType());
+ if (!thumb.empty())
+ {
+ item->SetArt("thumb", thumb);
+ CServiceBroker::GetTextureCache()->BackgroundCacheImage(thumb);
+ }
+ }
+ item->SetArt("icon", "DefaultActor.png");
+ item->SetLabel(it->strName);
+ item->SetLabel2(it->strRole);
+ m_castList->Add(item);
+ }
+ }
+ }
+ else if (type == MediaTypeVideoCollection)
+ {
+ CVideoDatabase database;
+ database.Open();
+ database.GetMoviesNav(m_movieItem->GetPath(), *m_castList, -1, -1, -1, -1, -1, -1,
+ m_movieItem->GetVideoInfoTag()->m_set.id, -1,
+ SortDescription(), VideoDbDetailsAll);
+ m_castList->Sort(SortBySortTitle, SortOrderDescending);
+ CVideoThumbLoader loader;
+ for (auto& item : *m_castList)
+ loader.LoadItem(item.get());
+ }
+ else
+ { // movie/show/episode
+ for (CVideoInfoTag::iCast it = m_movieItem->GetVideoInfoTag()->m_cast.begin(); it != m_movieItem->GetVideoInfoTag()->m_cast.end(); ++it)
+ {
+ CFileItemPtr item(new CFileItem(it->strName));
+ if (!it->thumb.empty())
+ item->SetArt("thumb", it->thumb);
+ else
+ {
+ const std::shared_ptr<CSettings> settings =
+ CServiceBroker::GetSettingsComponent()->GetSettings();
+ if (settings->GetInt(CSettings::SETTING_VIDEOLIBRARY_ARTWORK_LEVEL) !=
+ CSettings::VIDEOLIBRARY_ARTWORK_LEVEL_NONE &&
+ settings->GetBool(CSettings::SETTING_VIDEOLIBRARY_ACTORTHUMBS))
+ { // backward compatibility
+ std::string thumb = CScraperUrl::GetThumbUrl(it->thumbUrl.GetFirstUrlByType());
+ if (!thumb.empty())
+ {
+ item->SetArt("thumb", thumb);
+ CServiceBroker::GetTextureCache()->BackgroundCacheImage(thumb);
+ }
+ }
+ }
+ item->SetArt("icon", "DefaultActor.png");
+ item->SetLabel(it->strName);
+ item->SetLabel2(it->strRole);
+ m_castList->Add(item);
+ }
+ }
+
+ if (type == MediaTypeMovie)
+ {
+ // local trailers should always override non-local, so check
+ // for a local one if the registered trailer is online
+ if (m_movieItem->GetVideoInfoTag()->m_strTrailer.empty() ||
+ URIUtils::IsInternetStream(m_movieItem->GetVideoInfoTag()->m_strTrailer))
+ {
+ std::string localTrailer = m_movieItem->FindTrailer();
+ if (!localTrailer.empty())
+ {
+ m_movieItem->GetVideoInfoTag()->m_strTrailer = localTrailer;
+ CVideoDatabase database;
+ if (database.Open())
+ {
+ database.SetSingleValue(VideoDbContentType::MOVIES, VIDEODB_ID_TRAILER,
+ m_movieItem->GetVideoInfoTag()->m_iDbId,
+ m_movieItem->GetVideoInfoTag()->m_strTrailer);
+ database.Close();
+ CUtil::DeleteVideoDatabaseDirectoryCache();
+ }
+ }
+ }
+ }
+
+ m_castList->SetContent(CMediaTypes::ToPlural(type));
+
+ CVideoThumbLoader loader;
+ loader.LoadItem(m_movieItem.get());
+}
+
+void CGUIDialogVideoInfo::Update()
+{
+ // setup plot text area
+ std::shared_ptr<CSettingList> setting(std::dynamic_pointer_cast<CSettingList>(
+ CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(CSettings::SETTING_VIDEOLIBRARY_SHOWUNWATCHEDPLOTS)));
+ std::string strTmp = m_movieItem->GetVideoInfoTag()->m_strPlot;
+ if (m_movieItem->GetVideoInfoTag()->m_type != MediaTypeTvShow)
+ if (m_movieItem->GetVideoInfoTag()->GetPlayCount() == 0 && setting &&
+ ((m_movieItem->GetVideoInfoTag()->m_type == MediaTypeMovie &&
+ !CSettingUtils::FindIntInList(setting,
+ CSettings::VIDEOLIBRARY_PLOTS_SHOW_UNWATCHED_MOVIES)) ||
+ (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeEpisode &&
+ !CSettingUtils::FindIntInList(
+ setting, CSettings::VIDEOLIBRARY_PLOTS_SHOW_UNWATCHED_TVSHOWEPISODES))))
+ strTmp = g_localizeStrings.Get(20370);
+
+ StringUtils::Trim(strTmp);
+ SetLabel(CONTROL_TEXTAREA, strTmp);
+
+ CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST, 0, 0, m_castList);
+ OnMessage(msg);
+
+ if (GetControl(CONTROL_BTN_TRACKS)) // if no CONTROL_BTN_TRACKS found - allow skinner full visibility control over CONTROL_TEXTAREA and CONTROL_LIST
+ {
+ if (m_bViewReview)
+ {
+ if (!m_movieItem->GetVideoInfoTag()->m_artist.empty())
+ {
+ SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 133);
+ }
+ else if (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeVideoCollection)
+ {
+ SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 20342);
+ }
+ else
+ {
+ SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 206);
+ }
+
+ SET_CONTROL_HIDDEN(CONTROL_LIST);
+ SET_CONTROL_VISIBLE(CONTROL_TEXTAREA);
+ }
+ else
+ {
+ SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 207);
+
+ SET_CONTROL_HIDDEN(CONTROL_TEXTAREA);
+ SET_CONTROL_VISIBLE(CONTROL_LIST);
+ }
+ }
+
+ // Check for resumability
+ if (m_movieItem->GetVideoInfoTag()->GetResumePoint().timeInSeconds > 0.0)
+ CONTROL_ENABLE(CONTROL_BTN_RESUME);
+ else
+ CONTROL_DISABLE(CONTROL_BTN_RESUME);
+
+ CONTROL_ENABLE(CONTROL_BTN_PLAY);
+
+ // update the thumbnail
+ CGUIControl* pControl = GetControl(CONTROL_IMAGE);
+ if (pControl)
+ {
+ CGUIImage* pImageControl = static_cast<CGUIImage*>(pControl);
+ pImageControl->FreeResources();
+ pImageControl->SetFileName(m_movieItem->GetArt("thumb"));
+ }
+ // tell our GUI to completely reload all controls (as some of them
+ // are likely to have had this image in use so will need refreshing)
+ if (m_hasUpdatedThumb)
+ {
+ CGUIMessage reload(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(reload);
+ }
+}
+
+bool CGUIDialogVideoInfo::NeedRefresh() const
+{
+ return m_bRefresh;
+}
+
+bool CGUIDialogVideoInfo::RefreshAll() const
+{
+ return m_bRefreshAll;
+}
+
+void CGUIDialogVideoInfo::OnSearch(std::string& strSearch)
+{
+ CGUIDialogProgress *progress = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS);
+ if (progress)
+ {
+ progress->SetHeading(CVariant{194});
+ progress->SetLine(0, CVariant{strSearch});
+ progress->SetLine(1, CVariant{""});
+ progress->SetLine(2, CVariant{""});
+ progress->Open();
+ progress->Progress();
+ }
+ CFileItemList items;
+ DoSearch(strSearch, items);
+
+ if (progress)
+ progress->Close();
+
+ if (items.Size())
+ {
+ CGUIDialogSelect* pDlgSelect = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
+ if (pDlgSelect)
+ {
+ pDlgSelect->Reset();
+ pDlgSelect->SetHeading(CVariant{283});
+
+ CVideoThumbLoader loader;
+ for (int i = 0; i < items.Size(); i++)
+ {
+ if (items[i]->HasVideoInfoTag() &&
+ items[i]->GetVideoInfoTag()->GetPlayCount() > 0)
+ items[i]->SetLabel2(g_localizeStrings.Get(16102));
+
+ loader.LoadItem(items[i].get());
+ pDlgSelect->Add(*items[i]);
+ }
+
+ pDlgSelect->SetUseDetails(true);
+ pDlgSelect->Open();
+
+ int iItem = pDlgSelect->GetSelectedItem();
+ if (iItem < 0)
+ return;
+
+ OnSearchItemFound(items[iItem].get());
+ }
+ }
+ else
+ {
+ HELPERS::ShowOKDialogText(CVariant{194}, CVariant{284});
+ }
+}
+
+void CGUIDialogVideoInfo::DoSearch(std::string& strSearch, CFileItemList& items) const
+{
+ CVideoDatabase db;
+ if (!db.Open())
+ return;
+
+ CFileItemList movies;
+ db.GetMoviesByActor(strSearch, movies);
+ for (int i = 0; i < movies.Size(); ++i)
+ {
+ std::string label = movies[i]->GetVideoInfoTag()->m_strTitle;
+ if (movies[i]->GetVideoInfoTag()->HasYear())
+ label += StringUtils::Format(" ({})", movies[i]->GetVideoInfoTag()->GetYear());
+ movies[i]->SetLabel(label);
+ }
+ CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20338) + "] ", items);
+
+ db.GetTvShowsByActor(strSearch, movies);
+ for (int i = 0; i < movies.Size(); ++i)
+ {
+ std::string label = movies[i]->GetVideoInfoTag()->m_strShowTitle;
+ if (movies[i]->GetVideoInfoTag()->HasYear())
+ label += StringUtils::Format(" ({})", movies[i]->GetVideoInfoTag()->GetYear());
+ movies[i]->SetLabel(label);
+ }
+ CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20364) + "] ", items);
+
+ db.GetEpisodesByActor(strSearch, movies);
+ for (int i = 0; i < movies.Size(); ++i)
+ {
+ std::string label = movies[i]->GetVideoInfoTag()->m_strTitle + " (" + movies[i]->GetVideoInfoTag()->m_strShowTitle + ")";
+ movies[i]->SetLabel(label);
+ }
+ CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20359) + "] ", items);
+
+ db.GetMusicVideosByArtist(strSearch, movies);
+ for (int i = 0; i < movies.Size(); ++i)
+ {
+ std::string label = StringUtils::Join(movies[i]->GetVideoInfoTag()->m_artist, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator) + " - " + movies[i]->GetVideoInfoTag()->m_strTitle;
+ if (movies[i]->GetVideoInfoTag()->HasYear())
+ label += StringUtils::Format(" ({})", movies[i]->GetVideoInfoTag()->GetYear());
+ movies[i]->SetLabel(label);
+ }
+ CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20391) + "] ", items);
+ db.Close();
+
+ // Search for music albums by artist with name matching search string
+ CMusicDatabase music_database;
+ if (!music_database.Open())
+ return;
+
+ if (music_database.SearchAlbumsByArtistName(strSearch, movies))
+ {
+ for (int i = 0; i < movies.Size(); ++i)
+ {
+ // Set type so that video thumbloader handles album art
+ movies[i]->GetVideoInfoTag()->m_type = MediaTypeAlbum;
+ }
+ CGUIWindowVideoBase::AppendAndClearSearchItems(
+ movies, "[" + g_localizeStrings.Get(36918) + "] ", items);
+ }
+ music_database.Close();
+}
+
+void CGUIDialogVideoInfo::OnSearchItemFound(const CFileItem* pItem)
+{
+ VideoDbContentType type = pItem->GetVideoContentType();
+
+ CVideoDatabase db;
+ if (!db.Open())
+ return;
+
+ CVideoInfoTag movieDetails;
+ if (type == VideoDbContentType::MOVIES)
+ db.GetMovieInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
+ if (type == VideoDbContentType::EPISODES)
+ db.GetEpisodeInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
+ if (type == VideoDbContentType::TVSHOWS)
+ db.GetTvShowInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
+ if (type == VideoDbContentType::MUSICVIDEOS)
+ db.GetMusicVideoInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
+ db.Close();
+ if (type == VideoDbContentType::MUSICALBUMS)
+ {
+ Close();
+ CGUIDialogMusicInfo::ShowFor(const_cast<CFileItem*>(pItem));
+ return; // No video info to refresh so just close the window and go back to the fileitem list
+ }
+
+ CFileItem item(*pItem);
+ *item.GetVideoInfoTag() = movieDetails;
+ SetMovie(&item);
+ // refresh our window entirely
+ Close();
+ Open();
+}
+
+void CGUIDialogVideoInfo::ClearCastList()
+{
+ CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_LIST);
+ OnMessage(msg);
+ m_castList->Clear();
+}
+
+void CGUIDialogVideoInfo::Play(bool resume)
+{
+ if (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeTvShow)
+ {
+ std::string strPath;
+ if (m_movieItem->IsPlugin())
+ {
+ strPath = m_movieItem->GetPath();
+ Close();
+ if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VIDEO_NAV)
+ {
+ CGUIMessage message(GUI_MSG_NOTIFY_ALL, CServiceBroker::GetGUI()->
+ GetWindowManager().GetActiveWindow(), 0, GUI_MSG_UPDATE, 0);
+ message.SetStringParam(strPath);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
+ }
+ else
+ CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_VIDEO_NAV,strPath);
+ }
+ else
+ {
+ strPath = StringUtils::Format("videodb://tvshows/titles/{}/",
+ m_movieItem->GetVideoInfoTag()->m_iDbId);
+ Close();
+ CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_VIDEO_NAV,strPath);
+ }
+ return;
+ }
+
+ if (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeVideoCollection)
+ {
+ std::string strPath = StringUtils::Format("videodb://movies/sets/{}/?setid={}",
+ m_movieItem->GetVideoInfoTag()->m_iDbId,
+ m_movieItem->GetVideoInfoTag()->m_iDbId);
+ Close();
+ CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_VIDEO_NAV, strPath);
+ return;
+ }
+
+ CGUIWindowVideoNav* pWindow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowVideoNav>(WINDOW_VIDEO_NAV);
+ if (pWindow)
+ {
+ // close our dialog
+ Close(true);
+ if (resume)
+ m_movieItem->SetStartOffset(STARTOFFSET_RESUME);
+ else if (!CGUIWindowVideoBase::ShowResumeMenu(*m_movieItem))
+ {
+ // The Resume dialog was closed without any choice
+ Open();
+ return;
+ }
+ m_movieItem->SetProperty("playlist_type_hint", PLAYLIST::TYPE_VIDEO);
+
+ pWindow->PlayMovie(m_movieItem.get());
+ }
+}
+
+namespace
+{
+// Add art types required in Kodi and configured by the user
+void AddHardCodedAndExtendedArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTag& tag)
+{
+ for (const auto& artType : CVideoThumbLoader::GetArtTypes(tag.m_type))
+ {
+ if (find(artTypes.cbegin(), artTypes.cend(), artType) == artTypes.cend())
+ artTypes.emplace_back(artType);
+ }
+}
+
+// Add art types currently assigned to the media item
+void AddCurrentArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTag& tag,
+ CVideoDatabase& db)
+{
+ std::map<std::string, std::string> currentArt;
+ db.GetArtForItem(tag.m_iDbId, tag.m_type, currentArt);
+ for (const auto& art : currentArt)
+ {
+ if (!art.second.empty() && find(artTypes.cbegin(), artTypes.cend(), art.first) == artTypes.cend())
+ artTypes.push_back(art.first);
+ }
+}
+
+// Add art types that exist for other media items of the same type
+void AddMediaTypeArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTag& tag,
+ CVideoDatabase& db)
+{
+ std::vector<std::string> dbArtTypes;
+ db.GetArtTypes(tag.m_type, dbArtTypes);
+ for (const auto& artType : dbArtTypes)
+ {
+ if (find(artTypes.cbegin(), artTypes.cend(), artType) == artTypes.cend())
+ artTypes.push_back(artType);
+ }
+}
+
+// Add art types from available but unassigned artwork for this media item
+void AddAvailableArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTag& tag,
+ CVideoDatabase& db)
+{
+ for (const auto& artType : db.GetAvailableArtTypesForItem(tag.m_iDbId, tag.m_type))
+ {
+ if (find(artTypes.cbegin(), artTypes.cend(), artType) == artTypes.cend())
+ artTypes.push_back(artType);
+ }
+}
+
+std::vector<std::string> GetArtTypesList(const CVideoInfoTag& tag)
+{
+ CVideoDatabase db;
+ db.Open();
+
+ std::vector<std::string> artTypes;
+
+ AddHardCodedAndExtendedArtTypes(artTypes, tag);
+ AddCurrentArtTypes(artTypes, tag, db);
+ AddMediaTypeArtTypes(artTypes, tag, db);
+ AddAvailableArtTypes(artTypes, tag, db);
+
+ db.Close();
+ return artTypes;
+}
+}
+
+std::string CGUIDialogVideoInfo::ChooseArtType(const CFileItem &videoItem)
+{
+ // prompt for choice
+ CGUIDialogSelect *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
+ if (!dialog || !videoItem.HasVideoInfoTag())
+ return "";
+
+ CFileItemList items;
+ dialog->SetHeading(CVariant{13511});
+ dialog->Reset();
+ dialog->SetUseDetails(true);
+ dialog->EnableButton(true, 13516);
+
+ std::vector<std::string> artTypes = GetArtTypesList(*videoItem.GetVideoInfoTag());
+
+ for (std::vector<std::string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
+ {
+ const std::string& type = *i;
+ CFileItemPtr item(new CFileItem(type, false));
+ if (type == "banner")
+ item->SetLabel(g_localizeStrings.Get(20020));
+ else if (type == "fanart")
+ item->SetLabel(g_localizeStrings.Get(20445));
+ else if (type == "poster")
+ item->SetLabel(g_localizeStrings.Get(20021));
+ else if (type == "thumb")
+ item->SetLabel(g_localizeStrings.Get(21371));
+ else
+ item->SetLabel(type);
+ item->SetProperty("type", type);
+ if (videoItem.HasArt(type))
+ item->SetArt("thumb", videoItem.GetArt(type));
+ items.Add(item);
+ }
+
+ dialog->SetItems(items);
+ dialog->Open();
+
+ if (dialog->IsButtonPressed())
+ {
+ // Get the new artwork name
+ std::string strArtworkName;
+ if (!CGUIKeyboardFactory::ShowAndGetInput(strArtworkName, CVariant{g_localizeStrings.Get(13516)}, false))
+ return "";
+
+ return strArtworkName;
+ }
+
+ return dialog->GetSelectedFileItem()->GetProperty("type").asString();
+}
+
+void CGUIDialogVideoInfo::OnGetArt()
+{
+ if (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeVideoCollection)
+ {
+ ManageVideoItemArtwork(m_movieItem, m_movieItem->GetVideoInfoTag()->m_type);
+ return;
+ }
+ std::string type = ChooseArtType(*m_movieItem);
+ if (type.empty())
+ return; // cancelled
+
+ //! @todo this can be removed once these are unified.
+ if (type == "fanart")
+ OnGetFanart();
+ else
+ {
+ CFileItemList items;
+
+ // Current thumb
+ if (m_movieItem->HasArt(type))
+ {
+ CFileItemPtr item(new CFileItem("thumb://Current", false));
+ item->SetArt("thumb", m_movieItem->GetArt(type));
+ item->SetArt("icon", "DefaultPicture.png");
+ item->SetLabel(g_localizeStrings.Get(13512));
+ items.Add(item);
+ }
+ else if ((type == "poster" || type == "banner") && m_movieItem->HasArt("thumb"))
+ { // add the 'thumb' type in
+ CFileItemPtr item(new CFileItem("thumb://Thumb", false));
+ item->SetArt("thumb", m_movieItem->GetArt("thumb"));
+ item->SetArt("icon", "DefaultPicture.png");
+ item->SetLabel(g_localizeStrings.Get(13512));
+ items.Add(item);
+ }
+
+ std::string embeddedArt;
+ if (URIUtils::HasExtension(m_movieItem->GetVideoInfoTag()->m_strFileNameAndPath, ".mkv"))
+ {
+ CFileItem item(m_movieItem->GetVideoInfoTag()->m_strFileNameAndPath, false);
+ CVideoTagLoaderFFmpeg loader(item, nullptr, false);
+ CVideoInfoTag tag;
+ loader.Load(tag, false, nullptr);
+ for (const auto& it : tag.m_coverArt)
+ {
+ if (it.m_type == type)
+ {
+ CFileItemPtr itemF(new CFileItem("thumb://Embedded", false));
+ embeddedArt = CTextureUtils::GetWrappedImageURL(item.GetPath(), "video_" + type);
+ itemF->SetArt("thumb", embeddedArt);
+ itemF->SetLabel(g_localizeStrings.Get(13519));
+ items.Add(itemF);
+ }
+ }
+ }
+
+ // Grab the thumbnails from the web
+ m_movieItem->GetVideoInfoTag()->m_strPictureURL.Parse();
+ std::vector<std::string> thumbs;
+ int season = (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeSeason) ? m_movieItem->GetVideoInfoTag()->m_iSeason : -1;
+ m_movieItem->GetVideoInfoTag()->m_strPictureURL.GetThumbUrls(thumbs, type, season);
+
+ for (unsigned int i = 0; i < thumbs.size(); ++i)
+ {
+ std::string strItemPath = StringUtils::Format("thumb://Remote{}", i);
+ CFileItemPtr item(new CFileItem(strItemPath, false));
+ item->SetArt("thumb", thumbs[i]);
+ item->SetArt("icon", "DefaultPicture.png");
+ item->SetLabel(g_localizeStrings.Get(13513));
+
+ //! @todo Do we need to clear the cached image?
+ // CServiceBroker::GetTextureCache()->ClearCachedImage(thumb);
+ items.Add(item);
+ }
+
+ std::string localThumb = CVideoThumbLoader::GetLocalArt(*m_movieItem, type);
+ if (!localThumb.empty())
+ {
+ CFileItemPtr item(new CFileItem("thumb://Local", false));
+ item->SetArt("thumb", localThumb);
+ item->SetArt("icon", "DefaultPicture.png");
+ item->SetLabel(g_localizeStrings.Get(13514));
+ items.Add(item);
+ }
+ else
+ { // no local thumb exists, so we are just using the IMDb thumb or cached thumb
+ // which is probably the IMDb thumb. These could be wrong, so allow the user
+ // to delete the incorrect thumb
+ CFileItemPtr item(new CFileItem("thumb://None", false));
+ item->SetArt("icon", "DefaultPicture.png");
+ item->SetLabel(g_localizeStrings.Get(13515));
+ items.Add(item);
+ }
+
+ std::string result;
+ VECSOURCES sources(*CMediaSourceSettings::GetInstance().GetSources("video"));
+ AddItemPathToFileBrowserSources(sources, *m_movieItem);
+ CServiceBroker::GetMediaManager().GetLocalDrives(sources);
+ if (CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result) &&
+ result != "thumb://Current") // user didn't choose the one they have
+ {
+ std::string newThumb;
+ if (StringUtils::StartsWith(result, "thumb://Remote"))
+ {
+ int number = atoi(result.substr(14).c_str());
+ newThumb = thumbs[number];
+ }
+ else if (result == "thumb://Thumb")
+ newThumb = m_movieItem->GetArt("thumb");
+ else if (result == "thumb://Local")
+ newThumb = localThumb;
+ else if (result == "thumb://Embedded")
+ newThumb = embeddedArt;
+ else if (CFileUtils::Exists(result))
+ newThumb = result;
+ else // none
+ newThumb.clear();
+
+ // update thumb in the database
+ CVideoDatabase db;
+ if (db.Open())
+ {
+ db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, type, newThumb);
+ db.Close();
+ }
+ CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
+ m_movieItem->SetArt(type, newThumb);
+ if (m_movieItem->HasProperty("set_folder_thumb"))
+ { // have a folder thumb to set as well
+ VIDEO::CVideoInfoScanner::ApplyThumbToFolder(m_movieItem->GetProperty("set_folder_thumb").asString(), newThumb);
+ }
+ m_hasUpdatedThumb = true;
+ }
+ }
+
+ // Update our screen
+ Update();
+
+ // re-open the art selection dialog as we come back from
+ // the image selection dialog
+ OnGetArt();
+}
+
+// Allow user to select a Fanart
+void CGUIDialogVideoInfo::OnGetFanart()
+{
+ CFileItemList items;
+
+ // Ensure the fanart is unpacked
+ m_movieItem->GetVideoInfoTag()->m_fanart.Unpack();
+
+ if (m_movieItem->HasArt("fanart"))
+ {
+ CFileItemPtr itemCurrent(new CFileItem("fanart://Current",false));
+ itemCurrent->SetArt("thumb", m_movieItem->GetArt("fanart"));
+ itemCurrent->SetArt("icon", "DefaultPicture.png");
+ itemCurrent->SetLabel(g_localizeStrings.Get(20440));
+ items.Add(itemCurrent);
+ }
+
+ std::string embeddedArt;
+ if (URIUtils::HasExtension(m_movieItem->GetVideoInfoTag()->m_strFileNameAndPath, ".mkv"))
+ {
+ CFileItem item(m_movieItem->GetVideoInfoTag()->m_strFileNameAndPath, false);
+ CVideoTagLoaderFFmpeg loader(item, nullptr, false);
+ CVideoInfoTag tag;
+ loader.Load(tag, false, nullptr);
+ for (const auto& it : tag.m_coverArt)
+ {
+ if (it.m_type == "fanart")
+ {
+ CFileItemPtr itemF(new CFileItem("fanart://Embedded", false));
+ embeddedArt = CTextureUtils::GetWrappedImageURL(item.GetPath(), "video_fanart");
+ itemF->SetArt("thumb", embeddedArt);
+ itemF->SetLabel(g_localizeStrings.Get(13520));
+ items.Add(itemF);
+ }
+ }
+ }
+
+ // Grab the thumbnails from the web
+ for (unsigned int i = 0; i < m_movieItem->GetVideoInfoTag()->m_fanart.GetNumFanarts(); i++)
+ {
+ if (URIUtils::IsProtocol(m_movieItem->GetVideoInfoTag()->m_fanart.GetPreviewURL(i), "image"))
+ continue;
+ std::string strItemPath = StringUtils::Format("fanart://Remote{}", i);
+ CFileItemPtr item(new CFileItem(strItemPath, false));
+ std::string thumb = m_movieItem->GetVideoInfoTag()->m_fanart.GetPreviewURL(i);
+ item->SetArt("thumb", CTextureUtils::GetWrappedThumbURL(thumb));
+ item->SetArt("icon", "DefaultPicture.png");
+ item->SetLabel(g_localizeStrings.Get(20441));
+
+ //! @todo Do we need to clear the cached image?
+ // CServiceBroker::GetTextureCache()->ClearCachedImage(thumb);
+ items.Add(item);
+ }
+
+ CFileItem item(*m_movieItem->GetVideoInfoTag());
+ std::string strLocal = item.GetLocalFanart();
+ if (!strLocal.empty())
+ {
+ CFileItemPtr itemLocal(new CFileItem("fanart://Local",false));
+ itemLocal->SetArt("thumb", strLocal);
+ itemLocal->SetArt("icon", "DefaultPicture.png");
+ itemLocal->SetLabel(g_localizeStrings.Get(20438));
+
+ //! @todo Do we need to clear the cached image?
+ CServiceBroker::GetTextureCache()->ClearCachedImage(strLocal);
+ items.Add(itemLocal);
+ }
+ else
+ {
+ CFileItemPtr itemNone(new CFileItem("fanart://None", false));
+ itemNone->SetArt("icon", "DefaultPicture.png");
+ itemNone->SetLabel(g_localizeStrings.Get(20439));
+ items.Add(itemNone);
+ }
+
+ std::string result;
+ VECSOURCES sources(*CMediaSourceSettings::GetInstance().GetSources("video"));
+ AddItemPathToFileBrowserSources(sources, item);
+ CServiceBroker::GetMediaManager().GetLocalDrives(sources);
+ bool flip=false;
+ if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) ||
+ StringUtils::EqualsNoCase(result, "fanart://Current"))
+ return; // user cancelled
+
+ if (StringUtils::EqualsNoCase(result, "fanart://Local"))
+ result = strLocal;
+
+ if (StringUtils::EqualsNoCase(result, "fanart://Embedded"))
+ {
+ unsigned int current = m_movieItem->GetVideoInfoTag()->m_fanart.GetNumFanarts();
+ int found = -1;
+ for (size_t i = 0; i < current; ++i)
+ if (URIUtils::IsProtocol(m_movieItem->GetVideoInfoTag()->m_fanart.GetImageURL(), "image"))
+ found = i;
+ if (found != -1)
+ {
+ m_movieItem->GetVideoInfoTag()->m_fanart.AddFanart(embeddedArt, "", "");
+ found = current;
+ }
+
+ m_movieItem->GetVideoInfoTag()->m_fanart.SetPrimaryFanart(found);
+
+ CVideoDatabase db;
+ if (db.Open())
+ {
+ db.UpdateFanart(*m_movieItem, m_movieItem->GetVideoContentType());
+ db.Close();
+ }
+ result = embeddedArt;
+ }
+
+ if (StringUtils::StartsWith(result, "fanart://Remote"))
+ {
+ int iFanart = atoi(result.substr(15).c_str());
+ // set new primary fanart, and update our database accordingly
+ m_movieItem->GetVideoInfoTag()->m_fanart.SetPrimaryFanart(iFanart);
+ CVideoDatabase db;
+ if (db.Open())
+ {
+ db.UpdateFanart(*m_movieItem, m_movieItem->GetVideoContentType());
+ db.Close();
+ }
+ result = m_movieItem->GetVideoInfoTag()->m_fanart.GetImageURL();
+ }
+ else if (StringUtils::EqualsNoCase(result, "fanart://None") || !CFileUtils::Exists(result))
+ result.clear();
+
+ // set the fanart image
+ if (flip && !result.empty())
+ result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
+ CVideoDatabase db;
+ if (db.Open())
+ {
+ db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, "fanart", result);
+ db.Close();
+ }
+
+ CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
+ m_movieItem->SetArt("fanart", result);
+ m_hasUpdatedThumb = true;
+
+ // Update our screen
+ Update();
+}
+
+void CGUIDialogVideoInfo::OnSetUserrating() const
+{
+ CGUIDialogSelect *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
+ if (dialog)
+ {
+ dialog->SetHeading(CVariant{ 38023 });
+ dialog->Add(g_localizeStrings.Get(38022));
+ for (int i = 1; i <= 10; i++)
+ dialog->Add(StringUtils::Format("{}: {}", g_localizeStrings.Get(563), i));
+
+ dialog->SetSelected(m_movieItem->GetVideoInfoTag()->m_iUserRating);
+
+ dialog->Open();
+
+ int iItem = dialog->GetSelectedItem();
+ if (iItem < 0)
+ return;
+
+ SetUserrating(iItem);
+ }
+}
+
+void CGUIDialogVideoInfo::PlayTrailer()
+{
+ Close(true);
+ CGUIMessage msg(GUI_MSG_PLAY_TRAILER, 0, 0, 0, 0, m_movieItem);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
+}
+
+void CGUIDialogVideoInfo::SetLabel(int iControl, const std::string &strLabel)
+{
+ if (strLabel.empty())
+ {
+ SET_CONTROL_LABEL(iControl, 416); // "Not available"
+ }
+ else
+ {
+ SET_CONTROL_LABEL(iControl, strLabel);
+ }
+}
+
+std::string CGUIDialogVideoInfo::GetThumbnail() const
+{
+ return m_movieItem->GetArt("thumb");
+}
+
+namespace
+{
+std::string GetItemPathForBrowserSource(const CFileItem& item)
+{
+ if (!item.HasVideoInfoTag())
+ return "";
+
+ std::string itemDir = item.GetVideoInfoTag()->m_basePath;
+ //season
+ if (itemDir.empty())
+ itemDir = item.GetVideoInfoTag()->GetPath();
+
+ CFileItem itemTmp(itemDir, false);
+ if (itemTmp.IsVideo())
+ itemDir = URIUtils::GetParentPath(itemDir);
+
+ return itemDir;
+}
+
+void AddItemPathStringToFileBrowserSources(VECSOURCES& sources,
+ const std::string& itemDir, const std::string& label)
+{
+ if (!itemDir.empty() && CDirectory::Exists(itemDir))
+ {
+ CMediaSource itemSource;
+ itemSource.strName = label;
+ itemSource.strPath = itemDir;
+ sources.push_back(itemSource);
+ }
+}
+} // namespace
+
+void CGUIDialogVideoInfo::AddItemPathToFileBrowserSources(VECSOURCES& sources,
+ const CFileItem& item)
+{
+ std::string itemDir = GetItemPathForBrowserSource(item);
+ AddItemPathStringToFileBrowserSources(sources, itemDir, g_localizeStrings.Get(36041));
+}
+
+int CGUIDialogVideoInfo::ManageVideoItem(const std::shared_ptr<CFileItem>& item)
+{
+ if (item == nullptr || !item->IsVideoDb() || !item->HasVideoInfoTag() || item->GetVideoInfoTag()->m_iDbId < 0)
+ return -1;
+
+ CVideoDatabase database;
+ if (!database.Open())
+ return -1;
+
+ const std::string &type = item->GetVideoInfoTag()->m_type;
+ int dbId = item->GetVideoInfoTag()->m_iDbId;
+
+ CContextButtons buttons;
+ if (type == MediaTypeMovie || type == MediaTypeVideoCollection ||
+ type == MediaTypeTvShow || type == MediaTypeEpisode ||
+ (type == MediaTypeSeason && item->GetVideoInfoTag()->m_iSeason > 0) || // seasons without "all seasons" and "specials"
+ type == MediaTypeMusicVideo)
+ buttons.Add(CONTEXT_BUTTON_EDIT, 16105);
+
+ if (type == MediaTypeMovie || type == MediaTypeTvShow)
+ buttons.Add(CONTEXT_BUTTON_EDIT_SORTTITLE, 16107);
+
+ if (type == MediaTypeMovie)
+ {
+ // only show link/unlink if there are tvshows available
+ if (database.HasContent(VideoDbContentType::TVSHOWS))
+ {
+ buttons.Add(CONTEXT_BUTTON_LINK_MOVIE, 20384);
+ if (database.IsLinkedToTvshow(dbId))
+ buttons.Add(CONTEXT_BUTTON_UNLINK_MOVIE, 20385);
+ }
+
+ // set or change movie set the movie belongs to
+ buttons.Add(CONTEXT_BUTTON_SET_MOVIESET, 20465);
+ }
+
+ if (type == MediaTypeEpisode &&
+ item->GetVideoInfoTag()->m_iBookmarkId > 0)
+ buttons.Add(CONTEXT_BUTTON_UNLINK_BOOKMARK, 20405);
+
+ // movie sets
+ if (item->m_bIsFolder && type == MediaTypeVideoCollection)
+ {
+ buttons.Add(CONTEXT_BUTTON_SET_MOVIESET_ART, 13511);
+ buttons.Add(CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS, 20465);
+ }
+
+ // seasons
+ if (item->m_bIsFolder && type == MediaTypeSeason)
+ buttons.Add(CONTEXT_BUTTON_SET_SEASON_ART, 13511);
+
+ // tags
+ if (item->m_bIsFolder && type == "tag")
+ {
+ CVideoDbUrl videoUrl;
+ if (videoUrl.FromString(item->GetPath()))
+ {
+ const std::string &mediaType = videoUrl.GetItemType();
+
+ buttons.Add(
+ CONTEXT_BUTTON_TAGS_ADD_ITEMS,
+ StringUtils::Format(g_localizeStrings.Get(20460), GetLocalizedVideoType(mediaType)));
+ buttons.Add(CONTEXT_BUTTON_TAGS_REMOVE_ITEMS, StringUtils::Format(g_localizeStrings.Get(20461).c_str(), GetLocalizedVideoType(mediaType).c_str()));
+ }
+ }
+
+ if (type != MediaTypeSeason)
+ buttons.Add(CONTEXT_BUTTON_DELETE, 646);
+
+ //temporary workaround until the context menu ids are removed
+ const int addonItemOffset = 10000;
+
+ auto addonItems = CServiceBroker::GetContextMenuManager().GetAddonItems(*item, CContextMenuManager::MANAGE);
+ for (size_t i = 0; i < addonItems.size(); ++i)
+ buttons.Add(addonItemOffset + i, addonItems[i]->GetLabel(*item));
+
+ bool result = false;
+ int button = CGUIDialogContextMenu::ShowAndGetChoice(buttons);
+ if (button >= 0)
+ {
+ switch (static_cast<CONTEXT_BUTTON>(button))
+ {
+ case CONTEXT_BUTTON_EDIT:
+ result = UpdateVideoItemTitle(item);
+ break;
+
+ case CONTEXT_BUTTON_EDIT_SORTTITLE:
+ result = UpdateVideoItemSortTitle(item);
+ break;
+
+ case CONTEXT_BUTTON_LINK_MOVIE:
+ result = LinkMovieToTvShow(item, false, database);
+ break;
+
+ case CONTEXT_BUTTON_UNLINK_MOVIE:
+ result = LinkMovieToTvShow(item, true, database);
+ break;
+
+ case CONTEXT_BUTTON_SET_MOVIESET:
+ {
+ CFileItemPtr selectedSet;
+ if (GetSetForMovie(item.get(), selectedSet))
+ result = SetMovieSet(item.get(), selectedSet.get());
+ break;
+ }
+
+ case CONTEXT_BUTTON_UNLINK_BOOKMARK:
+ database.DeleteBookMarkForEpisode(*item->GetVideoInfoTag());
+ result = true;
+ break;
+
+ case CONTEXT_BUTTON_DELETE:
+ result = DeleteVideoItem(item);
+ break;
+
+ case CONTEXT_BUTTON_SET_MOVIESET_ART:
+ case CONTEXT_BUTTON_SET_SEASON_ART:
+ result = ManageVideoItemArtwork(item, type);
+ break;
+
+ case CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS:
+ result = ManageMovieSets(item);
+ break;
+
+ case CONTEXT_BUTTON_TAGS_ADD_ITEMS:
+ result = AddItemsToTag(item);
+ break;
+
+ case CONTEXT_BUTTON_TAGS_REMOVE_ITEMS:
+ result = RemoveItemsFromTag(item);
+ break;
+
+ default:
+ if (button >= addonItemOffset)
+ result = CONTEXTMENU::LoopFrom(*addonItems[button - addonItemOffset], item);
+ break;
+ }
+ }
+
+ database.Close();
+
+ if (result)
+ return button;
+
+ return -1;
+}
+
+//Add change a title's name
+bool CGUIDialogVideoInfo::UpdateVideoItemTitle(const std::shared_ptr<CFileItem>& pItem)
+{
+ if (pItem == nullptr || !pItem->HasVideoInfoTag())
+ return false;
+
+ // dont allow update while scanning
+ if (CVideoLibraryQueue::GetInstance().IsScanningLibrary())
+ {
+ HELPERS::ShowOKDialogText(CVariant{257}, CVariant{14057});
+ return false;
+ }
+
+ CVideoDatabase database;
+ if (!database.Open())
+ return false;
+
+ int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
+ MediaType mediaType = pItem->GetVideoInfoTag()->m_type;
+
+ CVideoInfoTag detail;
+ std::string title;
+ if (mediaType == MediaTypeMovie)
+ {
+ database.GetMovieInfo("", detail, iDbId, VideoDbDetailsNone);
+ title = detail.m_strTitle;
+ }
+ else if (mediaType == MediaTypeVideoCollection)
+ {
+ database.GetSetInfo(iDbId, detail);
+ title = detail.m_strTitle;
+ }
+ else if (mediaType == MediaTypeEpisode)
+ {
+ database.GetEpisodeInfo(pItem->GetPath(), detail, iDbId, VideoDbDetailsNone);
+ title = detail.m_strTitle;
+ }
+ else if (mediaType == MediaTypeSeason)
+ {
+ database.GetSeasonInfo(iDbId, detail);
+ title = detail.m_strSortTitle.empty() ? detail.m_strTitle : detail.m_strSortTitle;
+ }
+ else if (mediaType == MediaTypeTvShow)
+ {
+ database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId, 0, VideoDbDetailsNone);
+ title = detail.m_strTitle;
+ }
+ else if (mediaType == MediaTypeMusicVideo)
+ {
+ database.GetMusicVideoInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId, VideoDbDetailsNone);
+ title = detail.m_strTitle;
+ }
+
+ // get the new title
+ if (!CGUIKeyboardFactory::ShowAndGetInput(title, CVariant{ g_localizeStrings.Get(16105) }, false))
+ return false;
+
+ if (mediaType == MediaTypeSeason)
+ {
+ detail.m_strSortTitle = title;
+ std::map<std::string, std::string> artwork;
+ database.SetDetailsForSeason(detail, artwork, detail.m_iIdShow, detail.m_iDbId);
+ }
+ else
+ {
+ detail.m_strTitle = title;
+ VideoDbContentType iType = pItem->GetVideoContentType();
+ database.UpdateMovieTitle(iDbId, detail.m_strTitle, iType);
+ }
+
+ return true;
+}
+
+bool CGUIDialogVideoInfo::CanDeleteVideoItem(const std::shared_ptr<CFileItem>& item)
+{
+ if (item == nullptr || !item->HasVideoInfoTag())
+ return false;
+
+ if (item->GetVideoInfoTag()->m_type == "tag")
+ return true;
+
+ CQueryParams params;
+ CVideoDatabaseDirectory::GetQueryParams(item->GetPath(), params);
+
+ return params.GetMovieId() != -1 ||
+ params.GetEpisodeId() != -1 ||
+ params.GetMVideoId() != -1 ||
+ params.GetSetId() != -1 ||
+ (params.GetTvShowId() != -1 && params.GetSeason() <= -1 &&
+ !CVideoDatabaseDirectory::IsAllItem(item->GetPath()));
+}
+
+bool CGUIDialogVideoInfo::DeleteVideoItemFromDatabase(const std::shared_ptr<CFileItem>& item,
+ bool unavailable /* = false */)
+{
+ if (item == nullptr || !item->HasVideoInfoTag() ||
+ !CanDeleteVideoItem(item))
+ return false;
+
+ // dont allow update while scanning
+ if (CVideoLibraryQueue::GetInstance().IsScanningLibrary())
+ {
+ HELPERS::ShowOKDialogText(CVariant{257}, CVariant{14057});
+ return false;
+ }
+
+ CGUIDialogYesNo* pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogYesNo>(WINDOW_DIALOG_YES_NO);
+ if (pDialog == nullptr)
+ return false;
+
+ int heading = -1;
+ VideoDbContentType type = item->GetVideoContentType();
+ const std::string& subtype = item->GetVideoInfoTag()->m_type;
+ if (subtype != "tag")
+ {
+ switch (type)
+ {
+ case VideoDbContentType::MOVIES:
+ heading = 432;
+ break;
+ case VideoDbContentType::EPISODES:
+ heading = 20362;
+ break;
+ case VideoDbContentType::TVSHOWS:
+ heading = 20363;
+ break;
+ case VideoDbContentType::MUSICVIDEOS:
+ heading = 20392;
+ break;
+ case VideoDbContentType::MOVIE_SETS:
+ heading = 646;
+ break;
+ default:
+ return false;
+ }
+ }
+ else
+ {
+ heading = 10058;
+ }
+
+ pDialog->SetHeading(CVariant{heading});
+
+ if (unavailable)
+ {
+ pDialog->SetLine(0, CVariant{g_localizeStrings.Get(662)});
+ pDialog->SetLine(1, CVariant{g_localizeStrings.Get(663)});
+ }
+ else
+ {
+ pDialog->SetLine(0,
+ CVariant{StringUtils::Format(g_localizeStrings.Get(433), item->GetLabel())});
+ pDialog->SetLine(1, CVariant{""});
+ }
+ pDialog->SetLine(2, CVariant{""});
+ pDialog->Open();
+
+ if (!pDialog->IsConfirmed())
+ return false;
+
+ CVideoDatabase database;
+ database.Open();
+
+ if (item->GetVideoInfoTag()->m_iDbId < 0)
+ return false;
+
+ if (subtype == "tag")
+ {
+ database.DeleteTag(item->GetVideoInfoTag()->m_iDbId, type);
+ return true;
+ }
+
+ switch (type)
+ {
+ case VideoDbContentType::MOVIES:
+ database.DeleteMovie(item->GetVideoInfoTag()->m_iDbId);
+ break;
+ case VideoDbContentType::EPISODES:
+ database.DeleteEpisode(item->GetVideoInfoTag()->m_iDbId);
+ break;
+ case VideoDbContentType::TVSHOWS:
+ database.DeleteTvShow(item->GetVideoInfoTag()->m_iDbId);
+ break;
+ case VideoDbContentType::MUSICVIDEOS:
+ database.DeleteMusicVideo(item->GetVideoInfoTag()->m_iDbId);
+ break;
+ case VideoDbContentType::MOVIE_SETS:
+ database.DeleteSet(item->GetVideoInfoTag()->m_iDbId);
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool CGUIDialogVideoInfo::DeleteVideoItem(const std::shared_ptr<CFileItem>& item,
+ bool unavailable /* = false */)
+{
+ if (item == nullptr)
+ return false;
+
+ // delete the video item from the database
+ if (!DeleteVideoItemFromDatabase(item, unavailable))
+ return false;
+
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ // check if the user is allowed to delete the actual file as well
+ if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_ALLOWFILEDELETION) &&
+ (profileManager->GetCurrentProfile().getLockMode() == LOCK_MODE_EVERYONE ||
+ !profileManager->GetCurrentProfile().filesLocked() ||
+ g_passwordManager.IsMasterLockUnlocked(true)))
+ {
+ std::string strDeletePath = item->GetVideoInfoTag()->GetPath();
+
+ if (StringUtils::EqualsNoCase(URIUtils::GetFileName(strDeletePath), "VIDEO_TS.IFO"))
+ {
+ strDeletePath = URIUtils::GetDirectory(strDeletePath);
+ if (StringUtils::EndsWithNoCase(strDeletePath, "video_ts/"))
+ {
+ URIUtils::RemoveSlashAtEnd(strDeletePath);
+ strDeletePath = URIUtils::GetDirectory(strDeletePath);
+ }
+ }
+ if (URIUtils::HasSlashAtEnd(strDeletePath))
+ item->m_bIsFolder = true;
+
+ // check if the file/directory can be deleted
+ if (CUtil::SupportsWriteFileOperations(strDeletePath))
+ {
+ item->SetPath(strDeletePath);
+
+ // HACK: stacked files need to be treated as folders in order to be deleted
+ if (item->IsStack())
+ item->m_bIsFolder = true;
+
+ CGUIComponent *gui = CServiceBroker::GetGUI();
+ if (gui && gui->ConfirmDelete(item->GetPath()))
+ CFileUtils::DeleteItem(item);
+ }
+ }
+
+ CUtil::DeleteVideoDatabaseDirectoryCache();
+
+ return true;
+}
+
+bool CGUIDialogVideoInfo::ManageMovieSets(const std::shared_ptr<CFileItem>& item)
+{
+ if (item == nullptr)
+ return false;
+
+ CFileItemList originalItems;
+ CFileItemList selectedItems;
+
+ if (!GetMoviesForSet(item.get(), originalItems, selectedItems) ||
+ selectedItems.Size() == 0) // need at least one item selected
+ return false;
+
+ VECFILEITEMS original = originalItems.GetList();
+ std::sort(original.begin(), original.end(), compFileItemsByDbId);
+ VECFILEITEMS selected = selectedItems.GetList();
+ std::sort(selected.begin(), selected.end(), compFileItemsByDbId);
+
+ bool refreshNeeded = false;
+ // update the "added" items
+ VECFILEITEMS addedItems;
+ set_difference(selected.begin(),selected.end(), original.begin(),original.end(), std::back_inserter(addedItems), compFileItemsByDbId);
+ for (VECFILEITEMS::const_iterator it = addedItems.begin(); it != addedItems.end(); ++it)
+ {
+ if (SetMovieSet(it->get(), item.get()))
+ refreshNeeded = true;
+ }
+
+ // update the "deleted" items
+ CFileItemPtr clearItem(new CFileItem());
+ clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
+ VECFILEITEMS deletedItems;
+ set_difference(original.begin(),original.end(), selected.begin(),selected.end(), std::back_inserter(deletedItems), compFileItemsByDbId);
+ for (VECFILEITEMS::iterator it = deletedItems.begin(); it != deletedItems.end(); ++it)
+ {
+ if (SetMovieSet(it->get(), clearItem.get()))
+ refreshNeeded = true;
+ }
+
+ return refreshNeeded;
+}
+
+bool CGUIDialogVideoInfo::GetMoviesForSet(const CFileItem *setItem, CFileItemList &originalMovies, CFileItemList &selectedMovies)
+{
+ if (setItem == nullptr || !setItem->HasVideoInfoTag())
+ return false;
+
+ CVideoDatabase videodb;
+ if (!videodb.Open())
+ return false;
+
+ std::string baseDir =
+ StringUtils::Format("videodb://movies/sets/{}", setItem->GetVideoInfoTag()->m_iDbId);
+
+ if (!CDirectory::GetDirectory(baseDir, originalMovies, "", DIR_FLAG_DEFAULTS) ||
+ originalMovies.Size() <= 0) // keep a copy of the original members of the set
+ return false;
+
+ CFileItemList listItems;
+ if (!videodb.GetSortedVideos(MediaTypeMovie, "videodb://movies", SortDescription(), listItems) || listItems.Size() <= 0)
+ return false;
+
+ CGUIDialogSelect *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
+ if (dialog == nullptr)
+ return false;
+
+ listItems.Sort(SortByLabel, SortOrderAscending, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING) ? SortAttributeIgnoreArticle : SortAttributeNone);
+
+ dialog->Reset();
+ dialog->SetMultiSelection(true);
+ dialog->SetHeading(CVariant{g_localizeStrings.Get(20457)});
+ dialog->SetItems(listItems);
+ std::vector<int> selectedIndices;
+ for (int i = 0; i < originalMovies.Size(); i++)
+ {
+ for (int listIndex = 0; listIndex < listItems.Size(); listIndex++)
+ {
+ if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == originalMovies[i]->GetVideoInfoTag()->m_iDbId)
+ {
+ selectedIndices.push_back(listIndex);
+ break;
+ }
+ }
+ }
+ dialog->SetSelected(selectedIndices);
+ dialog->EnableButton(true, 186);
+ dialog->Open();
+
+ if (dialog->IsConfirmed())
+ {
+ for (int i : dialog->GetSelectedItems())
+ selectedMovies.Add(listItems.Get(i));
+ return (selectedMovies.Size() > 0);
+ }
+ else
+ return false;
+}
+
+bool CGUIDialogVideoInfo::GetSetForMovie(const CFileItem* movieItem,
+ std::shared_ptr<CFileItem>& selectedSet)
+{
+ if (movieItem == nullptr || !movieItem->HasVideoInfoTag())
+ return false;
+
+ CVideoDatabase videodb;
+ if (!videodb.Open())
+ return false;
+
+ CFileItemList listItems;
+
+ // " ignoreSingleMovieSets=false " as an option in the url is needed here
+ // to override the gui-setting "Include sets containing a single movie"
+ // and retrieve all moviesets
+
+ std::string baseDir = "videodb://movies/sets/?ignoreSingleMovieSets=false";
+
+ if (!CDirectory::GetDirectory(baseDir, listItems, "", DIR_FLAG_DEFAULTS))
+ return false;
+ listItems.Sort(SortByLabel, SortOrderAscending, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING) ? SortAttributeIgnoreArticle : SortAttributeNone);
+
+ int currentSetId = 0;
+ std::string currentSetLabel;
+
+ if (movieItem->GetVideoInfoTag()->m_set.id > currentSetId)
+ {
+ currentSetId = movieItem->GetVideoInfoTag()->m_set.id;
+ currentSetLabel = videodb.GetSetById(currentSetId);
+ }
+
+ if (currentSetId > 0)
+ {
+ // remove duplicate entry
+ for (int listIndex = 0; listIndex < listItems.Size(); listIndex++)
+ {
+ if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == currentSetId)
+ {
+ listItems.Remove(listIndex);
+ break;
+ }
+ }
+ // add clear item
+ std::string strClear = StringUtils::Format(g_localizeStrings.Get(20467), currentSetLabel);
+ CFileItemPtr clearItem(new CFileItem(strClear));
+ clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
+ listItems.AddFront(clearItem, 0);
+ // add keep current set item
+ std::string strKeep = StringUtils::Format(g_localizeStrings.Get(20469), currentSetLabel);
+ CFileItemPtr keepItem(new CFileItem(strKeep));
+ keepItem->GetVideoInfoTag()->m_iDbId = currentSetId;
+ listItems.AddFront(keepItem, 1);
+ }
+
+ CGUIDialogSelect *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
+ if (dialog == nullptr)
+ return false;
+
+ dialog->Reset();
+ dialog->SetHeading(CVariant{g_localizeStrings.Get(20466)});
+ dialog->SetItems(listItems);
+ if (currentSetId >= 0)
+ {
+ for (int listIndex = 0; listIndex < listItems.Size(); listIndex++)
+ {
+ if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == currentSetId)
+ {
+ dialog->SetSelected(listIndex);
+ break;
+ }
+ }
+ }
+ dialog->EnableButton(true, 20468); // new set via button
+ dialog->Open();
+
+ if (dialog->IsButtonPressed())
+ { // creating new set
+ std::string newSetTitle;
+ if (!CGUIKeyboardFactory::ShowAndGetInput(newSetTitle, CVariant{g_localizeStrings.Get(20468)}, false))
+ return false;
+ int idSet = videodb.AddSet(newSetTitle);
+ std::map<std::string, std::string> movieArt, setArt;
+ if (!videodb.GetArtForItem(idSet, MediaTypeVideoCollection, setArt))
+ {
+ videodb.GetArtForItem(movieItem->GetVideoInfoTag()->m_iDbId, MediaTypeMovie, movieArt);
+ videodb.SetArtForItem(idSet, MediaTypeVideoCollection, movieArt);
+ }
+ CFileItemPtr newSet(new CFileItem(newSetTitle));
+ newSet->GetVideoInfoTag()->m_iDbId = idSet;
+ selectedSet = newSet;
+ return true;
+ }
+ else if (dialog->IsConfirmed())
+ {
+ selectedSet = dialog->GetSelectedFileItem();
+ return (selectedSet != nullptr);
+ }
+ else
+ return false;
+}
+
+bool CGUIDialogVideoInfo::SetMovieSet(const CFileItem *movieItem, const CFileItem *selectedSet)
+{
+ if (movieItem == nullptr || !movieItem->HasVideoInfoTag() ||
+ selectedSet == nullptr || !selectedSet->HasVideoInfoTag())
+ return false;
+
+ CVideoDatabase videodb;
+ if (!videodb.Open())
+ return false;
+
+ videodb.SetMovieSet(movieItem->GetVideoInfoTag()->m_iDbId, selectedSet->GetVideoInfoTag()->m_iDbId);
+ return true;
+}
+
+bool CGUIDialogVideoInfo::GetItemsForTag(const std::string &strHeading, const std::string &type, CFileItemList &items, int idTag /* = -1 */, bool showAll /* = true */)
+{
+ CVideoDatabase videodb;
+ if (!videodb.Open())
+ return false;
+
+ MediaType mediaType = MediaTypeNone;
+ std::string baseDir = "videodb://";
+ std::string idColumn;
+ if (type.compare(MediaTypeMovie) == 0)
+ {
+ mediaType = MediaTypeMovie;
+ baseDir += "movies";
+ idColumn = "idMovie";
+ }
+ else if (type.compare(MediaTypeTvShow) == 0)
+ {
+ mediaType = MediaTypeTvShow;
+ baseDir += "tvshows";
+ idColumn = "idShow";
+ }
+ else if (type.compare(MediaTypeMusicVideo) == 0)
+ {
+ mediaType = MediaTypeMusicVideo;
+ baseDir += "musicvideos";
+ idColumn = "idMVideo";
+ }
+
+ baseDir += "/titles/";
+ CVideoDbUrl videoUrl;
+ if (!videoUrl.FromString(baseDir))
+ return false;
+
+ CVideoDatabase::Filter filter;
+ if (idTag > 0)
+ {
+ if (!showAll)
+ videoUrl.AddOption("tagid", idTag);
+ else
+ filter.where = videodb.PrepareSQL("%s_view.%s NOT IN (SELECT tag_link.media_id FROM tag_link WHERE tag_link.tag_id = %d AND tag_link.media_type = '%s')", type.c_str(), idColumn.c_str(), idTag, type.c_str());
+ }
+
+ CFileItemList listItems;
+ if (!videodb.GetSortedVideos(mediaType, videoUrl.ToString(), SortDescription(), listItems, filter) || listItems.Size() <= 0)
+ return false;
+
+ CGUIDialogSelect *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
+ if (dialog == nullptr)
+ return false;
+
+ listItems.Sort(SortByLabel, SortOrderAscending, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING) ? SortAttributeIgnoreArticle : SortAttributeNone);
+
+ dialog->Reset();
+ dialog->SetMultiSelection(true);
+ dialog->SetHeading(CVariant{strHeading});
+ dialog->SetItems(listItems);
+ dialog->EnableButton(true, 186);
+ dialog->Open();
+
+ for (int i : dialog->GetSelectedItems())
+ items.Add(listItems.Get(i));
+ return items.Size() > 0;
+}
+
+bool CGUIDialogVideoInfo::AddItemsToTag(const std::shared_ptr<CFileItem>& tagItem)
+{
+ if (tagItem == nullptr || !tagItem->HasVideoInfoTag())
+ return false;
+
+ CVideoDbUrl videoUrl;
+ if (!videoUrl.FromString(tagItem->GetPath()))
+ return false;
+
+ CVideoDatabase videodb;
+ if (!videodb.Open())
+ return true;
+
+ std::string mediaType = videoUrl.GetItemType();
+ mediaType = mediaType.substr(0, mediaType.length() - 1);
+
+ CFileItemList items;
+ std::string localizedType = GetLocalizedVideoType(mediaType);
+ std::string strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType);
+ if (!GetItemsForTag(strLabel, mediaType, items, tagItem->GetVideoInfoTag()->m_iDbId))
+ return true;
+
+ for (int index = 0; index < items.Size(); index++)
+ {
+ if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
+ continue;
+
+ videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, tagItem->GetVideoInfoTag()->m_iDbId, mediaType);
+ }
+
+ return true;
+}
+
+bool CGUIDialogVideoInfo::RemoveItemsFromTag(const std::shared_ptr<CFileItem>& tagItem)
+{
+ if (tagItem == nullptr || !tagItem->HasVideoInfoTag())
+ return false;
+
+ CVideoDbUrl videoUrl;
+ if (!videoUrl.FromString(tagItem->GetPath()))
+ return false;
+
+ CVideoDatabase videodb;
+ if (!videodb.Open())
+ return true;
+
+ std::string mediaType = videoUrl.GetItemType();
+ mediaType = mediaType.substr(0, mediaType.length() - 1);
+
+ CFileItemList items;
+ std::string localizedType = GetLocalizedVideoType(mediaType);
+ std::string strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType);
+ if (!GetItemsForTag(strLabel, mediaType, items, tagItem->GetVideoInfoTag()->m_iDbId, false))
+ return true;
+
+ for (int index = 0; index < items.Size(); index++)
+ {
+ if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
+ continue;
+
+ videodb.RemoveTagFromItem(items[index]->GetVideoInfoTag()->m_iDbId, tagItem->GetVideoInfoTag()->m_iDbId, mediaType);
+ }
+
+ return true;
+}
+
+namespace
+{
+std::string FindLocalMovieSetArtworkFile(const CFileItemPtr& item, const std::string& artType)
+{
+ std::string infoFolder = VIDEO::CVideoInfoScanner::GetMovieSetInfoFolder(item->GetLabel());
+ if (infoFolder.empty())
+ return "";
+
+ CFileItemList availableArtFiles;
+ CDirectory::GetDirectory(infoFolder, availableArtFiles,
+ CServiceBroker::GetFileExtensionProvider().GetPictureExtensions(),
+ DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_READ_CACHE | DIR_FLAG_NO_FILE_INFO);
+ for (const auto& artFile : availableArtFiles)
+ {
+ std::string candidate = URIUtils::GetFileName(artFile->GetPath());
+ URIUtils::RemoveExtension(candidate);
+ if (StringUtils::EqualsNoCase(candidate, artType))
+ return artFile->GetPath();
+ }
+ return "";
+}
+} // namespace
+
+bool CGUIDialogVideoInfo::ManageVideoItemArtwork(const std::shared_ptr<CFileItem>& item,
+ const MediaType& type)
+{
+ if (item == nullptr || !item->HasVideoInfoTag() || type.empty())
+ return false;
+
+ CVideoDatabase videodb;
+ if (!videodb.Open())
+ return true;
+
+ // Grab the thumbnails from the web
+ CFileItemList items;
+ CFileItemPtr noneitem(new CFileItem("thumb://None", false));
+ std::string currentThumb;
+ int idArtist = -1;
+ std::string artistPath;
+ std::string artistOldPath;
+ std::string artType = "thumb";
+ if (type == MediaTypeArtist)
+ {
+ CMusicDatabase musicdb;
+ if (musicdb.Open())
+ {
+ idArtist = musicdb.GetArtistByName(item->GetLabel()); // Fails when name not unique
+ if (idArtist >= 0 )
+ {
+ // Get artist paths - possible locations for thumb - while music db open
+ musicdb.GetOldArtistPath(idArtist, artistOldPath); // Old artist path, local to music files
+ CArtist artist;
+ musicdb.GetArtist(idArtist, artist); // Need name and mbid for artist folder name
+ musicdb.GetArtistPath(artist, artistPath); // Artist path in artist info folder
+
+ currentThumb = musicdb.GetArtForItem(idArtist, MediaTypeArtist, "thumb");
+ if (currentThumb.empty())
+ currentThumb = videodb.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType);
+ }
+ }
+ }
+ else if (type == "actor")
+ currentThumb = videodb.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType);
+ else
+ { // SEASON, SET
+ artType = ChooseArtType(*item);
+ if (artType.empty())
+ return false;
+
+ if (artType == "fanart" && type != MediaTypeVideoCollection)
+ return OnGetFanart(item);
+
+ if (item->HasArt(artType))
+ currentThumb = item->GetArt(artType);
+ else if ((artType == "poster" || artType == "banner") && item->HasArt("thumb"))
+ currentThumb = item->GetArt("thumb");
+ }
+
+ if (!currentThumb.empty())
+ {
+ CFileItemPtr item(new CFileItem("thumb://Current", false));
+ item->SetArt("thumb", currentThumb);
+ item->SetLabel(g_localizeStrings.Get(13512));
+ items.Add(item);
+ }
+ noneitem->SetArt("icon", "DefaultFolder.png");
+ noneitem->SetLabel(g_localizeStrings.Get(13515));
+
+ bool local = false;
+ std::vector<std::string> thumbs;
+ if (type != MediaTypeArtist)
+ {
+ CVideoInfoTag tag;
+ if (type == MediaTypeSeason)
+ {
+ videodb.GetTvShowInfo("", tag, item->GetVideoInfoTag()->m_iIdShow);
+ tag.m_strPictureURL.Parse();
+ tag.m_strPictureURL.GetThumbUrls(thumbs, artType, item->GetVideoInfoTag()->m_iSeason);
+ }
+ else if (type == MediaTypeVideoCollection)
+ {
+ CFileItemList items;
+ std::string baseDir =
+ StringUtils::Format("videodb://movies/sets/{}", item->GetVideoInfoTag()->m_iDbId);
+ if (videodb.GetMoviesNav(baseDir, items))
+ {
+ for (int i=0; i < items.Size(); i++)
+ {
+ CVideoInfoTag* pTag = items[i]->GetVideoInfoTag();
+ pTag->m_strPictureURL.Parse();
+ pTag->m_strPictureURL.GetThumbUrls(thumbs, "set." + artType, -1, true);
+ }
+ }
+ }
+ else
+ {
+ tag = *item->GetVideoInfoTag();
+ tag.m_strPictureURL.Parse();
+ tag.m_strPictureURL.GetThumbUrls(thumbs, artType);
+ }
+
+ for (size_t i = 0; i < thumbs.size(); i++)
+ {
+ CFileItemPtr item(new CFileItem(StringUtils::Format("thumb://Remote{0}", i), false));
+ item->SetArt("thumb", thumbs[i]);
+ item->SetArt("icon", "DefaultPicture.png");
+ item->SetLabel(g_localizeStrings.Get(13513));
+ items.Add(item);
+
+ //! @todo Do we need to clear the cached image?
+ // CServiceBroker::GetTextureCache()->ClearCachedImage(thumbs[i]);
+ }
+
+ if (type == "actor")
+ {
+ std::string picturePath;
+ std::string strThumb = URIUtils::AddFileToFolder(picturePath, "folder.jpg");
+ if (CFileUtils::Exists(strThumb))
+ {
+ CFileItemPtr pItem(new CFileItem(strThumb,false));
+ pItem->SetLabel(g_localizeStrings.Get(13514));
+ pItem->SetArt("thumb", strThumb);
+ items.Add(pItem);
+ local = true;
+ }
+ else
+ noneitem->SetArt("icon", "DefaultActor.png");
+ }
+
+ if (type == MediaTypeVideoCollection)
+ {
+ std::string localFile = FindLocalMovieSetArtworkFile(item, artType);
+ if (!localFile.empty())
+ {
+ CFileItemPtr pItem(new CFileItem(localFile, false));
+ pItem->SetLabel(g_localizeStrings.Get(13514));
+ pItem->SetArt("thumb", localFile);
+ items.Add(pItem);
+ local = true;
+ }
+ else
+ noneitem->SetArt("icon", "DefaultVideo.png");
+ }
+ }
+ else
+ {
+ std::string strThumb;
+ bool existsThumb = false;
+ // First look for artist thumb in the primary location
+ if (!artistPath.empty())
+ {
+ strThumb = URIUtils::AddFileToFolder(artistPath, "folder.jpg");
+ existsThumb = CFileUtils::Exists(strThumb);
+ }
+ // If not there fall back local to music files (historic location for those album artists with a unique folder)
+ if (!existsThumb && !artistOldPath.empty())
+ {
+ strThumb = URIUtils::AddFileToFolder(artistOldPath, "folder.jpg");
+ existsThumb = CFileUtils::Exists(strThumb);
+ }
+
+ if (existsThumb)
+ {
+ CFileItemPtr pItem(new CFileItem(strThumb, false));
+ pItem->SetLabel(g_localizeStrings.Get(13514));
+ pItem->SetArt("thumb", strThumb);
+ items.Add(pItem);
+ local = true;
+ }
+ else
+ noneitem->SetArt("icon", "DefaultArtist.png");
+ }
+
+ if (!local)
+ items.Add(noneitem);
+
+ std::string result;
+ VECSOURCES sources=*CMediaSourceSettings::GetInstance().GetSources("video");
+ CServiceBroker::GetMediaManager().GetLocalDrives(sources);
+ if (type == MediaTypeVideoCollection)
+ {
+ AddItemPathStringToFileBrowserSources(sources,
+ VIDEO::CVideoInfoScanner::GetMovieSetInfoFolder(item->GetLabel()),
+ g_localizeStrings.Get(36041));
+ AddItemPathStringToFileBrowserSources(sources,
+ CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
+ CSettings::SETTING_VIDEOLIBRARY_MOVIESETSFOLDER),
+ "* " + g_localizeStrings.Get(20226));
+ }
+ else
+ AddItemPathToFileBrowserSources(sources, *item);
+ if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result))
+ return false; // user cancelled
+
+ if (result == "thumb://Current")
+ result = currentThumb; // user chose the one they have
+
+ // delete the thumbnail if that's what the user wants, else overwrite with the
+ // new thumbnail
+ if (result == "thumb://None")
+ result.clear();
+ else if (StringUtils::StartsWith(result, "thumb://Remote"))
+ {
+ int number = atoi(StringUtils::Mid(result, 14).c_str());
+ result = thumbs[number];
+ }
+
+ // write the selected artwork to the database
+ if (type == MediaTypeVideoCollection ||
+ type == "actor" ||
+ type == MediaTypeSeason ||
+ (type == MediaTypeArtist && idArtist < 0))
+ videodb.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType, result);
+ else
+ {
+ CMusicDatabase musicdb;
+ if (musicdb.Open())
+ musicdb.SetArtForItem(idArtist, MediaTypeArtist, artType, result);
+ }
+
+ item->SetArt(artType, result);
+
+ CUtil::DeleteVideoDatabaseDirectoryCache();
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+
+ return true;
+}
+
+std::string CGUIDialogVideoInfo::GetLocalizedVideoType(const std::string &strType)
+{
+ if (CMediaTypes::IsMediaType(strType, MediaTypeMovie))
+ return g_localizeStrings.Get(20342);
+ else if (CMediaTypes::IsMediaType(strType, MediaTypeTvShow))
+ return g_localizeStrings.Get(20343);
+ else if (CMediaTypes::IsMediaType(strType, MediaTypeEpisode))
+ return g_localizeStrings.Get(20359);
+ else if (CMediaTypes::IsMediaType(strType, MediaTypeMusicVideo))
+ return g_localizeStrings.Get(20391);
+
+ return "";
+}
+
+bool CGUIDialogVideoInfo::UpdateVideoItemSortTitle(const std::shared_ptr<CFileItem>& pItem)
+{
+ // dont allow update while scanning
+ if (CVideoLibraryQueue::GetInstance().IsScanningLibrary())
+ {
+ HELPERS::ShowOKDialogText(CVariant{257}, CVariant{14057});
+ return false;
+ }
+
+ CVideoDatabase database;
+ if (!database.Open())
+ return false;
+
+ int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
+ CVideoInfoTag detail;
+ VideoDbContentType iType = pItem->GetVideoContentType();
+ if (iType == VideoDbContentType::MOVIES)
+ database.GetMovieInfo("", detail, iDbId, VideoDbDetailsNone);
+ else if (iType == VideoDbContentType::TVSHOWS)
+ database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId, 0, VideoDbDetailsNone);
+
+ std::string currentTitle;
+ if (detail.m_strSortTitle.empty())
+ currentTitle = detail.m_strTitle;
+ else
+ currentTitle = detail.m_strSortTitle;
+
+ // get the new sort title
+ if (!CGUIKeyboardFactory::ShowAndGetInput(currentTitle, CVariant{g_localizeStrings.Get(16107)}, false))
+ return false;
+
+ return database.UpdateVideoSortTitle(iDbId, currentTitle, iType);
+}
+
+bool CGUIDialogVideoInfo::LinkMovieToTvShow(const std::shared_ptr<CFileItem>& item,
+ bool bRemove,
+ CVideoDatabase& database)
+{
+ int dbId = item->GetVideoInfoTag()->m_iDbId;
+
+ CFileItemList list;
+ if (bRemove)
+ {
+ std::vector<int> ids;
+ if (!database.GetLinksToTvShow(dbId, ids))
+ return false;
+
+ for (unsigned int i = 0; i < ids.size(); ++i)
+ {
+ CVideoInfoTag tag;
+ database.GetTvShowInfo("", tag, ids[i], 0 , VideoDbDetailsNone);
+ CFileItemPtr show(new CFileItem(tag));
+ list.Add(show);
+ }
+ }
+ else
+ {
+ database.GetTvShowsNav("videodb://tvshows/titles", list);
+
+ // remove already linked shows
+ std::vector<int> ids;
+ if (!database.GetLinksToTvShow(dbId, ids))
+ return false;
+
+ for (int i = 0; i < list.Size(); )
+ {
+ size_t j;
+ for (j = 0; j < ids.size(); ++j)
+ {
+ if (list[i]->GetVideoInfoTag()->m_iDbId == ids[j])
+ break;
+ }
+ if (j == ids.size())
+ i++;
+ else
+ list.Remove(i);
+ }
+ }
+
+ int iSelectedLabel = 0;
+ if (list.Size() > 1 || (!bRemove && !list.IsEmpty()))
+ {
+ list.Sort(SortByLabel, SortOrderAscending, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING) ? SortAttributeIgnoreArticle : SortAttributeNone);
+ CGUIDialogSelect* pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
+ if (pDialog)
+ {
+ pDialog->Reset();
+ pDialog->SetItems(list);
+ pDialog->SetHeading(CVariant{20356});
+ pDialog->Open();
+ iSelectedLabel = pDialog->GetSelectedItem();
+ }
+ }
+
+ if (iSelectedLabel > -1 && iSelectedLabel < list.Size())
+ return database.LinkMovieToTvshow(dbId, list[iSelectedLabel]->GetVideoInfoTag()->m_iDbId, bRemove);
+
+ return false;
+}
+
+bool CGUIDialogVideoInfo::OnGetFanart(const std::shared_ptr<CFileItem>& videoItem)
+{
+ if (videoItem == nullptr || !videoItem->HasVideoInfoTag())
+ return false;
+
+ // update the db
+ CVideoDatabase videodb;
+ if (!videodb.Open())
+ return false;
+
+ CVideoThumbLoader loader;
+ CFileItem item(*videoItem);
+ loader.LoadItem(&item);
+
+ CFileItemList items;
+ if (item.HasArt("fanart"))
+ {
+ CFileItemPtr itemCurrent(new CFileItem("fanart://Current", false));
+ itemCurrent->SetArt("thumb", item.GetArt("fanart"));
+ itemCurrent->SetLabel(g_localizeStrings.Get(20440));
+ items.Add(itemCurrent);
+ }
+
+ // add the none option
+ {
+ CFileItemPtr itemNone(new CFileItem("fanart://None", false));
+ itemNone->SetArt("icon", "DefaultVideo.png");
+ itemNone->SetLabel(g_localizeStrings.Get(20439));
+ items.Add(itemNone);
+ }
+
+ std::string result;
+ VECSOURCES sources(*CMediaSourceSettings::GetInstance().GetSources("video"));
+ CServiceBroker::GetMediaManager().GetLocalDrives(sources);
+ AddItemPathToFileBrowserSources(sources, item);
+ bool flip = false;
+ if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) ||
+ StringUtils::EqualsNoCase(result, "fanart://Current"))
+ return false;
+
+ else if (StringUtils::EqualsNoCase(result, "fanart://None") || !CFileUtils::Exists(result))
+ result.clear();
+ if (!result.empty() && flip)
+ result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
+
+ videodb.SetArtForItem(item.GetVideoInfoTag()->m_iDbId, item.GetVideoInfoTag()->m_type, "fanart", result);
+
+ // clear view cache and reload images
+ CUtil::DeleteVideoDatabaseDirectoryCache();
+
+ return true;
+}
+
+void CGUIDialogVideoInfo::ShowFor(const CFileItem& item)
+{
+ auto window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowVideoNav>(WINDOW_VIDEO_NAV);
+ if (window)
+ {
+ ADDON::ScraperPtr info;
+ window->OnItemInfo(item, info);
+ }
+}
diff --git a/xbmc/video/dialogs/GUIDialogVideoInfo.h b/xbmc/video/dialogs/GUIDialogVideoInfo.h
new file mode 100644
index 0000000..ac19e98
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogVideoInfo.h
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "MediaSource.h"
+#include "guilib/GUIDialog.h"
+#include "media/MediaType.h"
+
+#include <memory>
+
+class CFileItem;
+class CFileItemList;
+class CVideoDatabase;
+
+class CGUIDialogVideoInfo :
+ public CGUIDialog
+{
+public:
+ CGUIDialogVideoInfo(void);
+ ~CGUIDialogVideoInfo(void) override;
+ bool OnMessage(CGUIMessage& message) override;
+ bool OnAction(const CAction &action) override;
+ void SetMovie(const CFileItem *item);
+ bool NeedRefresh() const;
+ bool RefreshAll() const;
+ bool HasUpdatedThumb() const { return m_hasUpdatedThumb; }
+ bool HasUpdatedUserrating() const { return m_hasUpdatedUserrating; }
+
+ std::string GetThumbnail() const;
+ std::shared_ptr<CFileItem> GetCurrentListItem(int offset = 0) override { return m_movieItem; }
+ const CFileItemList& CurrentDirectory() const { return *m_castList; }
+ bool HasListItems() const override { return true; }
+
+ static void AddItemPathToFileBrowserSources(VECSOURCES &sources, const CFileItem &item);
+
+ static int ManageVideoItem(const std::shared_ptr<CFileItem>& item);
+ static bool UpdateVideoItemTitle(const std::shared_ptr<CFileItem>& pItem);
+ static bool CanDeleteVideoItem(const std::shared_ptr<CFileItem>& item);
+ static bool DeleteVideoItemFromDatabase(const std::shared_ptr<CFileItem>& item,
+ bool unavailable = false);
+ static bool DeleteVideoItem(const std::shared_ptr<CFileItem>& item, bool unavailable = false);
+
+ static bool ManageMovieSets(const std::shared_ptr<CFileItem>& item);
+ static bool GetMoviesForSet(const CFileItem *setItem, CFileItemList &originalMovies, CFileItemList &selectedMovies);
+ static bool GetSetForMovie(const CFileItem* movieItem, std::shared_ptr<CFileItem>& selectedSet);
+ static bool SetMovieSet(const CFileItem *movieItem, const CFileItem *selectedSet);
+
+ static bool GetItemsForTag(const std::string &strHeading, const std::string &type, CFileItemList &items, int idTag = -1, bool showAll = true);
+ static bool AddItemsToTag(const std::shared_ptr<CFileItem>& tagItem);
+ static bool RemoveItemsFromTag(const std::shared_ptr<CFileItem>& tagItem);
+
+ static bool ManageVideoItemArtwork(const std::shared_ptr<CFileItem>& item, const MediaType& type);
+
+ static std::string GetLocalizedVideoType(const std::string &strType);
+
+ static void ShowFor(const CFileItem& item);
+
+protected:
+ void OnInitWindow() override;
+ void Update();
+ void SetLabel(int iControl, const std::string& strLabel);
+ void SetUserrating(int userrating) const;
+
+ // link cast to movies
+ void ClearCastList();
+ /**
+ * \brief Search the current directory for a string got from the virtual keyboard
+ * \param strSearch The search string
+ */
+ void OnSearch(std::string& strSearch);
+ /**
+ * \brief Make the actual search for the OnSearch function.
+ * \param strSearch The search string
+ * \param items Items Found
+ */
+ void DoSearch(std::string& strSearch, CFileItemList& items) const;
+ /**
+ * \brief React on the selected search item
+ * \param pItem Search result item
+ */
+ void OnSearchItemFound(const CFileItem* pItem);
+ void Play(bool resume = false);
+ void OnGetArt();
+ void OnGetFanart();
+ void OnSetUserrating() const;
+ void PlayTrailer();
+
+ static bool UpdateVideoItemSortTitle(const std::shared_ptr<CFileItem>& pItem);
+ static bool LinkMovieToTvShow(const std::shared_ptr<CFileItem>& item,
+ bool bRemove,
+ CVideoDatabase& database);
+
+ /*! \brief Pop up a fanart chooser. Does not utilise remote URLs.
+ \param videoItem the item to choose fanart for.
+ */
+ static bool OnGetFanart(const std::shared_ptr<CFileItem>& videoItem);
+
+ std::shared_ptr<CFileItem> m_movieItem;
+ CFileItemList *m_castList;
+ bool m_bViewReview = false;
+ bool m_bRefresh = false;
+ bool m_bRefreshAll = true;
+ bool m_hasUpdatedThumb = false;
+ bool m_hasUpdatedUserrating = false;
+ int m_startUserrating = -1;
+
+private:
+ static std::string ChooseArtType(const CFileItem& item);
+};
diff --git a/xbmc/video/dialogs/GUIDialogVideoOSD.cpp b/xbmc/video/dialogs/GUIDialogVideoOSD.cpp
new file mode 100644
index 0000000..3b8009d
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogVideoOSD.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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 "GUIDialogVideoOSD.h"
+
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "application/Application.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/WindowIDs.h"
+#include "input/InputManager.h"
+#include "input/actions/ActionIDs.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+
+using namespace PVR;
+
+CGUIDialogVideoOSD::CGUIDialogVideoOSD(void)
+ : CGUIDialog(WINDOW_DIALOG_VIDEO_OSD, "VideoOSD.xml")
+{
+ m_loadType = KEEP_IN_MEMORY;
+}
+
+CGUIDialogVideoOSD::~CGUIDialogVideoOSD(void) = default;
+
+void CGUIDialogVideoOSD::FrameMove()
+{
+ if (m_autoClosing)
+ {
+ // check for movement of mouse or a submenu open
+ if (CServiceBroker::GetInputManager().IsMouseActive()
+ || CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_DIALOG_AUDIO_OSD_SETTINGS)
+ || CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_DIALOG_SUBTITLE_OSD_SETTINGS)
+ || CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_DIALOG_VIDEO_OSD_SETTINGS)
+ || CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_DIALOG_CMS_OSD_SETTINGS)
+ || CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_DIALOG_VIDEO_BOOKMARKS)
+ || CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_DIALOG_PVR_OSD_CHANNELS)
+ || CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_DIALOG_PVR_CHANNEL_GUIDE)
+ || CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_DIALOG_OSD_TELETEXT))
+ // extend show time by original value
+ SetAutoClose(m_showDuration);
+ }
+ CGUIDialog::FrameMove();
+}
+
+bool CGUIDialogVideoOSD::OnAction(const CAction &action)
+{
+ if (action.GetID() == ACTION_SHOW_OSD)
+ {
+ Close();
+ return true;
+ }
+
+ return CGUIDialog::OnAction(action);
+}
+
+EVENT_RESULT CGUIDialogVideoOSD::OnMouseEvent(const CPoint &point, const CMouseEvent &event)
+{
+ if (event.m_id == ACTION_MOUSE_WHEEL_UP)
+ {
+ return g_application.OnAction(CAction(ACTION_ANALOG_SEEK_FORWARD, 0.5f)) ? EVENT_RESULT_HANDLED : EVENT_RESULT_UNHANDLED;
+ }
+ if (event.m_id == ACTION_MOUSE_WHEEL_DOWN)
+ {
+ return g_application.OnAction(CAction(ACTION_ANALOG_SEEK_BACK, 0.5f)) ? EVENT_RESULT_HANDLED : EVENT_RESULT_UNHANDLED;
+ }
+
+ return CGUIDialog::OnMouseEvent(point, event);
+}
+
+bool CGUIDialogVideoOSD::OnMessage(CGUIMessage& message)
+{
+ switch ( message.GetMessage() )
+ {
+ case GUI_MSG_VIDEO_MENU_STARTED:
+ {
+ // We have gone to the DVD menu, so close the OSD.
+ Close();
+ }
+ break;
+ case GUI_MSG_WINDOW_DEINIT: // fired when OSD is hidden
+ {
+ // Remove our subdialogs if visible
+ CGUIDialog *pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetDialog(WINDOW_DIALOG_AUDIO_OSD_SETTINGS);
+ if (pDialog && pDialog->IsDialogRunning())
+ pDialog->Close(true);
+ pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetDialog(WINDOW_DIALOG_SUBTITLE_OSD_SETTINGS);
+ if (pDialog && pDialog->IsDialogRunning())
+ pDialog->Close(true);
+ }
+ break;
+ }
+ return CGUIDialog::OnMessage(message);
+}
+
diff --git a/xbmc/video/dialogs/GUIDialogVideoOSD.h b/xbmc/video/dialogs/GUIDialogVideoOSD.h
new file mode 100644
index 0000000..95cfc9f
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogVideoOSD.h
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "guilib/GUIDialog.h"
+
+class CGUIDialogVideoOSD : public CGUIDialog
+{
+public:
+
+ CGUIDialogVideoOSD(void);
+ ~CGUIDialogVideoOSD(void) override;
+
+ void FrameMove() override;
+ bool OnMessage(CGUIMessage& message) override;
+ bool OnAction(const CAction &action) override;
+protected:
+ EVENT_RESULT OnMouseEvent(const CPoint &point, const CMouseEvent &event) override;
+};
diff --git a/xbmc/video/dialogs/GUIDialogVideoSettings.cpp b/xbmc/video/dialogs/GUIDialogVideoSettings.cpp
new file mode 100644
index 0000000..24d9d22
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogVideoSettings.cpp
@@ -0,0 +1,573 @@
+/*
+ * 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 "GUIDialogVideoSettings.h"
+
+#include "GUIPassword.h"
+#include "ServiceBroker.h"
+#include "addons/Skin.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPlayer.h"
+#include "dialogs/GUIDialogYesNo.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "profiles/ProfileManager.h"
+#include "settings/MediaSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "settings/lib/SettingDefinitions.h"
+#include "settings/lib/SettingsManager.h"
+#include "utils/LangCodeExpander.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+#include "video/VideoDatabase.h"
+#include "video/ViewModeSettings.h"
+
+#include <utility>
+
+#define SETTING_VIDEO_VIEW_MODE "video.viewmode"
+#define SETTING_VIDEO_ZOOM "video.zoom"
+#define SETTING_VIDEO_PIXEL_RATIO "video.pixelratio"
+#define SETTING_VIDEO_BRIGHTNESS "video.brightness"
+#define SETTING_VIDEO_CONTRAST "video.contrast"
+#define SETTING_VIDEO_GAMMA "video.gamma"
+#define SETTING_VIDEO_NONLIN_STRETCH "video.nonlinearstretch"
+#define SETTING_VIDEO_POSTPROCESS "video.postprocess"
+#define SETTING_VIDEO_VERTICAL_SHIFT "video.verticalshift"
+#define SETTING_VIDEO_TONEMAP_METHOD "video.tonemapmethod"
+#define SETTING_VIDEO_TONEMAP_PARAM "video.tonemapparam"
+#define SETTING_VIDEO_ORIENTATION "video.orientation"
+
+#define SETTING_VIDEO_VDPAU_NOISE "vdpau.noise"
+#define SETTING_VIDEO_VDPAU_SHARPNESS "vdpau.sharpness"
+
+#define SETTING_VIDEO_INTERLACEMETHOD "video.interlacemethod"
+#define SETTING_VIDEO_SCALINGMETHOD "video.scalingmethod"
+
+#define SETTING_VIDEO_STEREOSCOPICMODE "video.stereoscopicmode"
+#define SETTING_VIDEO_STEREOSCOPICINVERT "video.stereoscopicinvert"
+
+#define SETTING_VIDEO_MAKE_DEFAULT "video.save"
+#define SETTING_VIDEO_CALIBRATION "video.calibration"
+#define SETTING_VIDEO_STREAM "video.stream"
+
+CGUIDialogVideoSettings::CGUIDialogVideoSettings()
+ : CGUIDialogSettingsManualBase(WINDOW_DIALOG_VIDEO_OSD_SETTINGS, "DialogSettings.xml")
+{ }
+
+CGUIDialogVideoSettings::~CGUIDialogVideoSettings() = default;
+
+void CGUIDialogVideoSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingChanged(setting);
+
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == SETTING_VIDEO_INTERLACEMETHOD)
+ {
+ CVideoSettings vs = appPlayer->GetVideoSettings();
+ vs.m_InterlaceMethod = static_cast<EINTERLACEMETHOD>(std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
+ appPlayer->SetVideoSettings(vs);
+ }
+ else if (settingId == SETTING_VIDEO_SCALINGMETHOD)
+ {
+ CVideoSettings vs = appPlayer->GetVideoSettings();
+ vs.m_ScalingMethod = static_cast<ESCALINGMETHOD>(std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
+ appPlayer->SetVideoSettings(vs);
+ }
+ else if (settingId == SETTING_VIDEO_STREAM)
+ {
+ m_videoStream = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
+ // only change the video stream if a different one has been asked for
+ if (appPlayer->GetVideoStream() != m_videoStream)
+ {
+ appPlayer->SetVideoStream(m_videoStream); // Set the video stream to the one selected
+ }
+ }
+ else if (settingId == SETTING_VIDEO_VIEW_MODE)
+ {
+ int value = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
+ const CVideoSettings vs = appPlayer->GetVideoSettings();
+
+ appPlayer->SetRenderViewMode(value, vs.m_CustomZoomAmount, vs.m_CustomPixelRatio,
+ vs.m_CustomVerticalShift, vs.m_CustomNonLinStretch);
+
+ m_viewModeChanged = true;
+ GetSettingsManager()->SetNumber(SETTING_VIDEO_ZOOM, static_cast<double>(vs.m_CustomZoomAmount));
+ GetSettingsManager()->SetNumber(SETTING_VIDEO_PIXEL_RATIO,
+ static_cast<double>(vs.m_CustomPixelRatio));
+ GetSettingsManager()->SetNumber(SETTING_VIDEO_VERTICAL_SHIFT,
+ static_cast<double>(vs.m_CustomVerticalShift));
+ GetSettingsManager()->SetBool(SETTING_VIDEO_NONLIN_STRETCH, vs.m_CustomNonLinStretch);
+ m_viewModeChanged = false;
+ }
+ else if (settingId == SETTING_VIDEO_ZOOM ||
+ settingId == SETTING_VIDEO_VERTICAL_SHIFT ||
+ settingId == SETTING_VIDEO_PIXEL_RATIO ||
+ settingId == SETTING_VIDEO_NONLIN_STRETCH)
+ {
+ CVideoSettings vs = appPlayer->GetVideoSettings();
+ if (settingId == SETTING_VIDEO_ZOOM)
+ vs.m_CustomZoomAmount = static_cast<float>(std::static_pointer_cast<const CSettingNumber>(setting)->GetValue());
+ else if (settingId == SETTING_VIDEO_VERTICAL_SHIFT)
+ vs.m_CustomVerticalShift = static_cast<float>(std::static_pointer_cast<const CSettingNumber>(setting)->GetValue());
+ else if (settingId == SETTING_VIDEO_PIXEL_RATIO)
+ vs.m_CustomPixelRatio = static_cast<float>(std::static_pointer_cast<const CSettingNumber>(setting)->GetValue());
+ else if (settingId == SETTING_VIDEO_NONLIN_STRETCH)
+ vs.m_CustomNonLinStretch = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+
+ // try changing the view mode to custom. If it already is set to custom
+ // manually call the render manager
+ if (GetSettingsManager()->GetInt(SETTING_VIDEO_VIEW_MODE) != ViewModeCustom)
+ GetSettingsManager()->SetInt(SETTING_VIDEO_VIEW_MODE, ViewModeCustom);
+ else
+ appPlayer->SetRenderViewMode(vs.m_ViewMode, vs.m_CustomZoomAmount, vs.m_CustomPixelRatio,
+ vs.m_CustomVerticalShift, vs.m_CustomNonLinStretch);
+ }
+ else if (settingId == SETTING_VIDEO_POSTPROCESS)
+ {
+ CVideoSettings vs = appPlayer->GetVideoSettings();
+ vs.m_PostProcess = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ appPlayer->SetVideoSettings(vs);
+ }
+ else if (settingId == SETTING_VIDEO_BRIGHTNESS)
+ {
+ CVideoSettings vs = appPlayer->GetVideoSettings();
+ vs.m_Brightness = static_cast<float>(std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
+ appPlayer->SetVideoSettings(vs);
+ }
+ else if (settingId == SETTING_VIDEO_CONTRAST)
+ {
+ CVideoSettings vs = appPlayer->GetVideoSettings();
+ vs.m_Contrast = static_cast<float>(std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
+ appPlayer->SetVideoSettings(vs);
+ }
+ else if (settingId == SETTING_VIDEO_GAMMA)
+ {
+ CVideoSettings vs = appPlayer->GetVideoSettings();
+ vs.m_Gamma = static_cast<float>(std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
+ appPlayer->SetVideoSettings(vs);
+ }
+ else if (settingId == SETTING_VIDEO_VDPAU_NOISE)
+ {
+ CVideoSettings vs = appPlayer->GetVideoSettings();
+ vs.m_NoiseReduction = static_cast<float>(std::static_pointer_cast<const CSettingNumber>(setting)->GetValue());
+ appPlayer->SetVideoSettings(vs);
+ }
+ else if (settingId == SETTING_VIDEO_VDPAU_SHARPNESS)
+ {
+ CVideoSettings vs = appPlayer->GetVideoSettings();
+ vs.m_Sharpness = static_cast<float>(std::static_pointer_cast<const CSettingNumber>(setting)->GetValue());
+ appPlayer->SetVideoSettings(vs);
+ }
+ else if (settingId == SETTING_VIDEO_TONEMAP_METHOD)
+ {
+ CVideoSettings vs = appPlayer->GetVideoSettings();
+ vs.m_ToneMapMethod = static_cast<ETONEMAPMETHOD>(
+ std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
+ appPlayer->SetVideoSettings(vs);
+ }
+ else if (settingId == SETTING_VIDEO_TONEMAP_PARAM)
+ {
+ CVideoSettings vs = appPlayer->GetVideoSettings();
+ vs.m_ToneMapParam = static_cast<float>(std::static_pointer_cast<const CSettingNumber>(setting)->GetValue());
+ appPlayer->SetVideoSettings(vs);
+ }
+ else if (settingId == SETTING_VIDEO_ORIENTATION)
+ {
+ CVideoSettings vs = appPlayer->GetVideoSettings();
+ vs.m_Orientation = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
+ appPlayer->SetVideoSettings(vs);
+ }
+ else if (settingId == SETTING_VIDEO_STEREOSCOPICMODE)
+ {
+ CVideoSettings vs = appPlayer->GetVideoSettings();
+ vs.m_StereoMode = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
+ appPlayer->SetVideoSettings(vs);
+ }
+ else if (settingId == SETTING_VIDEO_STEREOSCOPICINVERT)
+ {
+ CVideoSettings vs = appPlayer->GetVideoSettings();
+ vs.m_StereoInvert = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ appPlayer->SetVideoSettings(vs);
+ }
+}
+
+void CGUIDialogVideoSettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingChanged(setting);
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == SETTING_VIDEO_CALIBRATION)
+ {
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ auto settingsComponent = CServiceBroker::GetSettingsComponent();
+ if (!settingsComponent)
+ return;
+
+ auto settings = settingsComponent->GetSettings();
+ if (!settings)
+ return;
+
+ auto calibsetting = settings->GetSetting(CSettings::SETTING_VIDEOSCREEN_GUICALIBRATION);
+ if (!calibsetting)
+ {
+ CLog::Log(LOGERROR, "Failed to load setting for: {}",
+ CSettings::SETTING_VIDEOSCREEN_GUICALIBRATION);
+ return;
+ }
+
+ // launch calibration window
+ if (profileManager->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE &&
+ g_passwordManager.CheckSettingLevelLock(calibsetting->GetLevel()))
+ return;
+
+ CServiceBroker::GetGUI()->GetWindowManager().ForceActivateWindow(WINDOW_SCREEN_CALIBRATION);
+ }
+ //! @todo implement
+ else if (settingId == SETTING_VIDEO_MAKE_DEFAULT)
+ Save();
+}
+
+bool CGUIDialogVideoSettings::Save()
+{
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ if (profileManager->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE &&
+ !g_passwordManager.CheckSettingLevelLock(::SettingLevel::Expert))
+ return true;
+
+ // prompt user if they are sure
+ if (CGUIDialogYesNo::ShowAndGetInput(CVariant(12376), CVariant(12377)))
+ { // reset the settings
+ CVideoDatabase db;
+ if (!db.Open())
+ return true;
+ db.EraseAllVideoSettings();
+ db.Close();
+
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ CMediaSettings::GetInstance().GetDefaultVideoSettings() = appPlayer->GetVideoSettings();
+ CMediaSettings::GetInstance().GetDefaultVideoSettings().m_SubtitleStream = -1;
+ CMediaSettings::GetInstance().GetDefaultVideoSettings().m_AudioStream = -1;
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+ }
+
+ return true;
+}
+
+void CGUIDialogVideoSettings::SetupView()
+{
+ CGUIDialogSettingsManualBase::SetupView();
+
+ SetHeading(13395);
+ SET_CONTROL_HIDDEN(CONTROL_SETTINGS_OKAY_BUTTON);
+ SET_CONTROL_HIDDEN(CONTROL_SETTINGS_CUSTOM_BUTTON);
+ SET_CONTROL_LABEL(CONTROL_SETTINGS_CANCEL_BUTTON, 15067);
+}
+
+void CGUIDialogVideoSettings::InitializeSettings()
+{
+ CGUIDialogSettingsManualBase::InitializeSettings();
+
+ const std::shared_ptr<CSettingCategory> category = AddCategory("videosettings", -1);
+ if (category == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogVideoSettings: unable to setup settings");
+ return;
+ }
+
+ // get all necessary setting groups
+ const std::shared_ptr<CSettingGroup> groupVideoStream = AddGroup(category);
+ if (groupVideoStream == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogVideoSettings: unable to setup settings");
+ return;
+ }
+ const std::shared_ptr<CSettingGroup> groupVideo = AddGroup(category);
+ if (groupVideo == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogVideoSettings: unable to setup settings");
+ return;
+ }
+ const std::shared_ptr<CSettingGroup> groupStereoscopic = AddGroup(category);
+ if (groupStereoscopic == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogVideoSettings: unable to setup settings");
+ return;
+ }
+ const std::shared_ptr<CSettingGroup> groupSaveAsDefault = AddGroup(category);
+ if (groupSaveAsDefault == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogVideoSettings: unable to setup settings");
+ return;
+ }
+
+ bool usePopup = g_SkinInfo->HasSkinFile("DialogSlider.xml");
+
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ const CVideoSettings videoSettings = appPlayer->GetVideoSettings();
+
+ TranslatableIntegerSettingOptions entries;
+
+ entries.clear();
+ entries.push_back(TranslatableIntegerSettingOption(16039, VS_INTERLACEMETHOD_NONE));
+ entries.push_back(TranslatableIntegerSettingOption(16019, VS_INTERLACEMETHOD_AUTO));
+ entries.push_back(TranslatableIntegerSettingOption(20131, VS_INTERLACEMETHOD_RENDER_BLEND));
+ entries.push_back(TranslatableIntegerSettingOption(20129, VS_INTERLACEMETHOD_RENDER_WEAVE));
+ entries.push_back(TranslatableIntegerSettingOption(16021, VS_INTERLACEMETHOD_RENDER_BOB));
+ entries.push_back(TranslatableIntegerSettingOption(16020, VS_INTERLACEMETHOD_DEINTERLACE));
+ entries.push_back(TranslatableIntegerSettingOption(16036, VS_INTERLACEMETHOD_DEINTERLACE_HALF));
+ entries.push_back(
+ TranslatableIntegerSettingOption(16311, VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL));
+ entries.push_back(TranslatableIntegerSettingOption(16310, VS_INTERLACEMETHOD_VDPAU_TEMPORAL));
+ entries.push_back(TranslatableIntegerSettingOption(16325, VS_INTERLACEMETHOD_VDPAU_BOB));
+ entries.push_back(
+ TranslatableIntegerSettingOption(16318, VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF));
+ entries.push_back(
+ TranslatableIntegerSettingOption(16317, VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF));
+ entries.push_back(TranslatableIntegerSettingOption(16327, VS_INTERLACEMETHOD_VAAPI_BOB));
+ entries.push_back(TranslatableIntegerSettingOption(16328, VS_INTERLACEMETHOD_VAAPI_MADI));
+ entries.push_back(TranslatableIntegerSettingOption(16329, VS_INTERLACEMETHOD_VAAPI_MACI));
+ entries.push_back(TranslatableIntegerSettingOption(16320, VS_INTERLACEMETHOD_DXVA_AUTO));
+
+ /* remove unsupported methods */
+ for (TranslatableIntegerSettingOptions::iterator it = entries.begin(); it != entries.end(); )
+ {
+ if (appPlayer->Supports(static_cast<EINTERLACEMETHOD>(it->value)))
+ ++it;
+ else
+ it = entries.erase(it);
+ }
+
+ if (!entries.empty())
+ {
+ EINTERLACEMETHOD method = videoSettings.m_InterlaceMethod;
+ if (!appPlayer->Supports(method))
+ {
+ method = appPlayer->GetDeinterlacingMethodDefault();
+ }
+ AddSpinner(groupVideo, SETTING_VIDEO_INTERLACEMETHOD, 16038, SettingLevel::Basic, static_cast<int>(method), entries);
+ }
+
+ entries.clear();
+ entries.push_back(TranslatableIntegerSettingOption(16301, VS_SCALINGMETHOD_NEAREST));
+ entries.push_back(TranslatableIntegerSettingOption(16302, VS_SCALINGMETHOD_LINEAR));
+ entries.push_back(TranslatableIntegerSettingOption(16303, VS_SCALINGMETHOD_CUBIC_B_SPLINE));
+ entries.push_back(TranslatableIntegerSettingOption(16314, VS_SCALINGMETHOD_CUBIC_MITCHELL));
+ entries.push_back(TranslatableIntegerSettingOption(16321, VS_SCALINGMETHOD_CUBIC_CATMULL));
+ entries.push_back(TranslatableIntegerSettingOption(16326, VS_SCALINGMETHOD_CUBIC_0_075));
+ entries.push_back(TranslatableIntegerSettingOption(16330, VS_SCALINGMETHOD_CUBIC_0_1));
+ entries.push_back(TranslatableIntegerSettingOption(16304, VS_SCALINGMETHOD_LANCZOS2));
+ entries.push_back(TranslatableIntegerSettingOption(16323, VS_SCALINGMETHOD_SPLINE36_FAST));
+ entries.push_back(TranslatableIntegerSettingOption(16315, VS_SCALINGMETHOD_LANCZOS3_FAST));
+ entries.push_back(TranslatableIntegerSettingOption(16322, VS_SCALINGMETHOD_SPLINE36));
+ entries.push_back(TranslatableIntegerSettingOption(16305, VS_SCALINGMETHOD_LANCZOS3));
+ entries.push_back(TranslatableIntegerSettingOption(16306, VS_SCALINGMETHOD_SINC8));
+ entries.push_back(TranslatableIntegerSettingOption(16307, VS_SCALINGMETHOD_BICUBIC_SOFTWARE));
+ entries.push_back(TranslatableIntegerSettingOption(16308, VS_SCALINGMETHOD_LANCZOS_SOFTWARE));
+ entries.push_back(TranslatableIntegerSettingOption(16309, VS_SCALINGMETHOD_SINC_SOFTWARE));
+ entries.push_back(TranslatableIntegerSettingOption(13120, VS_SCALINGMETHOD_VDPAU_HARDWARE));
+ entries.push_back(TranslatableIntegerSettingOption(16319, VS_SCALINGMETHOD_DXVA_HARDWARE));
+ entries.push_back(TranslatableIntegerSettingOption(16316, VS_SCALINGMETHOD_AUTO));
+
+ /* remove unsupported methods */
+ for(TranslatableIntegerSettingOptions::iterator it = entries.begin(); it != entries.end(); )
+ {
+ if (appPlayer->Supports(static_cast<ESCALINGMETHOD>(it->value)))
+ ++it;
+ else
+ it = entries.erase(it);
+ }
+
+ AddSpinner(groupVideo, SETTING_VIDEO_SCALINGMETHOD, 16300, SettingLevel::Basic, static_cast<int>(videoSettings.m_ScalingMethod), entries);
+
+ AddVideoStreams(groupVideoStream, SETTING_VIDEO_STREAM);
+
+ if (appPlayer->Supports(RENDERFEATURE_STRETCH) || appPlayer->Supports(RENDERFEATURE_PIXEL_RATIO))
+ {
+ AddList(groupVideo, SETTING_VIDEO_VIEW_MODE, 629, SettingLevel::Basic, videoSettings.m_ViewMode, CViewModeSettings::ViewModesFiller, 629);
+ }
+ if (appPlayer->Supports(RENDERFEATURE_ZOOM))
+ AddSlider(groupVideo, SETTING_VIDEO_ZOOM, 216, SettingLevel::Basic,
+ videoSettings.m_CustomZoomAmount, "{:2.2f}", 0.5f, 0.01f, 2.0f, 216, usePopup);
+ if (appPlayer->Supports(RENDERFEATURE_VERTICAL_SHIFT))
+ AddSlider(groupVideo, SETTING_VIDEO_VERTICAL_SHIFT, 225, SettingLevel::Basic,
+ videoSettings.m_CustomVerticalShift, "{:2.2f}", -2.0f, 0.01f, 2.0f, 225, usePopup);
+ if (appPlayer->Supports(RENDERFEATURE_PIXEL_RATIO))
+ AddSlider(groupVideo, SETTING_VIDEO_PIXEL_RATIO, 217, SettingLevel::Basic,
+ videoSettings.m_CustomPixelRatio, "{:2.2f}", 0.5f, 0.01f, 2.0f, 217, usePopup);
+
+ AddList(groupVideo, SETTING_VIDEO_ORIENTATION, 21843, SettingLevel::Basic, videoSettings.m_Orientation, CGUIDialogVideoSettings::VideoOrientationFiller, 21843);
+
+ if (appPlayer->Supports(RENDERFEATURE_POSTPROCESS))
+ AddToggle(groupVideo, SETTING_VIDEO_POSTPROCESS, 16400, SettingLevel::Basic, videoSettings.m_PostProcess);
+ if (appPlayer->Supports(RENDERFEATURE_BRIGHTNESS))
+ AddPercentageSlider(groupVideo, SETTING_VIDEO_BRIGHTNESS, 464, SettingLevel::Basic, static_cast<int>(videoSettings.m_Brightness), 14047, 1, 464, usePopup);
+ if (appPlayer->Supports(RENDERFEATURE_CONTRAST))
+ AddPercentageSlider(groupVideo, SETTING_VIDEO_CONTRAST, 465, SettingLevel::Basic, static_cast<int>(videoSettings.m_Contrast), 14047, 1, 465, usePopup);
+ if (appPlayer->Supports(RENDERFEATURE_GAMMA))
+ AddPercentageSlider(groupVideo, SETTING_VIDEO_GAMMA, 466, SettingLevel::Basic, static_cast<int>(videoSettings.m_Gamma), 14047, 1, 466, usePopup);
+ if (appPlayer->Supports(RENDERFEATURE_NOISE))
+ AddSlider(groupVideo, SETTING_VIDEO_VDPAU_NOISE, 16312, SettingLevel::Basic,
+ videoSettings.m_NoiseReduction, "{:2.2f}", 0.0f, 0.01f, 1.0f, 16312, usePopup);
+ if (appPlayer->Supports(RENDERFEATURE_SHARPNESS))
+ AddSlider(groupVideo, SETTING_VIDEO_VDPAU_SHARPNESS, 16313, SettingLevel::Basic,
+ videoSettings.m_Sharpness, "{:2.2f}", -1.0f, 0.02f, 1.0f, 16313, usePopup);
+ if (appPlayer->Supports(RENDERFEATURE_NONLINSTRETCH))
+ AddToggle(groupVideo, SETTING_VIDEO_NONLIN_STRETCH, 659, SettingLevel::Basic, videoSettings.m_CustomNonLinStretch);
+
+ // tone mapping
+ if (appPlayer->Supports(RENDERFEATURE_TONEMAP))
+ {
+ bool visible = !(CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
+ CServiceBroker::GetWinSystem()->SETTING_WINSYSTEM_IS_HDR_DISPLAY) &&
+ CServiceBroker::GetWinSystem()->IsHDRDisplay());
+ entries.clear();
+ entries.push_back(TranslatableIntegerSettingOption(36554, VS_TONEMAPMETHOD_OFF));
+ entries.push_back(TranslatableIntegerSettingOption(36555, VS_TONEMAPMETHOD_REINHARD));
+ entries.push_back(TranslatableIntegerSettingOption(36557, VS_TONEMAPMETHOD_ACES));
+ entries.push_back(TranslatableIntegerSettingOption(36558, VS_TONEMAPMETHOD_HABLE));
+
+ AddSpinner(groupVideo, SETTING_VIDEO_TONEMAP_METHOD, 36553, SettingLevel::Basic,
+ videoSettings.m_ToneMapMethod, entries, false, visible);
+ AddSlider(groupVideo, SETTING_VIDEO_TONEMAP_PARAM, 36556, SettingLevel::Basic,
+ videoSettings.m_ToneMapParam, "{:2.2f}", 0.1f, 0.1f, 5.0f, 36556, usePopup, false,
+ visible);
+ }
+
+ // stereoscopic settings
+ entries.clear();
+ entries.push_back(TranslatableIntegerSettingOption(16316, RENDER_STEREO_MODE_OFF));
+ entries.push_back(TranslatableIntegerSettingOption(36503, RENDER_STEREO_MODE_SPLIT_HORIZONTAL));
+ entries.push_back(TranslatableIntegerSettingOption(36504, RENDER_STEREO_MODE_SPLIT_VERTICAL));
+ AddSpinner(groupStereoscopic, SETTING_VIDEO_STEREOSCOPICMODE, 36535, SettingLevel::Basic, videoSettings.m_StereoMode, entries);
+ AddToggle(groupStereoscopic, SETTING_VIDEO_STEREOSCOPICINVERT, 36536, SettingLevel::Basic, videoSettings.m_StereoInvert);
+
+ // general settings
+ AddButton(groupSaveAsDefault, SETTING_VIDEO_MAKE_DEFAULT, 12376, SettingLevel::Basic);
+ AddButton(groupSaveAsDefault, SETTING_VIDEO_CALIBRATION, 214, SettingLevel::Basic);
+}
+
+void CGUIDialogVideoSettings::AddVideoStreams(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& settingId)
+{
+ if (group == NULL || settingId.empty())
+ return;
+
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ m_videoStream = appPlayer->GetVideoStream();
+ if (m_videoStream < 0)
+ m_videoStream = 0;
+
+ AddList(group, settingId, 38031, SettingLevel::Basic, m_videoStream, VideoStreamsOptionFiller, 38031);
+}
+
+void CGUIDialogVideoSettings::VideoStreamsOptionFiller(
+ const std::shared_ptr<const CSetting>& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ int videoStreamCount = appPlayer->GetVideoStreamCount();
+ // cycle through each video stream and add it to our list control
+ for (int i = 0; i < videoStreamCount; ++i)
+ {
+ std::string strItem;
+ std::string strLanguage;
+
+ VideoStreamInfo info;
+ appPlayer->GetVideoStreamInfo(i, info);
+
+ g_LangCodeExpander.Lookup(info.language, strLanguage);
+
+ if (!info.name.empty())
+ {
+ if (!strLanguage.empty())
+ strItem = StringUtils::Format("{} - {}", strLanguage, info.name);
+ else
+ strItem = info.name;
+ }
+ else if (!strLanguage.empty())
+ {
+ strItem = strLanguage;
+ }
+
+ if (info.codecName.empty())
+ strItem += StringUtils::Format(" ({}x{}", info.width, info.height);
+ else
+ strItem += StringUtils::Format(" ({}, {}x{}", info.codecName, info.width, info.height);
+
+ if (info.bitrate)
+ strItem += StringUtils::Format(", {} bps)", info.bitrate);
+ else
+ strItem += ")";
+
+ strItem += FormatFlags(info.flags);
+ strItem += StringUtils::Format(" ({}/{})", i + 1, videoStreamCount);
+ list.emplace_back(strItem, i);
+ }
+
+ if (list.empty())
+ {
+ list.emplace_back(g_localizeStrings.Get(231), -1);
+ current = -1;
+ }
+}
+
+void CGUIDialogVideoSettings::VideoOrientationFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ list.emplace_back(g_localizeStrings.Get(687), 0);
+ list.emplace_back(g_localizeStrings.Get(35229), 90);
+ list.emplace_back(g_localizeStrings.Get(35230), 180);
+ list.emplace_back(g_localizeStrings.Get(35231), 270);
+}
+
+std::string CGUIDialogVideoSettings::FormatFlags(StreamFlags flags)
+{
+ std::vector<std::string> localizedFlags;
+ if (flags & StreamFlags::FLAG_DEFAULT)
+ localizedFlags.emplace_back(g_localizeStrings.Get(39105));
+ if (flags & StreamFlags::FLAG_FORCED)
+ localizedFlags.emplace_back(g_localizeStrings.Get(39106));
+ if (flags & StreamFlags::FLAG_HEARING_IMPAIRED)
+ localizedFlags.emplace_back(g_localizeStrings.Get(39107));
+ if (flags & StreamFlags::FLAG_VISUAL_IMPAIRED)
+ localizedFlags.emplace_back(g_localizeStrings.Get(39108));
+
+ std::string formated = StringUtils::Join(localizedFlags, ", ");
+
+ if (!formated.empty())
+ formated = StringUtils::Format(" [{}]", formated);
+
+ return formated;
+}
diff --git a/xbmc/video/dialogs/GUIDialogVideoSettings.h b/xbmc/video/dialogs/GUIDialogVideoSettings.h
new file mode 100644
index 0000000..5314e40
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogVideoSettings.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "cores/VideoPlayer/Interface/StreamInfo.h"
+#include "settings/dialogs/GUIDialogSettingsManualBase.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+struct IntegerSettingOption;
+
+class CGUIDialogVideoSettings : public CGUIDialogSettingsManualBase
+{
+public:
+ CGUIDialogVideoSettings();
+ ~CGUIDialogVideoSettings() override;
+
+protected:
+ // implementations of ISettingCallback
+ void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+ void OnSettingAction(const std::shared_ptr<const CSetting>& setting) override;
+
+ void AddVideoStreams(const std::shared_ptr<CSettingGroup>& group, const std::string& settingId);
+ static void VideoStreamsOptionFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data);
+
+ static void VideoOrientationFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data);
+
+ static std::string FormatFlags(StreamFlags flags);
+
+ // specialization of CGUIDialogSettingsBase
+ bool AllowResettingSettings() const override { return false; }
+ bool Save() override;
+ void SetupView() override;
+
+ // specialization of CGUIDialogSettingsManualBase
+ void InitializeSettings() override;
+
+private:
+ int m_videoStream;
+ bool m_viewModeChanged = false;
+};