Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

743 lines (652 sloc) 19.674 kb
/*
* Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with XBMC; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*
*/
#include "system.h"
#include "MediaManager.h"
#include "guilib/LocalizeStrings.h"
#include "URL.h"
#include "utils/URIUtils.h"
#ifdef _WIN32
#include "WIN32Util.h"
#include "utils/CharsetConverter.h"
#endif
#include "guilib/GUIWindowManager.h"
#ifdef HAS_DVD_DRIVE
#ifndef _WIN32
// TODO: switch all ports to use auto sources
#include "DetectDVDType.h"
#include "filesystem/iso9660.h"
#endif
#endif
#include "Autorun.h"
#include "GUIUserMessages.h"
#include "settings/Settings.h"
#include "utils/XBMCTinyXML.h"
#include "threads/SingleLock.h"
#include "utils/log.h"
#include "dialogs/GUIDialogKaiToast.h"
#include "utils/JobManager.h"
#include "AutorunMediaJob.h"
#include "settings/GUISettings.h"
#include "FileItem.h"
#include "filesystem/File.h"
#include "filesystem/DirectoryFactory.h"
#include "filesystem/Directory.h"
#include "utils/Crc32.h"
#if defined(TARGET_DARWIN)
#include "osx/DarwinStorageProvider.h"
#elif defined(TARGET_ANDROID)
#include "android/AndroidStorageProvider.h"
#elif defined(TARGET_FREEBSD)
#include "linux/LinuxStorageProvider.h"
#elif defined(_LINUX)
#include "linux/LinuxStorageProvider.h"
#include <sys/ioctl.h>
#include <linux/cdrom.h>
#elif _WIN32
#include "windows/Win32StorageProvider.h"
#endif
using namespace std;
using namespace XFILE;
const char MEDIA_SOURCES_XML[] = { "special://profile/mediasources.xml" };
class CMediaManager g_mediaManager;
CMediaManager::CMediaManager()
{
m_bhasoptical = false;
m_platformStorage = NULL;
}
void CMediaManager::Stop()
{
m_platformStorage->Stop();
delete m_platformStorage;
m_platformStorage = NULL;
}
void CMediaManager::Initialize()
{
if (!m_platformStorage)
{
#if defined(TARGET_DARWIN)
m_platformStorage = new CDarwinStorageProvider();
#elif defined(TARGET_ANDROID)
m_platformStorage = new CAndroidStorageProvider();
#elif defined(_LINUX)
m_platformStorage = new CLinuxStorageProvider();
#elif _WIN32
m_platformStorage = new CWin32StorageProvider();
#endif
}
#ifdef HAS_DVD_DRIVE
m_strFirstAvailDrive = m_platformStorage->GetFirstOpticalDeviceFileName();
#endif
m_platformStorage->Initialize();
}
bool CMediaManager::LoadSources()
{
// clear our location list
m_locations.clear();
// load xml file...
CXBMCTinyXML xmlDoc;
if ( !xmlDoc.LoadFile( MEDIA_SOURCES_XML ) )
return false;
TiXmlElement* pRootElement = xmlDoc.RootElement();
if ( !pRootElement || strcmpi(pRootElement->Value(), "mediasources") != 0)
{
CLog::Log(LOGERROR, "Error loading %s, Line %d (%s)", MEDIA_SOURCES_XML, xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
return false;
}
// load the <network> block
TiXmlNode *pNetwork = pRootElement->FirstChild("network");
if (pNetwork)
{
TiXmlElement *pLocation = pNetwork->FirstChildElement("location");
while (pLocation)
{
CNetworkLocation location;
pLocation->Attribute("id", &location.id);
if (pLocation->FirstChild())
{
location.path = pLocation->FirstChild()->Value();
m_locations.push_back(location);
}
pLocation = pLocation->NextSiblingElement("location");
}
}
return true;
}
bool CMediaManager::SaveSources()
{
CXBMCTinyXML xmlDoc;
TiXmlElement xmlRootElement("mediasources");
TiXmlNode *pRoot = xmlDoc.InsertEndChild(xmlRootElement);
if (!pRoot) return false;
TiXmlElement networkNode("network");
TiXmlNode *pNetworkNode = pRoot->InsertEndChild(networkNode);
if (pNetworkNode)
{
for (vector<CNetworkLocation>::iterator it = m_locations.begin(); it != m_locations.end(); it++)
{
TiXmlElement locationNode("location");
locationNode.SetAttribute("id", (*it).id);
TiXmlText value((*it).path);
locationNode.InsertEndChild(value);
pNetworkNode->InsertEndChild(locationNode);
}
}
return xmlDoc.SaveFile(MEDIA_SOURCES_XML);
}
void CMediaManager::GetLocalDrives(VECSOURCES &localDrives, bool includeQ)
{
CSingleLock lock(m_CritSecStorageProvider);
m_platformStorage->GetLocalDrives(localDrives);
}
void CMediaManager::GetRemovableDrives(VECSOURCES &removableDrives)
{
CSingleLock lock(m_CritSecStorageProvider);
m_platformStorage->GetRemovableDrives(removableDrives);
}
void CMediaManager::GetNetworkLocations(VECSOURCES &locations, bool autolocations)
{
// Load our xml file
LoadSources();
for (unsigned int i = 0; i < m_locations.size(); i++)
{
CMediaSource share;
share.strPath = m_locations[i].path;
CURL url(share.strPath);
share.strName = url.GetWithoutUserDetails();
locations.push_back(share);
}
if (autolocations)
{
CMediaSource share;
share.m_ignore = true;
#ifdef HAS_FILESYSTEM_SMB
share.strPath = "smb://";
share.strName = g_localizeStrings.Get(20171);
locations.push_back(share);
#endif
#ifdef HAS_FILESYSTEM_NFS
share.strPath = "nfs://";
share.strName = g_localizeStrings.Get(20259);
locations.push_back(share);
#endif// HAS_FILESYSTEM_NFS
#ifdef HAS_UPNP
share.strPath = "upnp://";
share.strName = "UPnP Devices";
locations.push_back(share);
#endif
#ifdef HAS_ZEROCONF
share.strPath = "zeroconf://";
share.strName = "Zeroconf Browser";
locations.push_back(share);
#endif
}
}
bool CMediaManager::AddNetworkLocation(const CStdString &path)
{
CNetworkLocation location;
location.path = path;
location.id = (int)m_locations.size();
m_locations.push_back(location);
return SaveSources();
}
bool CMediaManager::HasLocation(const CStdString& path) const
{
for (unsigned int i=0;i<m_locations.size();++i)
{
if (URIUtils::CompareWithoutSlashAtEnd(m_locations[i].path, path))
return true;
}
return false;
}
bool CMediaManager::RemoveLocation(const CStdString& path)
{
for (unsigned int i=0;i<m_locations.size();++i)
{
if (URIUtils::CompareWithoutSlashAtEnd(m_locations[i].path, path))
{
// prompt for sources, remove, cancel,
m_locations.erase(m_locations.begin()+i);
return SaveSources();
}
}
return false;
}
bool CMediaManager::SetLocationPath(const CStdString& oldPath, const CStdString& newPath)
{
for (unsigned int i=0;i<m_locations.size();++i)
{
if (URIUtils::CompareWithoutSlashAtEnd(m_locations[i].path, oldPath))
{
m_locations[i].path = newPath;
return SaveSources();
}
}
return false;
}
void CMediaManager::AddAutoSource(const CMediaSource &share, bool bAutorun)
{
g_settings.AddShare("files",share);
g_settings.AddShare("video",share);
g_settings.AddShare("pictures",share);
g_settings.AddShare("music",share);
g_settings.AddShare("programs",share);
CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_SOURCES);
g_windowManager.SendThreadMessage( msg );
#ifdef HAS_DVD_DRIVE
if(bAutorun)
MEDIA_DETECT::CAutorun::ExecuteAutorun(share.strPath);
#endif
}
void CMediaManager::RemoveAutoSource(const CMediaSource &share)
{
g_settings.DeleteSource("files", share.strName, share.strPath, true);
g_settings.DeleteSource("video", share.strName, share.strPath, true);
g_settings.DeleteSource("pictures", share.strName, share.strPath, true);
g_settings.DeleteSource("music", share.strName, share.strPath, true);
g_settings.DeleteSource("programs", share.strName, share.strPath, true);
CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_SOURCES);
g_windowManager.SendThreadMessage( msg );
#ifdef HAS_DVD_DRIVE
// delete cached CdInfo if any
RemoveCdInfo(TranslateDevicePath(share.strPath, true));
#endif
}
/////////////////////////////////////////////////////////////
// AutoSource status functions:
// - TODO: translate cdda://<device>/
CStdString CMediaManager::TranslateDevicePath(const CStdString& devicePath, bool bReturnAsDevice)
{
CSingleLock waitLock(m_muAutoSource);
CStdString strDevice = devicePath;
// fallback for cdda://local/ and empty devicePath
#ifdef HAS_DVD_DRIVE
if(devicePath.empty() || devicePath.Left(12).Compare("cdda://local")==0)
strDevice = m_strFirstAvailDrive;
#endif
#ifdef _WIN32
if(!m_bhasoptical)
return "";
if(bReturnAsDevice == false)
strDevice.Replace("\\\\.\\","");
else if(!strDevice.empty() && strDevice[1]==':')
strDevice.Format("\\\\.\\%c:", strDevice[0]);
URIUtils::RemoveSlashAtEnd(strDevice);
#endif
return strDevice;
}
bool CMediaManager::IsDiscInDrive(const CStdString& devicePath)
{
#ifdef HAS_DVD_DRIVE
#ifdef _WIN32
if(!m_bhasoptical)
return false;
CStdString strDevice = TranslateDevicePath(devicePath, true);
std::map<CStdString,CCdInfo*>::iterator it;
CSingleLock waitLock(m_muAutoSource);
it = m_mapCdInfo.find(strDevice);
if(it != m_mapCdInfo.end())
return true;
else
return false;
#else
if(URIUtils::IsDVD(devicePath) || devicePath.IsEmpty())
return MEDIA_DETECT::CDetectDVDMedia::IsDiscInDrive(); // TODO: switch all ports to use auto sources
else
return true; // Assume other paths to be mounted already
#endif
#else
return false;
#endif
}
bool CMediaManager::IsAudio(const CStdString& devicePath)
{
#ifdef HAS_DVD_DRIVE
#ifdef _WIN32
if(!m_bhasoptical)
return false;
CCdInfo* pCdInfo = GetCdInfo(devicePath);
if(pCdInfo != NULL && pCdInfo->IsAudio(1))
return true;
return false;
#else
// TODO: switch all ports to use auto sources
MEDIA_DETECT::CCdInfo* pInfo = MEDIA_DETECT::CDetectDVDMedia::GetCdInfo();
if (pInfo != NULL && pInfo->IsAudio(1))
return true;
#endif
#endif
return false;
}
bool CMediaManager::HasOpticalDrive()
{
#ifdef HAS_DVD_DRIVE
if (!m_strFirstAvailDrive.IsEmpty())
return true;
#endif
return false;
}
DWORD CMediaManager::GetDriveStatus(const CStdString& devicePath)
{
#ifdef HAS_DVD_DRIVE
#ifdef _WIN32
if(!m_bhasoptical)
return DRIVE_NOT_READY;
CStdString strDevice = TranslateDevicePath(devicePath, true);
DWORD dwRet = DRIVE_NOT_READY;
int status = CWIN32Util::GetDriveStatus(strDevice);
switch(status)
{
case -1: // error
dwRet = DRIVE_NOT_READY;
break;
case 0: // no media
dwRet = DRIVE_CLOSED_NO_MEDIA;
break;
case 1: // tray open
dwRet = DRIVE_OPEN;
break;
case 2: // media accessible
dwRet = DRIVE_CLOSED_MEDIA_PRESENT;
break;
}
return dwRet;
#else
return MEDIA_DETECT::CDetectDVDMedia::DriveReady();
#endif
#else
return DRIVE_NOT_READY;
#endif
}
#ifdef HAS_DVD_DRIVE
CCdInfo* CMediaManager::GetCdInfo(const CStdString& devicePath)
{
#ifdef _WIN32
if(!m_bhasoptical)
return NULL;
CStdString strDevice = TranslateDevicePath(devicePath, true);
std::map<CStdString,CCdInfo*>::iterator it;
{
CSingleLock waitLock(m_muAutoSource);
it = m_mapCdInfo.find(strDevice);
if(it != m_mapCdInfo.end())
return it->second;
}
CCdInfo* pCdInfo=NULL;
CCdIoSupport cdio;
pCdInfo = cdio.GetCdInfo((char*)strDevice.c_str());
if(pCdInfo!=NULL)
{
CSingleLock waitLock(m_muAutoSource);
m_mapCdInfo.insert(std::pair<CStdString,CCdInfo*>(strDevice,pCdInfo));
}
return pCdInfo;
#else
return MEDIA_DETECT::CDetectDVDMedia::GetCdInfo();
#endif
}
bool CMediaManager::RemoveCdInfo(const CStdString& devicePath)
{
if(!m_bhasoptical)
return false;
CStdString strDevice = TranslateDevicePath(devicePath, true);
std::map<CStdString,CCdInfo*>::iterator it;
CSingleLock waitLock(m_muAutoSource);
it = m_mapCdInfo.find(strDevice);
if(it != m_mapCdInfo.end())
{
if(it->second != NULL)
delete it->second;
m_mapCdInfo.erase(it);
return true;
}
return false;
}
CStdString CMediaManager::GetDiskLabel(const CStdString& devicePath)
{
#ifdef _WIN32
if(!m_bhasoptical)
return "";
CStdString strDevice = TranslateDevicePath(devicePath);
WCHAR cVolumenName[128];
WCHAR cFSName[128];
URIUtils::AddSlashAtEnd(strDevice);
if(GetVolumeInformationW(CStdStringW(strDevice).c_str(), cVolumenName, 127, NULL, NULL, NULL, cFSName, 127)==0)
return "";
g_charsetConverter.wToUTF8(cVolumenName, strDevice);
return strDevice.TrimRight(" ");
#else
return MEDIA_DETECT::CDetectDVDMedia::GetDVDLabel();
#endif
}
CStdString CMediaManager::GetDiskUniqueId(const CStdString& devicePath)
{
CStdString strDevice = devicePath;
if (strDevice.IsEmpty()) // if no value passed, use the current default disc path.
strDevice = GetDiscPath(); // in case of non-Windows we must obtain the disc path
#ifdef _WIN32
if (!m_bhasoptical)
return "";
strDevice = TranslateDevicePath(strDevice);
URIUtils::AddSlashAtEnd(strDevice);
#endif
CStdString strDrive = g_mediaManager.TranslateDevicePath(strDevice);
#ifndef _WIN32
{
CSingleLock waitLock(m_muAutoSource);
CCdInfo* pInfo = g_mediaManager.GetCdInfo();
if ( pInfo )
{
if (pInfo->IsISOUDF(1) || pInfo->IsISOHFS(1) || pInfo->IsIso9660(1) || pInfo->IsIso9660Interactive(1))
strDrive = "iso9660://";
else
strDrive = "D:\\";
}
else
{
CLog::Log(LOGERROR, "GetDiskUniqueId: Failed getting CD info");
return "";
}
}
#endif
CStdString pathVideoTS = URIUtils::AddFileToFolder(strDrive, "VIDEO_TS");
if(! CDirectory::Exists(pathVideoTS) )
return ""; // return empty
CLog::Log(LOGDEBUG, "GetDiskUniqueId: Trying to retrieve ID for path %s", pathVideoTS.c_str());
uint32_t dvdcrc = 0;
CStdString strID;
if (HashDVD(pathVideoTS, dvdcrc))
{
strID.Format("removable://%s_%08x", GetDiskLabel(devicePath), dvdcrc);
CLog::Log(LOGDEBUG, "GetDiskUniqueId: Got ID %s for DVD disk", strID.c_str());
}
return strID;
}
bool CMediaManager::HashDVD(const CStdString& dvdpath, uint32_t& crc)
{
CFileItemList vecItemsTS;
bool success = false;
// first try to open the VIDEO_TS folder of the DVD
if (!CDirectory::GetDirectory( dvdpath, vecItemsTS, ".ifo" ))
{
CLog::Log(LOGERROR, "%s - Cannot open dvd VIDEO_TS folder -- ABORTING", __FUNCTION__);
return false;
}
Crc32 crc32;
bool dataRead = false;
vecItemsTS.Sort(SORT_METHOD_FILE, SortOrderAscending);
for (int i = 0; i < vecItemsTS.Size(); i++)
{
CFileItemPtr videoTSItem = vecItemsTS[i];
success = true;
// get the file name for logging purposes
CStdString fileName = URIUtils::GetFileName(videoTSItem->GetPath());
CLog::Log(LOGDEBUG, "%s - Adding file content for dvd file: %s", __FUNCTION__, fileName.c_str());
CFile file;
if(!file.Open(videoTSItem->GetPath()))
{
CLog::Log(LOGERROR, "%s - Cannot open dvd file: %s -- ABORTING", __FUNCTION__, fileName.c_str());
return false;
}
int res;
char buf[2048];
while( (res = file.Read(buf, sizeof(buf))) > 0)
{
dataRead = true;
crc32.Compute(buf, res);
}
file.Close();
}
if (!dataRead)
{
CLog::Log(LOGERROR, "%s - Did not read any data from the IFO files -- ABORTING", __FUNCTION__);
return false;
}
// put result back in reference parameter
crc = (uint32_t) crc32;
return success;
}
CStdString CMediaManager::GetDiscPath()
{
#ifdef _WIN32
return g_mediaManager.TranslateDevicePath("");
#else
CSingleLock lock(m_CritSecStorageProvider);
VECSOURCES drives;
m_platformStorage->GetRemovableDrives(drives);
for(unsigned i = 0; i < drives.size(); ++i)
{
if(drives[i].m_iDriveType == CMediaSource::SOURCE_TYPE_DVD)
return drives[i].strPath;
}
// iso9660://, cdda://local/ or D:\ depending on disc type
return MEDIA_DETECT::CDetectDVDMedia::GetDVDPath();
#endif
}
#endif
void CMediaManager::SetHasOpticalDrive(bool bstatus)
{
CSingleLock waitLock(m_muAutoSource);
m_bhasoptical = bstatus;
}
bool CMediaManager::Eject(CStdString mountpath)
{
CSingleLock lock(m_CritSecStorageProvider);
return m_platformStorage->Eject(mountpath);
}
void CMediaManager::EjectTray( const bool bEject, const char cDriveLetter )
{
#ifdef HAS_DVD_DRIVE
#ifdef TARGET_WINDOWS
CWIN32Util::EjectTray(cDriveLetter);
#else
boost::shared_ptr<CLibcdio> c_cdio = CLibcdio::GetInstance();
char* dvdDevice = c_cdio->GetDeviceFileName();
m_isoReader.Reset();
int nRetries=3;
while (nRetries-- > 0)
{
CdIo_t* cdio = c_cdio->cdio_open(dvdDevice, DRIVER_UNKNOWN);
if (cdio)
{
c_cdio->cdio_eject_media(&cdio);
c_cdio->cdio_destroy(cdio);
}
else
break;
}
#endif
#endif
}
void CMediaManager::CloseTray(const char cDriveLetter)
{
#ifdef HAS_DVD_DRIVE
#if defined(TARGET_DARWIN)
// FIXME...
#elif defined(__FreeBSD__)
// NYI
#elif defined(_LINUX)
char* dvdDevice = CLibcdio::GetInstance()->GetDeviceFileName();
if (strlen(dvdDevice) != 0)
{
int fd = open(dvdDevice, O_RDONLY | O_NONBLOCK);
if (fd >= 0)
{
ioctl(fd, CDROMCLOSETRAY, 0);
close(fd);
}
}
#elif defined(TARGET_WINDOWS)
CWIN32Util::CloseTray(cDriveLetter);
#endif
#endif
}
void CMediaManager::ToggleTray(const char cDriveLetter)
{
#ifdef HAS_DVD_DRIVE
#if defined(TARGET_WINDOWS)
CWIN32Util::ToggleTray(cDriveLetter);
#else
if (GetDriveStatus() == TRAY_OPEN || GetDriveStatus() == DRIVE_OPEN)
CloseTray();
else
EjectTray();
#endif
#endif
}
void CMediaManager::ProcessEvents()
{
CSingleLock lock(m_CritSecStorageProvider);
if (m_platformStorage->PumpDriveChangeEvents(this))
{
#if defined(HAS_DVD_DRIVE) && defined(TARGET_DARWIN_OSX)
// darwins GetFirstOpticalDeviceFileName only gives us something
// when a disc is inserted
// so we have to refresh m_strFirstAvailDrive when this happens after Initialize
// was called (e.x. the disc was inserted after the start of xbmc)
// else TranslateDevicePath wouldn't give the correct device
m_strFirstAvailDrive = m_platformStorage->GetFirstOpticalDeviceFileName();
#endif
CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES);
g_windowManager.SendThreadMessage(msg);
}
}
std::vector<CStdString> CMediaManager::GetDiskUsage()
{
CSingleLock lock(m_CritSecStorageProvider);
return m_platformStorage->GetDiskUsage();
}
void CMediaManager::OnStorageAdded(const CStdString &label, const CStdString &path)
{
if (g_guiSettings.GetInt("audiocds.autoaction") != AUTOCD_NONE || g_guiSettings.GetBool("dvds.autorun"))
if (g_guiSettings.GetInt("audiocds.autoaction") == AUTOCD_RIP)
CJobManager::GetInstance().AddJob(new CAutorunMediaJob(label, path), this, CJob::PRIORITY_LOW);
else
CJobManager::GetInstance().AddJob(new CAutorunMediaJob(label, path), this, CJob::PRIORITY_HIGH);
else
CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(13021), label, TOAST_DISPLAY_TIME, false);
}
void CMediaManager::OnStorageSafelyRemoved(const CStdString &label)
{
CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(13023), label, TOAST_DISPLAY_TIME, false);
}
void CMediaManager::OnStorageUnsafelyRemoved(const CStdString &label)
{
CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, g_localizeStrings.Get(13022), label);
}
Jump to Line
Something went wrong with that request. Please try again.