/* * Copyright (C) 2005-2013 Team XBMC * http://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 * . * */ #include "system.h" #ifdef HAS_DVD_DRIVE #include "Autorun.h" #include "Application.h" #include "GUIPassword.h" #include "GUIUserMessages.h" #include "PlayListPlayer.h" #include "filesystem/StackDirectory.h" #include "filesystem/Directory.h" #include "filesystem/DirectoryFactory.h" #include "filesystem/File.h" #include "profiles/ProfilesManager.h" #include "settings/Settings.h" #include "playlists/PlayList.h" #include "guilib/GUIWindowManager.h" #include "guilib/LocalizeStrings.h" #include "storage/MediaManager.h" #include "video/VideoDatabase.h" #include "dialogs/GUIDialogYesNo.h" #include "utils/URIUtils.h" #include "utils/log.h" #include "video/windows/GUIWindowVideoBase.h" #ifdef HAS_CDDA_RIPPER #include "cdrip/CDDARipper.h" #endif using namespace std; using namespace XFILE; using namespace PLAYLIST; using namespace MEDIA_DETECT; CAutorun::CAutorun() { m_bEnable = true; } CAutorun::~CAutorun() {} void CAutorun::ExecuteAutorun(const CStdString& path, bool bypassSettings, bool ignoreplaying, bool startFromBeginning ) { if ((!ignoreplaying && (g_application.m_pPlayer->IsPlayingAudio() || g_application.m_pPlayer->IsPlayingVideo() || g_windowManager.HasModalDialog())) || g_windowManager.GetActiveWindow() == WINDOW_LOGIN_SCREEN) return ; CCdInfo* pInfo = g_mediaManager.GetCdInfo(path); if ( pInfo == NULL ) return ; g_application.ResetScreenSaver(); g_application.WakeUpScreenSaverAndDPMS(); // turn off the screensaver if it's active #ifdef HAS_CDDA_RIPPER if (CSettings::Get().GetInt("audiocds.autoaction") == AUTOCD_RIP && pInfo->IsAudio(1) && !CProfilesManager::Get().GetCurrentProfile().musicLocked()) { CCDDARipper::GetInstance().RipCD(); } else #endif PlayDisc(path, bypassSettings, startFromBeginning); } bool CAutorun::PlayDisc(const CStdString& path, bool bypassSettings, bool startFromBeginning) { if ( !bypassSettings && CSettings::Get().GetInt("audiocds.autoaction") != AUTOCD_PLAY && !CSettings::Get().GetBool("dvds.autorun")) return false; int nSize = g_playlistPlayer.GetPlaylist( PLAYLIST_MUSIC ).size(); int nAddedToPlaylist = 0; CStdString mediaPath; CCdInfo* pInfo = g_mediaManager.GetCdInfo(path); if (pInfo == NULL) return false; if (mediaPath.empty() && pInfo->IsAudio(1)) mediaPath = "cdda://local/"; if (mediaPath.empty() && (pInfo->IsISOUDF(1) || pInfo->IsISOHFS(1) || pInfo->IsIso9660(1) || pInfo->IsIso9660Interactive(1))) mediaPath = "iso9660://"; if (mediaPath.empty()) mediaPath = path; #ifdef TARGET_WINDOWS if (mediaPath.empty() || mediaPath == "iso9660://") mediaPath = g_mediaManager.TranslateDevicePath(""); #endif const CURL pathToUrl(mediaPath); auto_ptr pDir ( CDirectoryFactory::Create( pathToUrl )); bool bPlaying = RunDisc(pDir.get(), mediaPath, nAddedToPlaylist, true, bypassSettings, startFromBeginning); if ( !bPlaying && nAddedToPlaylist > 0 ) { CGUIMessage msg( GUI_MSG_PLAYLIST_CHANGED, 0, 0 ); g_windowManager.SendMessage( msg ); g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_MUSIC); // Start playing the items we inserted return g_playlistPlayer.Play(nSize); } return bPlaying; } /** * This method tries to determine what type of disc is located in the given drive and starts to play the content appropriately. */ bool CAutorun::RunDisc(IDirectory* pDir, const CStdString& strDrive, int& nAddedToPlaylist, bool bRoot, bool bypassSettings /* = false */, bool startFromBeginning /* = false */) { bool bPlaying(false); CFileItemList vecItems; const CURL pathToUrl(strDrive); if ( !pDir->GetDirectory( pathToUrl, vecItems ) ) { return false; } // Sorting necessary for easier HDDVD handling vecItems.Sort(SortByLabel, SortOrderAscending); bool bAllowVideo = true; // bool bAllowPictures = true; bool bAllowMusic = true; if (!g_passwordManager.IsMasterLockUnlocked(false)) { bAllowVideo = !CProfilesManager::Get().GetCurrentProfile().videoLocked(); // bAllowPictures = !CProfilesManager::Get().GetCurrentProfile().picturesLocked(); bAllowMusic = !CProfilesManager::Get().GetCurrentProfile().musicLocked(); } // is this a root folder we have to check the content to determine a disc type if( bRoot ) { CStdString hddvdname = ""; CFileItemPtr phddvdItem; // check root folders next, for normal structured dvd's for (int i = 0; i < vecItems.Size(); i++) { CFileItemPtr pItem = vecItems[i]; // is the current item a (non system) folder? if (pItem->m_bIsFolder && pItem->GetPath() != "." && pItem->GetPath() != "..") { CStdString name = pItem->GetPath(); URIUtils::RemoveSlashAtEnd(name); name = URIUtils::GetFileName(name); // Check if the current foldername indicates a DVD structure (name is "VIDEO_TS") if (name.Equals("VIDEO_TS") && bAllowVideo && (bypassSettings || CSettings::Get().GetBool("dvds.autorun"))) { CStdString path = URIUtils::AddFileToFolder(pItem->GetPath(), "VIDEO_TS.IFO"); if(!CFile::Exists(path)) path = URIUtils::AddFileToFolder(pItem->GetPath(), "video_ts.ifo"); CFileItemPtr item(new CFileItem(path, false)); item->SetLabel(g_mediaManager.GetDiskLabel(strDrive)); item->GetVideoInfoTag()->m_strFileNameAndPath = g_mediaManager.GetDiskUniqueId(strDrive); if (!startFromBeginning && !item->GetVideoInfoTag()->m_strFileNameAndPath.empty()) item->m_lStartOffset = STARTOFFSET_RESUME; g_playlistPlayer.ClearPlaylist(PLAYLIST_VIDEO); g_playlistPlayer.SetShuffle (PLAYLIST_VIDEO, false); g_playlistPlayer.Add(PLAYLIST_VIDEO, item); g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_VIDEO); g_playlistPlayer.Play(0); return true; } // Check if the current foldername indicates a Blu-Ray structure (default is "BDMV"). // A BR should also include an "AACS" folder for encryption, Sony-BRs can also include update folders for PS3 (PS3_UPDATE / PS3_VPRM). // ToDo: for the time beeing, the DVD autorun settings are used to determine if the BR should be started automatically. if (name.Equals("BDMV") && bAllowVideo && (bypassSettings || CSettings::Get().GetBool("dvds.autorun"))) { CFileItemPtr item(new CFileItem(URIUtils::AddFileToFolder(pItem->GetPath(), "index.bdmv"), false)); item->SetLabel(g_mediaManager.GetDiskLabel(strDrive)); item->GetVideoInfoTag()->m_strFileNameAndPath = g_mediaManager.GetDiskUniqueId(strDrive); if (!startFromBeginning && !item->GetVideoInfoTag()->m_strFileNameAndPath.empty()) item->m_lStartOffset = STARTOFFSET_RESUME; g_playlistPlayer.ClearPlaylist(PLAYLIST_VIDEO); g_playlistPlayer.SetShuffle (PLAYLIST_VIDEO, false); if (!CGUIWindowVideoBase::ShowPlaySelection(item)) return false; g_playlistPlayer.Add(PLAYLIST_VIDEO, item); g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_VIDEO); g_playlistPlayer.Play(0); return true; } // Check if the current foldername indicates a HD DVD structure (default is "HVDVD_TS"). // Most HD DVD will also include an "ADV_OBJ" folder for advanced content. This folder should be handled first. // ToDo: for the time beeing, the DVD autorun settings are used to determine if the HD DVD should be started automatically. CFileItemList items, sitems; // Advanced Content HD DVD (most discs?) if (name.Equals("ADV_OBJ")) { CLog::Log(LOGINFO,"HD DVD: Checking for playlist."); // find playlist file CDirectory::GetDirectory(pItem->GetPath(), items, "*.xpl"); if (items.Size()) { // HD DVD Standard says the highest numbered playlist has to be handled first. CLog::Log(LOGINFO,"HD DVD: Playlist found. Set filetypes to *.xpl for external player."); items.Sort(SortByLabel, SortOrderDescending); phddvdItem = pItem; hddvdname = URIUtils::GetFileName(items[0]->GetPath()); CLog::Log(LOGINFO,"HD DVD: %s", items[0]->GetPath().c_str()); } } // Standard Content HD DVD (few discs?) if (name.Equals("HVDVD_TS") && bAllowVideo && (bypassSettings || CSettings::Get().GetBool("dvds.autorun"))) { if (hddvdname == "") { CLog::Log(LOGINFO,"HD DVD: Checking for ifo."); // find Video Manager or Title Set Information CDirectory::GetDirectory(pItem->GetPath(), items, "HV*.ifo"); if (items.Size()) { // HD DVD Standard says the lowest numbered ifo has to be handled first. CLog::Log(LOGINFO,"HD DVD: IFO found. Set filename to HV* and filetypes to *.ifo for external player."); items.Sort(SortByLabel, SortOrderAscending); phddvdItem = pItem; hddvdname = URIUtils::GetFileName(items[0]->GetPath()); CLog::Log(LOGINFO,"HD DVD: %s",items[0]->GetPath().c_str()); } } // Find and sort *.evo files for internal playback. // While this algorithm works for all of my HD DVDs, it may fail on other discs. If there are very large extras which are // alphabetically before the main movie they will be sorted to the top of the playlist and get played first. CDirectory::GetDirectory(pItem->GetPath(), items, "*.evo"); if (items.Size()) { // Sort *.evo files in alphabetical order. items.Sort(SortByLabel, SortOrderAscending); int64_t asize = 0; int ecount = 0; // calculate average size of elements above 1gb for (int j = 0; j < items.Size(); j++) if (items[j]->m_dwSize > 1000000000) { ecount++; asize = asize + items[j]->m_dwSize; } asize = asize / ecount; // Put largest files in alphabetical order to top of new list. for (int j = 0; j < items.Size(); j++) if (items[j]->m_dwSize >= asize) sitems.Add (items[j]); // Sort *.evo files by size. items.Sort(SortBySize, SortOrderDescending); // Add other files with descending size to bottom of new list. for (int j = 0; j < items.Size(); j++) if (items[j]->m_dwSize < asize) sitems.Add (items[j]); // Replace list with optimized list. items.Clear(); items.Copy (sitems); sitems.Clear(); } if (hddvdname != "") { CFileItem item(URIUtils::AddFileToFolder(phddvdItem->GetPath(), hddvdname), false); item.SetLabel(g_mediaManager.GetDiskLabel(strDrive)); item.GetVideoInfoTag()->m_strFileNameAndPath = g_mediaManager.GetDiskUniqueId(strDrive); if (!startFromBeginning && !item.GetVideoInfoTag()->m_strFileNameAndPath.empty()) item.m_lStartOffset = STARTOFFSET_RESUME; // get playername CStdString hddvdplayer = CPlayerCoreFactory::Get().GetPlayerName(CPlayerCoreFactory::Get().GetDefaultPlayer(item)); // Single *.xpl or *.ifo files require an external player to handle playback. // If no matching rule was found, DVDPlayer will be default player. if (hddvdplayer != "DVDPlayer") { CLog::Log(LOGINFO,"HD DVD: External singlefile playback initiated: %s",hddvdname.c_str()); g_application.PlayFile(item, false); return true; } else CLog::Log(LOGINFO,"HD DVD: No external player found. Fallback to internal one."); } // internal *.evo playback. CLog::Log(LOGINFO,"HD DVD: Internal multifile playback initiated."); g_playlistPlayer.ClearPlaylist(PLAYLIST_VIDEO); g_playlistPlayer.SetShuffle (PLAYLIST_VIDEO, false); g_playlistPlayer.Add(PLAYLIST_VIDEO, items); g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_VIDEO); g_playlistPlayer.Play(0); return true; } // Video CDs can have multiple file formats. First we need to determine which one is used on the CD CStdString strExt; if (name.Equals("MPEGAV")) strExt = ".dat"; if (name.Equals("MPEG2")) strExt = ".mpg"; // If a file format was extracted we are sure this is a VCD. Autoplay if settings indicate we should. if (!strExt.empty() && bAllowVideo && (bypassSettings || CSettings::Get().GetBool("dvds.autorun"))) { CFileItemList items; CDirectory::GetDirectory(pItem->GetPath(), items, strExt); if (items.Size()) { items.Sort(SortByLabel, SortOrderAscending); g_playlistPlayer.ClearPlaylist(PLAYLIST_VIDEO); g_playlistPlayer.Add(PLAYLIST_VIDEO, items); g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_VIDEO); g_playlistPlayer.Play(0); return true; } } /* Probably want this if/when we add some automedia action dialog... else if (pItem->GetPath().Find("PICTURES") != -1 && bAllowPictures && (bypassSettings)) { bPlaying = true; CStdString strExec = StringUtils::Format("RecursiveSlideShow(%s)", pItem->GetPath().c_str()); CBuiltins::Execute(strExec); return true; } */ } } } // check video first if (!nAddedToPlaylist && !bPlaying && (bypassSettings || CSettings::Get().GetBool("dvds.autorun"))) { // stack video files CFileItemList tempItems; tempItems.Append(vecItems); if (CSettings::Get().GetBool("myvideos.stackvideos")) tempItems.Stack(); CFileItemList itemlist; for (int i = 0; i < tempItems.Size(); i++) { CFileItemPtr pItem = tempItems[i]; if (!pItem->m_bIsFolder && pItem->IsVideo()) { bPlaying = true; if (pItem->IsStack()) { // TODO: remove this once the app/player is capable of handling stacks immediately CStackDirectory dir; CFileItemList items; dir.GetDirectory(pItem->GetURL(), items); itemlist.Append(items); } else itemlist.Add(pItem); } } if (itemlist.Size()) { if (!bAllowVideo) { if (!bypassSettings) return false; if (g_windowManager.GetActiveWindow() != WINDOW_VIDEO_FILES) if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; } g_playlistPlayer.ClearPlaylist(PLAYLIST_VIDEO); g_playlistPlayer.Add(PLAYLIST_VIDEO, itemlist); g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_VIDEO); g_playlistPlayer.Play(0); } } // then music if (!bPlaying && (bypassSettings || CSettings::Get().GetInt("audiocds.autoaction") == AUTOCD_PLAY) && bAllowMusic) { for (int i = 0; i < vecItems.Size(); i++) { CFileItemPtr pItem = vecItems[i]; if (!pItem->m_bIsFolder && pItem->IsAudio()) { nAddedToPlaylist++; g_playlistPlayer.Add(PLAYLIST_MUSIC, pItem); } } } /* Probably want this if/when we add some automedia action dialog... // and finally pictures if (!nAddedToPlaylist && !bPlaying && bypassSettings && bAllowPictures) { for (int i = 0; i < vecItems.Size(); i++) { CFileItemPtr pItem = vecItems[i]; if (!pItem->m_bIsFolder && pItem->IsPicture()) { bPlaying = true; CStdString strExec = StringUtils::Format("RecursiveSlideShow(%s)", strDrive.c_str()); CBuiltins::Execute(strExec); break; } } } */ // check subdirs if we are not playing yet if (!bPlaying) { for (int i = 0; i < vecItems.Size(); i++) { CFileItemPtr pItem = vecItems[i]; if (pItem->m_bIsFolder) { if (pItem->GetPath() != "." && pItem->GetPath() != ".." ) { if (RunDisc(pDir, pItem->GetPath(), nAddedToPlaylist, false, bypassSettings, startFromBeginning)) { bPlaying = true; break; } } } // if (non system) folder } // for all items in directory } // if root folder return bPlaying; } void CAutorun::HandleAutorun() { #ifndef TARGET_WINDOWS if (!m_bEnable) { CDetectDVDMedia::m_evAutorun.Reset(); return ; } if (CDetectDVDMedia::m_evAutorun.WaitMSec(0)) { ExecuteAutorun(); CDetectDVDMedia::m_evAutorun.Reset(); } #endif } void CAutorun::Enable() { m_bEnable = true; } void CAutorun::Disable() { m_bEnable = false; } bool CAutorun::IsEnabled() const { return m_bEnable; } bool CAutorun::PlayDiscAskResume(const CStdString& path) { return PlayDisc(path, true, !CanResumePlayDVD(path) || CGUIDialogYesNo::ShowAndGetInput(341, -1, -1, -1, 13404, 12021)); } bool CAutorun::CanResumePlayDVD(const CStdString& path) { CStdString strUniqueId = g_mediaManager.GetDiskUniqueId(path); if (!strUniqueId.empty()) { CVideoDatabase dbs; dbs.Open(); CBookmark bookmark; if (dbs.GetResumeBookMark(strUniqueId, bookmark)) return true; } return false; } void CAutorun::SettingOptionAudioCdActionsFiller(const CSetting *setting, std::vector< std::pair > &list, int ¤t, void *data) { list.push_back(make_pair(g_localizeStrings.Get(16018), AUTOCD_NONE)); list.push_back(make_pair(g_localizeStrings.Get(14098), AUTOCD_PLAY)); #ifdef HAS_CDDA_RIPPER list.push_back(make_pair(g_localizeStrings.Get(14096), AUTOCD_RIP)); #endif } #endif