1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "FileItem.h"
10 #include "MediaManager.h"
11 #include "ServiceBroker.h"
12 #include "guilib/GUIComponent.h"
13 #include "guilib/LocalizeStrings.h"
14 #include "URL.h"
15 #include "utils/URIUtils.h"
16 #ifdef TARGET_WINDOWS
17 #include "platform/win32/WIN32Util.h"
18 #include "utils/CharsetConverter.h"
19 #endif
20 #include "guilib/GUIWindowManager.h"
21 #ifdef HAS_DVD_DRIVE
22 #ifndef TARGET_WINDOWS
23 //! @todo switch all ports to use auto sources
24 #include <map>
25 #include <utility>
26 #include "DetectDVDType.h"
27 #endif
28 #endif
29 #include "Autorun.h"
30 #include "AutorunMediaJob.h"
31 #include "GUIUserMessages.h"
32 #include "addons/VFSEntry.h"
33 #include "cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.h"
34 #include "dialogs/GUIDialogKaiToast.h"
35 #include "dialogs/GUIDialogPlayEject.h"
36 #include "filesystem/File.h"
37 #include "messaging/helpers/DialogOKHelper.h"
38 #include "settings/MediaSourceSettings.h"
39 #include "settings/Settings.h"
40 #include "settings/SettingsComponent.h"
41 #include "threads/SingleLock.h"
42 #include "utils/JobManager.h"
43 #include "utils/StringUtils.h"
44 #include "utils/XBMCTinyXML.h"
45 #include "utils/log.h"
46
47 #if defined(TARGET_POSIX) && !defined(TARGET_DARWIN) && !(defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY))
48 #include <sys/ioctl.h>
49 #include <linux/cdrom.h>
50 #endif
51 #include <string>
52 #include <vector>
53
54 #ifdef HAVE_LIBBLURAY
55 #include "filesystem/BlurayDirectory.h"
56 #endif
57
58 using namespace XFILE;
59
60 #ifdef HAS_DVD_DRIVE
61 using namespace MEDIA_DETECT;
62 #endif
63
64 const char MEDIA_SOURCES_XML[] = { "special://profile/mediasources.xml" };
65
CMediaManager()66 CMediaManager::CMediaManager()
67 {
68 m_bhasoptical = false;
69 m_platformStorage = nullptr;
70 }
71
Stop()72 void CMediaManager::Stop()
73 {
74 if (m_platformStorage)
75 m_platformStorage->Stop();
76
77 delete m_platformStorage;
78 m_platformStorage = nullptr;
79 }
80
Initialize()81 void CMediaManager::Initialize()
82 {
83 if (!m_platformStorage)
84 {
85 m_platformStorage = IStorageProvider::CreateInstance();
86 }
87 #ifdef HAS_DVD_DRIVE
88 m_strFirstAvailDrive = m_platformStorage->GetFirstOpticalDeviceFileName();
89 #endif
90 m_platformStorage->Initialize();
91 }
92
LoadSources()93 bool CMediaManager::LoadSources()
94 {
95 // clear our location list
96 m_locations.clear();
97
98 // load xml file...
99 CXBMCTinyXML xmlDoc;
100 if ( !xmlDoc.LoadFile( MEDIA_SOURCES_XML ) )
101 return false;
102
103 TiXmlElement* pRootElement = xmlDoc.RootElement();
104 if (!pRootElement || StringUtils::CompareNoCase(pRootElement->Value(), "mediasources") != 0)
105 {
106 CLog::Log(LOGERROR, "Error loading %s, Line %d (%s)", MEDIA_SOURCES_XML, xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
107 return false;
108 }
109
110 // load the <network> block
111 TiXmlNode *pNetwork = pRootElement->FirstChild("network");
112 if (pNetwork)
113 {
114 TiXmlElement *pLocation = pNetwork->FirstChildElement("location");
115 while (pLocation)
116 {
117 CNetworkLocation location;
118 pLocation->Attribute("id", &location.id);
119 if (pLocation->FirstChild())
120 {
121 location.path = pLocation->FirstChild()->Value();
122 m_locations.push_back(location);
123 }
124 pLocation = pLocation->NextSiblingElement("location");
125 }
126 }
127 return true;
128 }
129
SaveSources()130 bool CMediaManager::SaveSources()
131 {
132 CXBMCTinyXML xmlDoc;
133 TiXmlElement xmlRootElement("mediasources");
134 TiXmlNode *pRoot = xmlDoc.InsertEndChild(xmlRootElement);
135 if (!pRoot) return false;
136
137 TiXmlElement networkNode("network");
138 TiXmlNode *pNetworkNode = pRoot->InsertEndChild(networkNode);
139 if (pNetworkNode)
140 {
141 for (std::vector<CNetworkLocation>::iterator it = m_locations.begin(); it != m_locations.end(); ++it)
142 {
143 TiXmlElement locationNode("location");
144 locationNode.SetAttribute("id", (*it).id);
145 TiXmlText value((*it).path);
146 locationNode.InsertEndChild(value);
147 pNetworkNode->InsertEndChild(locationNode);
148 }
149 }
150 return xmlDoc.SaveFile(MEDIA_SOURCES_XML);
151 }
152
GetLocalDrives(VECSOURCES & localDrives,bool includeQ)153 void CMediaManager::GetLocalDrives(VECSOURCES &localDrives, bool includeQ)
154 {
155 CSingleLock lock(m_CritSecStorageProvider);
156 m_platformStorage->GetLocalDrives(localDrives);
157 }
158
GetRemovableDrives(VECSOURCES & removableDrives)159 void CMediaManager::GetRemovableDrives(VECSOURCES &removableDrives)
160 {
161 CSingleLock lock(m_CritSecStorageProvider);
162 if (m_platformStorage)
163 m_platformStorage->GetRemovableDrives(removableDrives);
164 }
165
GetNetworkLocations(VECSOURCES & locations,bool autolocations)166 void CMediaManager::GetNetworkLocations(VECSOURCES &locations, bool autolocations)
167 {
168 // Load our xml file
169 LoadSources();
170 for (unsigned int i = 0; i < m_locations.size(); i++)
171 {
172 CMediaSource share;
173 share.strPath = m_locations[i].path;
174 CURL url(share.strPath);
175 share.strName = url.GetWithoutUserDetails();
176 locations.push_back(share);
177 }
178 if (autolocations)
179 {
180 CMediaSource share;
181 share.m_ignore = true;
182 #ifdef HAS_FILESYSTEM_SMB
183 share.strPath = "smb://";
184 share.strName = g_localizeStrings.Get(20171);
185 locations.push_back(share);
186 #endif
187
188 #ifdef HAS_FILESYSTEM_NFS
189 share.strPath = "nfs://";
190 share.strName = g_localizeStrings.Get(20259);
191 locations.push_back(share);
192 #endif// HAS_FILESYSTEM_NFS
193
194 #ifdef HAS_UPNP
195 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SERVICES_UPNP))
196 {
197 const std::string& strDevices = g_localizeStrings.Get(33040); //"% Devices"
198 share.strPath = "upnp://";
199 share.strName = StringUtils::Format(strDevices.c_str(), "UPnP"); //"UPnP Devices"
200 locations.push_back(share);
201 }
202 #endif
203
204 #ifdef HAS_ZEROCONF
205 share.strPath = "zeroconf://";
206 share.strName = g_localizeStrings.Get(20262);
207 locations.push_back(share);
208 #endif
209
210 if (CServiceBroker::IsBinaryAddonCacheUp())
211 {
212 for (const auto& addon : CServiceBroker::GetVFSAddonCache().GetAddonInstances())
213 {
214 const auto& info = addon->GetProtocolInfo();
215 if (!info.type.empty() && info.supportBrowsing)
216 {
217 share.strPath = info.type + "://";
218 share.strName = g_localizeStrings.GetAddonString(addon->ID(), info.label);
219 if (share.strName.empty())
220 share.strName = g_localizeStrings.Get(info.label);
221 locations.push_back(share);
222 }
223 }
224 }
225 }
226 }
227
AddNetworkLocation(const std::string & path)228 bool CMediaManager::AddNetworkLocation(const std::string &path)
229 {
230 CNetworkLocation location;
231 location.path = path;
232 location.id = (int)m_locations.size();
233 m_locations.push_back(location);
234 return SaveSources();
235 }
236
HasLocation(const std::string & path) const237 bool CMediaManager::HasLocation(const std::string& path) const
238 {
239 for (unsigned int i=0;i<m_locations.size();++i)
240 {
241 if (URIUtils::CompareWithoutSlashAtEnd(m_locations[i].path, path))
242 return true;
243 }
244
245 return false;
246 }
247
248
RemoveLocation(const std::string & path)249 bool CMediaManager::RemoveLocation(const std::string& path)
250 {
251 for (unsigned int i=0;i<m_locations.size();++i)
252 {
253 if (URIUtils::CompareWithoutSlashAtEnd(m_locations[i].path, path))
254 {
255 // prompt for sources, remove, cancel,
256 m_locations.erase(m_locations.begin()+i);
257 return SaveSources();
258 }
259 }
260
261 return false;
262 }
263
SetLocationPath(const std::string & oldPath,const std::string & newPath)264 bool CMediaManager::SetLocationPath(const std::string& oldPath, const std::string& newPath)
265 {
266 for (unsigned int i=0;i<m_locations.size();++i)
267 {
268 if (URIUtils::CompareWithoutSlashAtEnd(m_locations[i].path, oldPath))
269 {
270 m_locations[i].path = newPath;
271 return SaveSources();
272 }
273 }
274
275 return false;
276 }
277
AddAutoSource(const CMediaSource & share,bool bAutorun)278 void CMediaManager::AddAutoSource(const CMediaSource &share, bool bAutorun)
279 {
280 CMediaSourceSettings::GetInstance().AddShare("files", share);
281 CMediaSourceSettings::GetInstance().AddShare("video", share);
282 CMediaSourceSettings::GetInstance().AddShare("pictures", share);
283 CMediaSourceSettings::GetInstance().AddShare("music", share);
284 CMediaSourceSettings::GetInstance().AddShare("programs", share);
285 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_SOURCES);
286 CGUIComponent *gui = CServiceBroker::GetGUI();
287 if (gui)
288 gui->GetWindowManager().SendThreadMessage( msg );
289
290 #ifdef HAS_DVD_DRIVE
291 if(bAutorun)
292 MEDIA_DETECT::CAutorun::ExecuteAutorun(share.strPath);
293 #endif
294 }
295
RemoveAutoSource(const CMediaSource & share)296 void CMediaManager::RemoveAutoSource(const CMediaSource &share)
297 {
298 CMediaSourceSettings::GetInstance().DeleteSource("files", share.strName, share.strPath, true);
299 CMediaSourceSettings::GetInstance().DeleteSource("video", share.strName, share.strPath, true);
300 CMediaSourceSettings::GetInstance().DeleteSource("pictures", share.strName, share.strPath, true);
301 CMediaSourceSettings::GetInstance().DeleteSource("music", share.strName, share.strPath, true);
302 CMediaSourceSettings::GetInstance().DeleteSource("programs", share.strName, share.strPath, true);
303 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_SOURCES);
304 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage( msg );
305
306 #ifdef HAS_DVD_DRIVE
307 // delete cached CdInfo if any
308 RemoveCdInfo(TranslateDevicePath(share.strPath, true));
309 RemoveDiscInfo(TranslateDevicePath(share.strPath, true));
310 #endif
311 }
312
313 /////////////////////////////////////////////////////////////
314 // AutoSource status functions:
315 //! @todo translate cdda://<device>/
316
TranslateDevicePath(const std::string & devicePath,bool bReturnAsDevice)317 std::string CMediaManager::TranslateDevicePath(const std::string& devicePath, bool bReturnAsDevice)
318 {
319 CSingleLock waitLock(m_muAutoSource);
320 std::string strDevice = devicePath;
321 // fallback for cdda://local/ and empty devicePath
322 #ifdef HAS_DVD_DRIVE
323 if(devicePath.empty() || StringUtils::StartsWith(devicePath, "cdda://local"))
324 strDevice = m_strFirstAvailDrive;
325 #endif
326
327 #ifdef TARGET_WINDOWS
328 if(!m_bhasoptical)
329 return "";
330
331 if(bReturnAsDevice == false)
332 StringUtils::Replace(strDevice, "\\\\.\\","");
333 else if(!strDevice.empty() && strDevice[1]==':')
334 strDevice = StringUtils::Format("\\\\.\\%c:", strDevice[0]);
335
336 URIUtils::RemoveSlashAtEnd(strDevice);
337 #endif
338 return strDevice;
339 }
340
IsDiscInDrive(const std::string & devicePath)341 bool CMediaManager::IsDiscInDrive(const std::string& devicePath)
342 {
343 #ifdef HAS_DVD_DRIVE
344 #ifdef TARGET_WINDOWS
345 if(!m_bhasoptical)
346 return false;
347
348 std::string strDevice = TranslateDevicePath(devicePath, false);
349 std::map<std::string,CCdInfo*>::iterator it;
350 CSingleLock waitLock(m_muAutoSource);
351 it = m_mapCdInfo.find(strDevice);
352 if(it != m_mapCdInfo.end())
353 return true;
354 else
355 return false;
356 #else
357 if(URIUtils::IsDVD(devicePath) || devicePath.empty())
358 return MEDIA_DETECT::CDetectDVDMedia::IsDiscInDrive(); //! @todo switch all ports to use auto sources
359 else
360 return true; // Assume other paths to be mounted already
361 #endif
362 #else
363 return false;
364 #endif
365 }
366
IsAudio(const std::string & devicePath)367 bool CMediaManager::IsAudio(const std::string& devicePath)
368 {
369 #ifdef HAS_DVD_DRIVE
370 #ifdef TARGET_WINDOWS
371 if(!m_bhasoptical)
372 return false;
373
374 CCdInfo* pCdInfo = GetCdInfo(devicePath);
375 if(pCdInfo != NULL && pCdInfo->IsAudio(1))
376 return true;
377
378 return false;
379 #else
380 //! @todo switch all ports to use auto sources
381 MEDIA_DETECT::CCdInfo* pInfo = MEDIA_DETECT::CDetectDVDMedia::GetCdInfo();
382 if (pInfo != NULL && pInfo->IsAudio(1))
383 return true;
384 #endif
385 #endif
386 return false;
387 }
388
HasOpticalDrive()389 bool CMediaManager::HasOpticalDrive()
390 {
391 #ifdef HAS_DVD_DRIVE
392 if (!m_strFirstAvailDrive.empty())
393 return true;
394 #endif
395 return false;
396 }
397
GetDriveStatus(const std::string & devicePath)398 DWORD CMediaManager::GetDriveStatus(const std::string& devicePath)
399 {
400 #ifdef HAS_DVD_DRIVE
401 #ifdef TARGET_WINDOWS
402 if(!m_bhasoptical)
403 return DRIVE_NOT_READY;
404
405 std::string strDevice = TranslateDevicePath(devicePath, true);
406 DWORD dwRet = DRIVE_NOT_READY;
407 int status = CWIN32Util::GetDriveStatus(strDevice);
408
409 switch(status)
410 {
411 case -1: // error
412 dwRet = DRIVE_NOT_READY;
413 break;
414 case 0: // no media
415 dwRet = DRIVE_CLOSED_NO_MEDIA;
416 break;
417 case 1: // tray open
418 dwRet = DRIVE_OPEN;
419 break;
420 case 2: // media accessible
421 dwRet = DRIVE_CLOSED_MEDIA_PRESENT;
422 break;
423 }
424 return dwRet;
425 #else
426 return MEDIA_DETECT::CDetectDVDMedia::DriveReady();
427 #endif
428 #else
429 return DRIVE_NOT_READY;
430 #endif
431 }
432
433 #ifdef HAS_DVD_DRIVE
GetCdInfo(const std::string & devicePath)434 CCdInfo* CMediaManager::GetCdInfo(const std::string& devicePath)
435 {
436 #ifdef TARGET_WINDOWS
437 if(!m_bhasoptical)
438 return NULL;
439
440 std::string strDevice = TranslateDevicePath(devicePath, false);
441 std::map<std::string,CCdInfo*>::iterator it;
442 {
443 CSingleLock waitLock(m_muAutoSource);
444 it = m_mapCdInfo.find(strDevice);
445 if(it != m_mapCdInfo.end())
446 return it->second;
447 }
448
449 CCdInfo* pCdInfo=NULL;
450 CCdIoSupport cdio;
451 pCdInfo = cdio.GetCdInfo((char*)strDevice.c_str());
452 if(pCdInfo!=NULL)
453 {
454 CSingleLock waitLock(m_muAutoSource);
455 m_mapCdInfo.insert(std::pair<std::string,CCdInfo*>(strDevice,pCdInfo));
456 }
457
458 return pCdInfo;
459 #else
460 return MEDIA_DETECT::CDetectDVDMedia::GetCdInfo();
461 #endif
462 }
463
RemoveCdInfo(const std::string & devicePath)464 bool CMediaManager::RemoveCdInfo(const std::string& devicePath)
465 {
466 if(!m_bhasoptical)
467 return false;
468
469 std::string strDevice = TranslateDevicePath(devicePath, false);
470
471 std::map<std::string,CCdInfo*>::iterator it;
472 CSingleLock waitLock(m_muAutoSource);
473 it = m_mapCdInfo.find(strDevice);
474 if(it != m_mapCdInfo.end())
475 {
476 if(it->second != NULL)
477 delete it->second;
478
479 m_mapCdInfo.erase(it);
480 return true;
481 }
482 return false;
483 }
484
GetDiskLabel(const std::string & devicePath)485 std::string CMediaManager::GetDiskLabel(const std::string& devicePath)
486 {
487 #ifdef TARGET_WINDOWS_STORE
488 return ""; // GetVolumeInformationW nut support in UWP app
489 #elif defined(TARGET_WINDOWS)
490 if(!m_bhasoptical)
491 return "";
492
493 std::string mediaPath = CServiceBroker::GetMediaManager().TranslateDevicePath(devicePath);
494
495 auto cached = m_mapDiscInfo.find(mediaPath);
496 if (cached != m_mapDiscInfo.end())
497 return cached->second.name;
498
499 // try to minimize the chance of a "device not ready" dialog
500 std::string drivePath = CServiceBroker::GetMediaManager().TranslateDevicePath(devicePath, true);
501 if (CServiceBroker::GetMediaManager().GetDriveStatus(drivePath) != DRIVE_CLOSED_MEDIA_PRESENT)
502 return "";
503
504 DiscInfo info;
505 info = GetDiscInfo(mediaPath);
506 if (!info.name.empty())
507 {
508 m_mapDiscInfo[mediaPath] = info;
509 return info.name;
510 }
511
512 std::string strDevice = TranslateDevicePath(devicePath);
513 WCHAR cVolumenName[128];
514 WCHAR cFSName[128];
515 URIUtils::AddSlashAtEnd(strDevice);
516 std::wstring strDeviceW;
517 g_charsetConverter.utf8ToW(strDevice, strDeviceW);
518 if(GetVolumeInformationW(strDeviceW.c_str(), cVolumenName, 127, NULL, NULL, NULL, cFSName, 127)==0)
519 return "";
520 g_charsetConverter.wToUTF8(cVolumenName, strDevice);
521 info.name = StringUtils::TrimRight(strDevice, " ");
522 if (!info.name.empty())
523 m_mapDiscInfo[mediaPath] = info;
524
525 return info.name;
526 #else
527 return MEDIA_DETECT::CDetectDVDMedia::GetDVDLabel();
528 #endif
529 }
530
GetDiskUniqueId(const std::string & devicePath)531 std::string CMediaManager::GetDiskUniqueId(const std::string& devicePath)
532 {
533 std::string mediaPath;
534
535 CCdInfo* pInfo = CServiceBroker::GetMediaManager().GetCdInfo(devicePath);
536 if (pInfo == NULL)
537 return "";
538
539 if (mediaPath.empty() && pInfo->IsAudio(1))
540 mediaPath = "cdda://local/";
541
542 if (mediaPath.empty() && (pInfo->IsISOUDF(1) || pInfo->IsISOHFS(1) || pInfo->IsIso9660(1) || pInfo->IsIso9660Interactive(1)))
543 mediaPath = "iso9660://";
544
545 if (mediaPath.empty())
546 mediaPath = devicePath;
547
548 #ifdef TARGET_WINDOWS
549 if (mediaPath.empty() || mediaPath == "iso9660://")
550 {
551 mediaPath = CServiceBroker::GetMediaManager().TranslateDevicePath(devicePath);
552 }
553 #endif
554
555 DiscInfo info = GetDiscInfo(mediaPath);
556 if (info.empty())
557 {
558 CLog::Log(LOGDEBUG, "GetDiskUniqueId: Retrieving ID for path %s failed, ID is empty.", CURL::GetRedacted(mediaPath).c_str());
559 return "";
560 }
561
562 std::string strID = StringUtils::Format("removable://%s_%s", info.name.c_str(), info.serial.c_str());
563 CLog::Log(LOGDEBUG, "GetDiskUniqueId: Got ID %s for %s with path %s", strID.c_str(), info.type.c_str(), CURL::GetRedacted(mediaPath).c_str());
564
565 return strID;
566 }
567
GetDiscPath()568 std::string CMediaManager::GetDiscPath()
569 {
570 #ifdef TARGET_WINDOWS
571 return CServiceBroker::GetMediaManager().TranslateDevicePath("");
572 #else
573
574 CSingleLock lock(m_CritSecStorageProvider);
575 VECSOURCES drives;
576 m_platformStorage->GetRemovableDrives(drives);
577 for(unsigned i = 0; i < drives.size(); ++i)
578 {
579 if(drives[i].m_iDriveType == CMediaSource::SOURCE_TYPE_DVD && !drives[i].strPath.empty())
580 return drives[i].strPath;
581 }
582
583 // iso9660://, cdda://local/ or D:\ depending on disc type
584 return MEDIA_DETECT::CDetectDVDMedia::GetDVDPath();
585 #endif
586 }
587 #endif
588
SetHasOpticalDrive(bool bstatus)589 void CMediaManager::SetHasOpticalDrive(bool bstatus)
590 {
591 CSingleLock waitLock(m_muAutoSource);
592 m_bhasoptical = bstatus;
593 }
594
Eject(const std::string & mountpath)595 bool CMediaManager::Eject(const std::string& mountpath)
596 {
597 CSingleLock lock(m_CritSecStorageProvider);
598 return m_platformStorage->Eject(mountpath);
599 }
600
EjectTray(const bool bEject,const char cDriveLetter)601 void CMediaManager::EjectTray( const bool bEject, const char cDriveLetter )
602 {
603 #ifdef HAS_DVD_DRIVE
604 #ifdef TARGET_WINDOWS
605 CWIN32Util::EjectTray(cDriveLetter);
606 #else
607 std::shared_ptr<CLibcdio> c_cdio = CLibcdio::GetInstance();
608 char* dvdDevice = c_cdio->GetDeviceFileName();
609 int nRetries=3;
610 while (nRetries-- > 0)
611 {
612 CdIo_t* cdio = c_cdio->cdio_open(dvdDevice, DRIVER_UNKNOWN);
613 if (cdio)
614 {
615 c_cdio->cdio_eject_media(&cdio);
616 c_cdio->cdio_destroy(cdio);
617 }
618 else
619 break;
620 }
621 #endif
622 #endif
623 }
624
CloseTray(const char cDriveLetter)625 void CMediaManager::CloseTray(const char cDriveLetter)
626 {
627 #ifdef HAS_DVD_DRIVE
628 #if defined(TARGET_DARWIN)
629 // FIXME...
630 #elif (defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY))
631 // NYI
632 #elif defined(TARGET_POSIX)
633 char* dvdDevice = CLibcdio::GetInstance()->GetDeviceFileName();
634 if (strlen(dvdDevice) != 0)
635 {
636 int fd = open(dvdDevice, O_RDONLY | O_NONBLOCK);
637 if (fd >= 0)
638 {
639 ioctl(fd, CDROMCLOSETRAY, 0);
640 close(fd);
641 }
642 }
643 #elif defined(TARGET_WINDOWS)
644 CWIN32Util::CloseTray(cDriveLetter);
645 #endif
646 #endif
647 }
648
ToggleTray(const char cDriveLetter)649 void CMediaManager::ToggleTray(const char cDriveLetter)
650 {
651 #ifdef HAS_DVD_DRIVE
652 #if defined(TARGET_WINDOWS)
653 CWIN32Util::ToggleTray(cDriveLetter);
654 #else
655 if (GetDriveStatus() == TRAY_OPEN || GetDriveStatus() == DRIVE_OPEN)
656 CloseTray();
657 else
658 EjectTray();
659 #endif
660 #endif
661 }
662
ProcessEvents()663 void CMediaManager::ProcessEvents()
664 {
665 CSingleLock lock(m_CritSecStorageProvider);
666 if (m_platformStorage->PumpDriveChangeEvents(this))
667 {
668 #if defined(HAS_DVD_DRIVE) && defined(TARGET_DARWIN_OSX)
669 // darwins GetFirstOpticalDeviceFileName only gives us something
670 // when a disc is inserted
671 // so we have to refresh m_strFirstAvailDrive when this happens after Initialize
672 // was called (e.x. the disc was inserted after the start of xbmc)
673 // else TranslateDevicePath wouldn't give the correct device
674 m_strFirstAvailDrive = m_platformStorage->GetFirstOpticalDeviceFileName();
675 #endif
676
677 CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES);
678 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
679 }
680 }
681
GetDiskUsage()682 std::vector<std::string> CMediaManager::GetDiskUsage()
683 {
684 CSingleLock lock(m_CritSecStorageProvider);
685 return m_platformStorage->GetDiskUsage();
686 }
687
OnStorageAdded(const std::string & label,const std::string & path)688 void CMediaManager::OnStorageAdded(const std::string &label, const std::string &path)
689 {
690 #ifdef HAS_DVD_DRIVE
691 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
692 if (settings->GetInt(CSettings::SETTING_AUDIOCDS_AUTOACTION) != AUTOCD_NONE || settings->GetBool(CSettings::SETTING_DVDS_AUTORUN))
693 if (settings->GetInt(CSettings::SETTING_AUDIOCDS_AUTOACTION) == AUTOCD_RIP)
694 CJobManager::GetInstance().AddJob(new CAutorunMediaJob(label, path), this, CJob::PRIORITY_LOW);
695 else
696 CJobManager::GetInstance().AddJob(new CAutorunMediaJob(label, path), this, CJob::PRIORITY_HIGH);
697 else
698 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(13021), label, TOAST_DISPLAY_TIME, false);
699 #endif
700 }
701
OnStorageSafelyRemoved(const std::string & label)702 void CMediaManager::OnStorageSafelyRemoved(const std::string &label)
703 {
704 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(13023), label, TOAST_DISPLAY_TIME, false);
705 }
706
OnStorageUnsafelyRemoved(const std::string & label)707 void CMediaManager::OnStorageUnsafelyRemoved(const std::string &label)
708 {
709 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, g_localizeStrings.Get(13022), label);
710 }
711
GetDiscInfo(const std::string & mediaPath)712 CMediaManager::DiscInfo CMediaManager::GetDiscInfo(const std::string& mediaPath)
713 {
714 DiscInfo info;
715
716 if (mediaPath.empty())
717 return info;
718
719 // Try finding VIDEO_TS/VIDEO_TS.IFO - this indicates a DVD disc is inserted
720 std::string pathVideoTS = URIUtils::AddFileToFolder(mediaPath, "VIDEO_TS", "VIDEO_TS.IFO");
721 // correct the filename if needed
722 if (StringUtils::StartsWith(mediaPath, "dvd://") ||
723 StringUtils::StartsWith(mediaPath, "iso9660://"))
724 {
725 pathVideoTS = TranslateDevicePath("");
726 }
727
728 if (XFILE::CFile::Exists(pathVideoTS))
729 {
730 CFileItem item(pathVideoTS, false);
731 CDVDInputStreamNavigator dvdNavigator(nullptr, item);
732 if (dvdNavigator.Open())
733 {
734 info.type = "DVD";
735 info.name = dvdNavigator.GetDVDTitleString();
736 info.serial = dvdNavigator.GetDVDSerialString();
737 return info;
738 }
739 }
740 #ifdef HAVE_LIBBLURAY
741 // check for Blu-ray discs
742 if (XFILE::CFile::Exists(URIUtils::AddFileToFolder(mediaPath, "BDMV", "index.bdmv")))
743 {
744 info.type = "Blu-ray";
745 CBlurayDirectory bdDir;
746
747 if (!bdDir.InitializeBluray(mediaPath))
748 return info;
749
750 info.name = bdDir.GetBlurayTitle();
751 info.serial = bdDir.GetBlurayID();
752 }
753 #endif
754
755 return info;
756 }
757
RemoveDiscInfo(const std::string & devicePath)758 void CMediaManager::RemoveDiscInfo(const std::string& devicePath)
759 {
760 std::string strDevice = TranslateDevicePath(devicePath, false);
761
762 auto it = m_mapDiscInfo.find(strDevice);
763 if (it != m_mapDiscInfo.end())
764 m_mapDiscInfo.erase(it);
765 }
766
playStubFile(const CFileItem & item)767 bool CMediaManager::playStubFile(const CFileItem& item)
768 {
769 // Figure out Lines 1 and 2 of the dialog
770 std::string strLine1, strLine2;
771
772 // use generic message by default
773 strLine1 = g_localizeStrings.Get(435).c_str();
774 strLine2 = g_localizeStrings.Get(436).c_str();
775
776 CXBMCTinyXML discStubXML;
777 if (discStubXML.LoadFile(item.GetPath()))
778 {
779 TiXmlElement* pRootElement = discStubXML.RootElement();
780 if (!pRootElement || StringUtils::CompareNoCase(pRootElement->Value(), "discstub") != 0)
781 CLog::Log(LOGINFO, "No <discstub> node found for %s. Using default info dialog message", item.GetPath().c_str());
782 else
783 {
784 XMLUtils::GetString(pRootElement, "title", strLine1);
785 XMLUtils::GetString(pRootElement, "message", strLine2);
786 // no title? use the label of the CFileItem as line 1
787 if (strLine1.empty())
788 strLine1 = item.GetLabel();
789 }
790 }
791
792 if (HasOpticalDrive())
793 {
794 #ifdef HAS_DVD_DRIVE
795 if (CGUIDialogPlayEject::ShowAndGetInput(strLine1, strLine2))
796 return MEDIA_DETECT::CAutorun::PlayDiscAskResume();
797 #endif
798 }
799 else
800 {
801 KODI::MESSAGING::HELPERS::ShowOKDialogText(strLine1, strLine2);
802 }
803 return true;
804 }
805