summaryrefslogtreecommitdiffstats
path: root/xbmc/dialogs/GUIDialogMediaSource.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/dialogs/GUIDialogMediaSource.cpp')
-rw-r--r--xbmc/dialogs/GUIDialogMediaSource.cpp620
1 files changed, 620 insertions, 0 deletions
diff --git a/xbmc/dialogs/GUIDialogMediaSource.cpp b/xbmc/dialogs/GUIDialogMediaSource.cpp
new file mode 100644
index 0000000..13b13e5
--- /dev/null
+++ b/xbmc/dialogs/GUIDialogMediaSource.cpp
@@ -0,0 +1,620 @@
+/*
+ * 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 "GUIDialogMediaSource.h"
+#include "ServiceBroker.h"
+#include "guilib/GUIKeyboardFactory.h"
+#include "GUIDialogFileBrowser.h"
+#include "video/windows/GUIWindowVideoBase.h"
+#include "music/windows/GUIWindowMusicBase.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "input/Key.h"
+#include "Util.h"
+#include "utils/URIUtils.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+#include "filesystem/Directory.h"
+#include "filesystem/PVRDirectory.h"
+#include "GUIDialogYesNo.h"
+#include "FileItem.h"
+#include "settings/MediaSourceSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "guilib/LocalizeStrings.h"
+#include "PasswordManager.h"
+#include "URL.h"
+#include "pvr/recordings/PVRRecordingsPath.h"
+
+#if defined(TARGET_ANDROID)
+#include "utils/FileUtils.h"
+
+#include "platform/android/activity/XBMCApp.h"
+#endif
+
+#ifdef TARGET_WINDOWS_STORE
+#include "platform/win10/filesystem/WinLibraryDirectory.h"
+#endif
+
+using namespace XFILE;
+
+#define CONTROL_HEADING 2
+#define CONTROL_PATH 10
+#define CONTROL_PATH_BROWSE 11
+#define CONTROL_NAME 12
+#define CONTROL_PATH_ADD 13
+#define CONTROL_PATH_REMOVE 14
+#define CONTROL_OK 18
+#define CONTROL_CANCEL 19
+#define CONTROL_CONTENT 20
+
+CGUIDialogMediaSource::CGUIDialogMediaSource(void)
+ : CGUIDialog(WINDOW_DIALOG_MEDIA_SOURCE, "DialogMediaSource.xml")
+{
+ m_paths = new CFileItemList;
+ m_loadType = KEEP_IN_MEMORY;
+}
+
+CGUIDialogMediaSource::~CGUIDialogMediaSource()
+{
+ delete m_paths;
+}
+
+bool CGUIDialogMediaSource::OnBack(int actionID)
+{
+ m_confirmed = false;
+ return CGUIDialog::OnBack(actionID);
+}
+
+bool CGUIDialogMediaSource::OnMessage(CGUIMessage& message)
+{
+ switch (message.GetMessage())
+ {
+ case GUI_MSG_CLICKED:
+ {
+ int iControl = message.GetSenderId();
+ int iAction = message.GetParam1();
+ if (iControl == CONTROL_PATH && (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK))
+ OnPath(GetSelectedItem());
+ else if (iControl == CONTROL_PATH_BROWSE)
+ OnPathBrowse(GetSelectedItem());
+ else if (iControl == CONTROL_PATH_ADD)
+ OnPathAdd();
+ else if (iControl == CONTROL_PATH_REMOVE)
+ OnPathRemove(GetSelectedItem());
+ else if (iControl == CONTROL_NAME)
+ {
+ OnEditChanged(iControl, m_name);
+ UpdateButtons();
+ }
+ else if (iControl == CONTROL_OK)
+ OnOK();
+ else if (iControl == CONTROL_CANCEL)
+ OnCancel();
+ else
+ break;
+ return true;
+ }
+ break;
+ case GUI_MSG_WINDOW_INIT:
+ {
+ UpdateButtons();
+ }
+ break;
+ case GUI_MSG_SETFOCUS:
+ if (message.GetControlId() == CONTROL_PATH_BROWSE ||
+ message.GetControlId() == CONTROL_PATH_ADD ||
+ message.GetControlId() == CONTROL_PATH_REMOVE)
+ {
+ HighlightItem(GetSelectedItem());
+ }
+ else
+ HighlightItem(-1);
+ break;
+ }
+ return CGUIDialog::OnMessage(message);
+}
+
+// \brief Show CGUIDialogMediaSource dialog and prompt for a new media source.
+// \return True if the media source is added, false otherwise.
+bool CGUIDialogMediaSource::ShowAndAddMediaSource(const std::string &type)
+{
+ CGUIDialogMediaSource *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogMediaSource>(WINDOW_DIALOG_MEDIA_SOURCE);
+ if (!dialog) return false;
+ dialog->Initialize();
+ dialog->SetShare(CMediaSource());
+ dialog->SetTypeOfMedia(type);
+ dialog->Open();
+ bool confirmed(dialog->IsConfirmed());
+ if (confirmed)
+ {
+ // Add this media source
+ // Get unique source name
+ std::string strName = dialog->GetUniqueMediaSourceName();
+
+ CMediaSource share;
+ share.FromNameAndPaths(type, strName, dialog->GetPaths());
+ if (dialog->m_paths->Size() > 0)
+ share.m_strThumbnailImage = dialog->m_paths->Get(0)->GetArt("thumb");
+ CMediaSourceSettings::GetInstance().AddShare(type, share);
+ OnMediaSourceChanged(type, "", share);
+ }
+ dialog->m_paths->Clear();
+ return confirmed;
+}
+
+bool CGUIDialogMediaSource::ShowAndEditMediaSource(const std::string &type, const std::string&share)
+{
+ VECSOURCES* pShares = CMediaSourceSettings::GetInstance().GetSources(type);
+ if (pShares)
+ {
+ for (unsigned int i = 0;i<pShares->size();++i)
+ {
+ if (StringUtils::EqualsNoCase((*pShares)[i].strName, share))
+ return ShowAndEditMediaSource(type, (*pShares)[i]);
+ }
+ }
+ return false;
+}
+
+bool CGUIDialogMediaSource::ShowAndEditMediaSource(const std::string &type, const CMediaSource &share)
+{
+ std::string strOldName = share.strName;
+ CGUIDialogMediaSource *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogMediaSource>(WINDOW_DIALOG_MEDIA_SOURCE);
+ if (!dialog) return false;
+ dialog->Initialize();
+ dialog->SetShare(share);
+ dialog->SetTypeOfMedia(type, true);
+ dialog->Open();
+ bool confirmed(dialog->IsConfirmed());
+ if (confirmed)
+ {
+ // Update media source
+ // Get unique new source name when changed
+ std::string strName(dialog->m_name);
+ if (!StringUtils::EqualsNoCase(dialog->m_name, strOldName))
+ strName = dialog->GetUniqueMediaSourceName();
+
+ CMediaSource newShare;
+ newShare.FromNameAndPaths(type, strName, dialog->GetPaths());
+ CMediaSourceSettings::GetInstance().UpdateShare(type, strOldName, newShare);
+
+ OnMediaSourceChanged(type, strOldName, newShare);
+ }
+ dialog->m_paths->Clear();
+ return confirmed;
+}
+
+std::string CGUIDialogMediaSource::GetUniqueMediaSourceName()
+{
+ // Get unique source name for this media type
+ unsigned int i, j = 2;
+ bool bConfirmed = false;
+ VECSOURCES* pShares = CMediaSourceSettings::GetInstance().GetSources(m_type);
+ std::string strName = m_name;
+ while (!bConfirmed)
+ {
+ for (i = 0; i<pShares->size(); ++i)
+ {
+ if (StringUtils::EqualsNoCase((*pShares)[i].strName, strName))
+ break;
+ }
+ if (i < pShares->size())
+ // found a match - try next
+ strName = StringUtils::Format("{} ({})", m_name, j++);
+ else
+ bConfirmed = true;
+ }
+ return strName;
+}
+
+void CGUIDialogMediaSource::OnMediaSourceChanged(const std::string& type, const std::string& oldName, const CMediaSource& share)
+{
+ // Processing once media source added/edited - library scraping and scanning
+ if (!StringUtils::StartsWithNoCase(share.strPath, "rss://") &&
+ !StringUtils::StartsWithNoCase(share.strPath, "rsss://") &&
+ !StringUtils::StartsWithNoCase(share.strPath, "upnp://"))
+ {
+ if (type == "video" && !URIUtils::IsLiveTV(share.strPath))
+ // Assign content to a path, refresh scraper information optionally start a scan
+ CGUIWindowVideoBase::OnAssignContent(share.strPath);
+ else if (type == "music")
+ CGUIWindowMusicBase::OnAssignContent(oldName, share);
+ }
+}
+
+void CGUIDialogMediaSource::OnPathBrowse(int item)
+{
+ if (item < 0 || item >= m_paths->Size()) return;
+ // Browse is called. Open the filebrowser dialog.
+ // Ignore current path is best at this stage??
+ std::string path = m_paths->Get(item)->GetPath();
+ bool allowNetworkShares(m_type != "programs");
+ VECSOURCES extraShares;
+
+ if (m_name != CUtil::GetTitleFromPath(path))
+ m_bNameChanged = true;
+ path.clear();
+
+ if (m_type == "music")
+ {
+ CMediaSource share1;
+#if defined(TARGET_ANDROID)
+ // add the default android music directory
+ std::string path;
+ if (CXBMCApp::GetExternalStorage(path, "music") && !path.empty() && CDirectory::Exists(path))
+ {
+ share1.strPath = path;
+ share1.strName = g_localizeStrings.Get(20240);
+ share1.m_ignore = true;
+ extraShares.push_back(share1);
+ }
+#endif
+
+#if defined(TARGET_WINDOWS_STORE)
+ // add the default UWP music directory
+ std::string path;
+ if (XFILE::CWinLibraryDirectory::GetStoragePath(m_type, path) && !path.empty() && CDirectory::Exists(path))
+ {
+ share1.strPath = path;
+ share1.strName = g_localizeStrings.Get(20245);
+ share1.m_ignore = true;
+ extraShares.push_back(share1);
+ }
+#endif
+
+ // add the music playlist location
+ share1.strPath = "special://musicplaylists/";
+ share1.strName = g_localizeStrings.Get(20011);
+ share1.m_ignore = true;
+ extraShares.push_back(share1);
+
+ // add the recordings dir as needed
+ if (CPVRDirectory::HasRadioRecordings())
+ {
+ share1.strPath = PVR::CPVRRecordingsPath::PATH_ACTIVE_RADIO_RECORDINGS;
+ share1.strName = g_localizeStrings.Get(19017); // Recordings
+ extraShares.push_back(share1);
+ }
+ if (CPVRDirectory::HasDeletedRadioRecordings())
+ {
+ share1.strPath = PVR::CPVRRecordingsPath::PATH_DELETED_RADIO_RECORDINGS;
+ share1.strName = g_localizeStrings.Get(19184); // Deleted recordings
+ extraShares.push_back(share1);
+ }
+
+ if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_AUDIOCDS_RECORDINGPATH) != "")
+ {
+ share1.strPath = "special://recordings/";
+ share1.strName = g_localizeStrings.Get(21883);
+ extraShares.push_back(share1);
+ }
+ }
+ else if (m_type == "video")
+ {
+ CMediaSource share1;
+#if defined(TARGET_ANDROID)
+ // add the default android video directory
+ std::string path;
+ if (CXBMCApp::GetExternalStorage(path, "videos") && !path.empty() && CFileUtils::Exists(path))
+ {
+ share1.strPath = path;
+ share1.strName = g_localizeStrings.Get(20241);
+ share1.m_ignore = true;
+ extraShares.push_back(share1);
+ }
+#endif
+#if defined(TARGET_WINDOWS_STORE)
+ // add the default UWP music directory
+ std::string path;
+ if (XFILE::CWinLibraryDirectory::GetStoragePath(m_type, path) && !path.empty() && CDirectory::Exists(path))
+ {
+ share1.strPath = path;
+ share1.strName = g_localizeStrings.Get(20246);
+ share1.m_ignore = true;
+ extraShares.push_back(share1);
+ }
+#endif
+
+ // add the video playlist location
+ share1.m_ignore = true;
+ share1.strPath = "special://videoplaylists/";
+ share1.strName = g_localizeStrings.Get(20012);
+ extraShares.push_back(share1);
+
+ // add the recordings dir as needed
+ if (CPVRDirectory::HasTVRecordings())
+ {
+ share1.strPath = PVR::CPVRRecordingsPath::PATH_ACTIVE_TV_RECORDINGS;
+ share1.strName = g_localizeStrings.Get(19017); // Recordings
+ extraShares.push_back(share1);
+ }
+ if (CPVRDirectory::HasDeletedTVRecordings())
+ {
+ share1.strPath = PVR::CPVRRecordingsPath::PATH_DELETED_TV_RECORDINGS;
+ share1.strName = g_localizeStrings.Get(19184); // Deleted recordings
+ extraShares.push_back(share1);
+ }
+ }
+ else if (m_type == "pictures")
+ {
+ CMediaSource share1;
+#if defined(TARGET_ANDROID)
+ // add the default android music directory
+ std::string path;
+ if (CXBMCApp::GetExternalStorage(path, "pictures") && !path.empty() && CFileUtils::Exists(path))
+ {
+ share1.strPath = path;
+ share1.strName = g_localizeStrings.Get(20242);
+ share1.m_ignore = true;
+ extraShares.push_back(share1);
+ }
+
+ path.clear();
+ if (CXBMCApp::GetExternalStorage(path, "photos") && !path.empty() && CFileUtils::Exists(path))
+ {
+ share1.strPath = path;
+ share1.strName = g_localizeStrings.Get(20243);
+ share1.m_ignore = true;
+ extraShares.push_back(share1);
+ }
+#endif
+#if defined(TARGET_WINDOWS_STORE)
+ // add the default UWP music directory
+ std::string path;
+ if (XFILE::CWinLibraryDirectory::GetStoragePath(m_type, path) && !path.empty() && CDirectory::Exists(path))
+ {
+ share1.strPath = path;
+ share1.strName = g_localizeStrings.Get(20247);
+ share1.m_ignore = true;
+ extraShares.push_back(share1);
+ }
+ path.clear();
+ if (XFILE::CWinLibraryDirectory::GetStoragePath("photos", path) && !path.empty() && CDirectory::Exists(path))
+ {
+ share1.strPath = path;
+ share1.strName = g_localizeStrings.Get(20248);
+ share1.m_ignore = true;
+ extraShares.push_back(share1);
+ }
+#endif
+
+ share1.m_ignore = true;
+ if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_DEBUG_SCREENSHOTPATH) != "")
+ {
+ share1.strPath = "special://screenshots/";
+ share1.strName = g_localizeStrings.Get(20008);
+ extraShares.push_back(share1);
+ }
+ }
+ else if (m_type == "games")
+ {
+ // nothing to add
+ }
+ else if (m_type == "programs")
+ {
+ // nothing to add
+ }
+ if (CGUIDialogFileBrowser::ShowAndGetSource(path, allowNetworkShares, extraShares.size() == 0 ? NULL : &extraShares))
+ {
+ if (item < m_paths->Size()) // if the skin does funky things, m_paths may have been cleared
+ m_paths->Get(item)->SetPath(path);
+ if (!m_bNameChanged || m_name.empty())
+ {
+ CURL url(path);
+ m_name = url.GetWithoutUserDetails();
+ URIUtils::RemoveSlashAtEnd(m_name);
+ m_name = CUtil::GetTitleFromPath(m_name);
+ }
+ UpdateButtons();
+ }
+}
+
+void CGUIDialogMediaSource::OnPath(int item)
+{
+ if (item < 0 || item >= m_paths->Size()) return;
+
+ std::string path(m_paths->Get(item)->GetPath());
+ if (m_name != CUtil::GetTitleFromPath(path))
+ m_bNameChanged = true;
+
+ CGUIKeyboardFactory::ShowAndGetInput(path, CVariant{ g_localizeStrings.Get(1021) }, false);
+ m_paths->Get(item)->SetPath(path);
+
+ if (!m_bNameChanged || m_name.empty())
+ {
+ CURL url(m_paths->Get(item)->GetPath());
+ m_name = url.GetWithoutUserDetails();
+ URIUtils::RemoveSlashAtEnd(m_name);
+ m_name = CUtil::GetTitleFromPath(m_name);
+ }
+ UpdateButtons();
+}
+
+void CGUIDialogMediaSource::OnOK()
+{
+ // Verify the paths by doing a GetDirectory.
+ CFileItemList items;
+
+ // Create temp media source to encode path urls as multipath
+ // Name of actual source may need to be made unique when saved in sources
+ CMediaSource share;
+ share.FromNameAndPaths(m_type, m_name, GetPaths());
+
+ if (StringUtils::StartsWithNoCase(share.strPath, "plugin://") ||
+ CDirectory::GetDirectory(share.strPath, items, "", DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_ALLOW_PROMPT) ||
+ CGUIDialogYesNo::ShowAndGetInput(CVariant{ 1001 }, CVariant{ 1025 }))
+ {
+ m_confirmed = true;
+ Close();
+ }
+}
+
+void CGUIDialogMediaSource::OnCancel()
+{
+ m_confirmed = false;
+ Close();
+}
+
+void CGUIDialogMediaSource::UpdateButtons()
+{
+ if (!m_paths->Size()) // sanity
+ return;
+
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_OK, !m_paths->Get(0)->GetPath().empty() && !m_name.empty());
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_PATH_ADD, !m_paths->Get(0)->GetPath().empty());
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_PATH_REMOVE, m_paths->Size() > 1);
+ // name
+ SET_CONTROL_LABEL2(CONTROL_NAME, m_name);
+ SendMessage(GUI_MSG_SET_TYPE, CONTROL_NAME, 0, 1022);
+
+ int currentItem = GetSelectedItem();
+ SendMessage(GUI_MSG_LABEL_RESET, CONTROL_PATH);
+ for (int i = 0; i < m_paths->Size(); i++)
+ {
+ CFileItemPtr item = m_paths->Get(i);
+ std::string path;
+ CURL url(item->GetPath());
+ path = url.GetWithoutUserDetails();
+ if (path.empty()) path = "<" + g_localizeStrings.Get(231) + ">"; // <None>
+ item->SetLabel(path);
+ }
+ CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), CONTROL_PATH, 0, 0, m_paths);
+ OnMessage(msg);
+ SendMessage(GUI_MSG_ITEM_SELECT, CONTROL_PATH, currentItem);
+
+ SET_CONTROL_HIDDEN(CONTROL_CONTENT);
+}
+
+void CGUIDialogMediaSource::SetShare(const CMediaSource &share)
+{
+ m_paths->Clear();
+ for (unsigned int i = 0; i < share.vecPaths.size(); i++)
+ {
+ CFileItemPtr item(new CFileItem(share.vecPaths[i], true));
+ m_paths->Add(item);
+ }
+ if (0 == share.vecPaths.size())
+ {
+ CFileItemPtr item(new CFileItem("", true));
+ m_paths->Add(item);
+ }
+ m_name = share.strName;
+ UpdateButtons();
+}
+
+void CGUIDialogMediaSource::SetTypeOfMedia(const std::string &type, bool editNotAdd)
+{
+ m_type = type;
+ std::string heading;
+ if (editNotAdd)
+ {
+ if (type == "video")
+ heading = g_localizeStrings.Get(10053);
+ else if (type == "music")
+ heading = g_localizeStrings.Get(10054);
+ else if (type == "pictures")
+ heading = g_localizeStrings.Get(10055);
+ else if (type == "games")
+ heading = g_localizeStrings.Get(35252); // "Edit game source"
+ else if (type == "programs")
+ heading = g_localizeStrings.Get(10056);
+ else
+ heading = g_localizeStrings.Get(10057);
+ }
+ else
+ {
+ if (type == "video")
+ heading = g_localizeStrings.Get(10048);
+ else if (type == "music")
+ heading = g_localizeStrings.Get(10049);
+ else if (type == "pictures")
+ heading = g_localizeStrings.Get(13006);
+ else if (type == "games")
+ heading = g_localizeStrings.Get(35251); // "Add game source"
+ else if (type == "programs")
+ heading = g_localizeStrings.Get(10051);
+ else
+ heading = g_localizeStrings.Get(10052);
+ }
+ SET_CONTROL_LABEL(CONTROL_HEADING, heading);
+}
+
+int CGUIDialogMediaSource::GetSelectedItem()
+{
+ CGUIMessage message(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_PATH);
+ OnMessage(message);
+ int value = message.GetParam1();
+ if (value < 0 || value >= m_paths->Size()) return 0;
+ return value;
+}
+
+void CGUIDialogMediaSource::HighlightItem(int item)
+{
+ for (int i = 0; i < m_paths->Size(); i++)
+ m_paths->Get(i)->Select(false);
+ if (item >= 0 && item < m_paths->Size())
+ m_paths->Get(item)->Select(true);
+ CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), CONTROL_PATH, item);
+ OnMessage(msg);
+}
+
+void CGUIDialogMediaSource::OnPathRemove(int item)
+{
+ m_paths->Remove(item);
+ UpdateButtons();
+ if (item >= m_paths->Size())
+ HighlightItem(m_paths->Size() - 1);
+ else
+ HighlightItem(item);
+ if (m_paths->Size() <= 1)
+ {
+ SET_CONTROL_FOCUS(CONTROL_PATH_ADD, 0);
+ }
+}
+
+void CGUIDialogMediaSource::OnPathAdd()
+{
+ // add a new item and select it as well
+ CFileItemPtr item(new CFileItem("", true));
+ m_paths->Add(item);
+ UpdateButtons();
+ HighlightItem(m_paths->Size() - 1);
+}
+
+std::vector<std::string> CGUIDialogMediaSource::GetPaths() const
+{
+ std::vector<std::string> paths;
+ for (int i = 0; i < m_paths->Size(); i++)
+ {
+ if (!m_paths->Get(i)->GetPath().empty())
+ { // strip off the user and password for supported paths (anything that the password manager can auth)
+ // and add the user/pass to the password manager - note, we haven't confirmed that it works
+ // at this point, but if it doesn't, the user will get prompted anyway in implementation.
+ CURL url(m_paths->Get(i)->GetPath());
+ if (CPasswordManager::GetInstance().IsURLSupported(url) && !url.GetUserName().empty())
+ {
+ CPasswordManager::GetInstance().SaveAuthenticatedURL(url);
+ url.SetPassword("");
+ url.SetUserName("");
+ url.SetDomain("");
+ }
+ paths.push_back(url.Get());
+ }
+ }
+ return paths;
+}
+
+void CGUIDialogMediaSource::OnDeinitWindow(int nextWindowID)
+{
+ CGUIDialog::OnDeinitWindow(nextWindowID);
+
+ // clear paths container
+ CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_PATH, 0);
+ OnMessage(msg);
+}