[video] fix selection after changing video or extra art
[xbmc.git] / xbmc / Autorun.cpp
blob6c672b208d7f9d3d1e6ac1072922ec30da62323c
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "Autorun.h"
11 #include "FileItem.h"
12 #include "GUIPassword.h"
13 #include "GUIUserMessages.h"
14 #include "PlayListPlayer.h"
15 #include "ServiceBroker.h"
16 #include "URL.h"
17 #include "application/Application.h"
18 #include "application/ApplicationComponents.h"
19 #include "application/ApplicationPowerHandling.h"
20 #include "cores/playercorefactory/PlayerCoreFactory.h"
21 #include "filesystem/Directory.h"
22 #include "filesystem/DirectoryFactory.h"
23 #include "filesystem/StackDirectory.h"
24 #include "guilib/GUIComponent.h"
25 #include "guilib/GUIWindowManager.h"
26 #include "guilib/LocalizeStrings.h"
27 #include "messaging/helpers/DialogHelper.h"
28 #include "playlists/PlayList.h"
29 #include "profiles/ProfileManager.h"
30 #include "settings/Settings.h"
31 #include "settings/SettingsComponent.h"
32 #include "settings/lib/Setting.h"
33 #include "settings/lib/SettingDefinitions.h"
35 #include <stdlib.h>
36 #ifndef TARGET_WINDOWS
37 #include "storage/DetectDVDType.h"
38 #endif
39 #include "storage/MediaManager.h"
40 #include "utils/FileUtils.h"
41 #include "utils/StringUtils.h"
42 #include "utils/URIUtils.h"
43 #include "utils/Variant.h"
44 #include "utils/log.h"
45 #include "video/VideoDatabase.h"
47 #ifdef HAS_CDDA_RIPPER
48 #include "cdrip/CDDARipper.h"
49 #endif
51 using namespace XFILE;
52 using namespace MEDIA_DETECT;
53 using namespace KODI::MESSAGING;
54 using namespace std::chrono_literals;
56 using KODI::MESSAGING::HELPERS::DialogResponse;
58 CAutorun::CAutorun()
60 m_bEnable = true;
63 CAutorun::~CAutorun() = default;
65 bool CAutorun::ExecuteAutorun(const std::string& path)
67 if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_LOGIN_SCREEN)
68 return false;
70 CCdInfo* pInfo = CServiceBroker::GetMediaManager().GetCdInfo(path);
72 if ( pInfo == NULL )
73 return false;
75 auto& components = CServiceBroker::GetAppComponents();
76 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
77 appPower->ResetScreenSaver();
78 appPower->WakeUpScreenSaverAndDPMS(); // turn off the screensaver if it's active
80 bool success = false;
82 #ifdef HAS_CDDA_RIPPER
83 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_AUDIOCDS_AUTOACTION) == AUTOCD_RIP &&
84 pInfo->IsAudio(1) && !CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetCurrentProfile().musicLocked())
86 success = KODI::CDRIP::CCDDARipper::GetInstance().RipCD();
88 else
89 #endif
91 success = PlayDisc(path, false, false);
92 return success;
95 bool CAutorun::PlayDisc(const std::string& path, bool bypassSettings, bool startFromBeginning)
97 if ( !bypassSettings && CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_AUDIOCDS_AUTOACTION) != AUTOCD_PLAY && !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_DVDS_AUTORUN))
98 return false;
100 int nSize = CServiceBroker::GetPlaylistPlayer().GetPlaylist(PLAYLIST::TYPE_MUSIC).size();
101 int nAddedToPlaylist = 0;
103 std::string mediaPath;
105 CCdInfo* pInfo = CServiceBroker::GetMediaManager().GetCdInfo(path);
106 if (pInfo == NULL)
107 return false;
109 if (pInfo->IsAudio(1))
110 mediaPath = "cdda://local/";
112 if (mediaPath.empty() && (pInfo->IsISOUDF(1) || pInfo->IsISOHFS(1) || pInfo->IsIso9660(1) || pInfo->IsIso9660Interactive(1)))
113 mediaPath = "iso9660://";
115 if (mediaPath.empty())
116 mediaPath = path;
118 if (mediaPath.empty() || mediaPath == "iso9660://")
119 mediaPath = CServiceBroker::GetMediaManager().GetDiscPath();
121 const CURL pathToUrl(mediaPath);
122 std::unique_ptr<IDirectory> pDir ( CDirectoryFactory::Create( pathToUrl ));
123 bool bPlaying = RunDisc(pDir.get(), mediaPath, nAddedToPlaylist, true, bypassSettings, startFromBeginning);
125 if ( !bPlaying && nAddedToPlaylist > 0 )
127 CGUIMessage msg( GUI_MSG_PLAYLIST_CHANGED, 0, 0 );
128 CServiceBroker::GetGUI()->GetWindowManager().SendMessage( msg );
129 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::TYPE_MUSIC);
130 // Start playing the items we inserted
131 return CServiceBroker::GetPlaylistPlayer().Play(nSize, "");
134 return bPlaying;
138 * This method tries to determine what type of disc is located in the given drive and starts to play the content appropriately.
140 bool CAutorun::RunDisc(IDirectory* pDir, const std::string& strDrive, int& nAddedToPlaylist, bool bRoot, bool bypassSettings /* = false */, bool startFromBeginning /* = false */)
142 if (!pDir)
144 CLog::Log(LOGDEBUG, "CAutorun::{}: cannot run disc. is it properly mounted?", __FUNCTION__);
145 return false;
148 bool bPlaying(false);
149 CFileItemList vecItems;
151 CURL pathToUrl{strDrive};
152 // if the url being requested is a generic "iso9660://" we need to enrich it with the actual drive.
153 // use the hostname section to prepend the drive path
154 if (pathToUrl.GetRedacted() == "iso9660://")
156 pathToUrl.Reset();
157 pathToUrl.SetProtocol("iso9660");
158 pathToUrl.SetHostName(CServiceBroker::GetMediaManager().TranslateDevicePath(""));
161 if ( !pDir->GetDirectory( pathToUrl, vecItems ) )
163 return false;
166 // Sorting necessary for easier HDDVD handling
167 vecItems.Sort(SortByLabel, SortOrderAscending);
169 bool bAllowVideo = true;
170 // bool bAllowPictures = true;
171 bool bAllowMusic = true;
172 if (!g_passwordManager.IsMasterLockUnlocked(false))
174 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
176 bAllowVideo = !profileManager->GetCurrentProfile().videoLocked();
177 // bAllowPictures = !profileManager->GetCurrentProfile().picturesLocked();
178 bAllowMusic = !profileManager->GetCurrentProfile().musicLocked();
181 // is this a root folder we have to check the content to determine a disc type
182 if (bRoot)
184 std::string hddvdname = "";
185 CFileItemPtr phddvdItem;
186 bool bAutorunDVDs = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_DVDS_AUTORUN);
188 // check root folders next, for normal structured dvd's
189 for (const auto& pItem : vecItems)
191 // is the current item a (non system) folder?
192 if (pItem->m_bIsFolder && pItem->GetPath() != "." && pItem->GetPath() != "..")
194 std::string name = pItem->GetPath();
195 URIUtils::RemoveSlashAtEnd(name);
196 name = URIUtils::GetFileName(name);
198 // Check if the current foldername indicates a DVD structure (name is "VIDEO_TS")
199 if (StringUtils::EqualsNoCase(name, "VIDEO_TS") && bAllowVideo
200 && (bypassSettings || bAutorunDVDs))
202 std::string path = URIUtils::AddFileToFolder(pItem->GetPath(), "VIDEO_TS.IFO");
203 if (!CFileUtils::Exists(path))
204 path = URIUtils::AddFileToFolder(pItem->GetPath(), "video_ts.ifo");
205 CFileItemPtr item(new CFileItem(path, false));
206 item->SetLabel(CServiceBroker::GetMediaManager().GetDiskLabel(strDrive));
207 item->GetVideoInfoTag()->m_strFileNameAndPath =
208 CServiceBroker::GetMediaManager().GetDiskUniqueId(strDrive);
210 if (!startFromBeginning && !item->GetVideoInfoTag()->m_strFileNameAndPath.empty())
211 item->SetStartOffset(STARTOFFSET_RESUME);
213 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST::TYPE_VIDEO);
214 CServiceBroker::GetPlaylistPlayer().SetShuffle(PLAYLIST::TYPE_VIDEO, false);
215 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::TYPE_VIDEO, item);
216 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::TYPE_VIDEO);
217 CServiceBroker::GetPlaylistPlayer().Play(0, "");
218 return true;
221 // Check if the current foldername indicates a Blu-Ray structure (default is "BDMV").
222 // A BR should also include an "AACS" folder for encryption, Sony-BRs can also include update folders for PS3 (PS3_UPDATE / PS3_VPRM).
223 //! @todo for the time being, the DVD autorun settings are used to determine if the BR should be started automatically.
224 if (StringUtils::EqualsNoCase(name, "BDMV") && bAllowVideo
225 && (bypassSettings || bAutorunDVDs))
227 CFileItemPtr item(new CFileItem(URIUtils::AddFileToFolder(pItem->GetPath(), "index.bdmv"), false));
228 item->SetLabel(CServiceBroker::GetMediaManager().GetDiskLabel(strDrive));
229 item->GetVideoInfoTag()->m_strFileNameAndPath =
230 CServiceBroker::GetMediaManager().GetDiskUniqueId(strDrive);
232 if (!startFromBeginning && !item->GetVideoInfoTag()->m_strFileNameAndPath.empty())
233 item->SetStartOffset(STARTOFFSET_RESUME);
235 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST::TYPE_VIDEO);
236 CServiceBroker::GetPlaylistPlayer().SetShuffle(PLAYLIST::TYPE_VIDEO, false);
237 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::TYPE_VIDEO, item);
238 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::TYPE_VIDEO);
239 CServiceBroker::GetPlaylistPlayer().Play(0, "");
240 return true;
243 // Check if the current foldername indicates a HD DVD structure (default is "HVDVD_TS").
244 // Most HD DVD will also include an "ADV_OBJ" folder for advanced content. This folder should be handled first.
245 //! @todo for the time being, the DVD autorun settings are used to determine if the HD DVD should be started automatically.
246 CFileItemList items, sitems;
248 // Advanced Content HD DVD (most discs?)
249 if (StringUtils::EqualsNoCase(name, "ADV_OBJ"))
251 CLog::Log(LOGINFO,"HD DVD: Checking for playlist.");
252 // find playlist file
253 CDirectory::GetDirectory(pItem->GetPath(), items, "*.xpl", DIR_FLAG_DEFAULTS);
254 if (items.Size())
256 // HD DVD Standard says the highest numbered playlist has to be handled first.
257 CLog::Log(LOGINFO,"HD DVD: Playlist found. Set filetypes to *.xpl for external player.");
258 items.Sort(SortByLabel, SortOrderDescending);
259 phddvdItem = pItem;
260 hddvdname = URIUtils::GetFileName(items[0]->GetPath());
261 CLog::Log(LOGINFO, "HD DVD: {}", items[0]->GetPath());
265 // Standard Content HD DVD (few discs?)
266 if (StringUtils::EqualsNoCase(name, "HVDVD_TS") && bAllowVideo
267 && (bypassSettings || bAutorunDVDs))
269 if (hddvdname == "")
271 CLog::Log(LOGINFO,"HD DVD: Checking for ifo.");
272 // find Video Manager or Title Set Information
273 CDirectory::GetDirectory(pItem->GetPath(), items, "HV*.ifo", DIR_FLAG_DEFAULTS);
274 if (items.Size())
276 // HD DVD Standard says the lowest numbered ifo has to be handled first.
277 CLog::Log(LOGINFO,"HD DVD: IFO found. Set filename to HV* and filetypes to *.ifo for external player.");
278 items.Sort(SortByLabel, SortOrderAscending);
279 phddvdItem = pItem;
280 hddvdname = URIUtils::GetFileName(items[0]->GetPath());
281 CLog::Log(LOGINFO, "HD DVD: {}", items[0]->GetPath());
284 // Find and sort *.evo files for internal playback.
285 // While this algorithm works for all of my HD DVDs, it may fail on other discs. If there are very large extras which are
286 // alphabetically before the main movie they will be sorted to the top of the playlist and get played first.
287 CDirectory::GetDirectory(pItem->GetPath(), items, "*.evo", DIR_FLAG_DEFAULTS);
288 if (items.Size())
290 // Sort *.evo files in alphabetical order.
291 items.Sort(SortByLabel, SortOrderAscending);
292 int64_t asize = 0;
293 int ecount = 0;
294 // calculate average size of elements above 1gb
295 for (int j = 0; j < items.Size(); j++)
296 if (items[j]->m_dwSize > 1000000000)
298 ecount++;
299 asize = asize + items[j]->m_dwSize;
301 if (ecount > 0)
302 asize = asize / ecount;
303 // Put largest files in alphabetical order to top of new list.
304 for (int j = 0; j < items.Size(); j++)
305 if (items[j]->m_dwSize >= asize)
306 sitems.Add (items[j]);
307 // Sort *.evo files by size.
308 items.Sort(SortBySize, SortOrderDescending);
309 // Add other files with descending size to bottom of new list.
310 for (int j = 0; j < items.Size(); j++)
311 if (items[j]->m_dwSize < asize)
312 sitems.Add (items[j]);
313 // Replace list with optimized list.
314 items.Clear();
315 items.Copy (sitems);
316 sitems.Clear();
318 if (hddvdname != "")
320 CFileItem item(URIUtils::AddFileToFolder(phddvdItem->GetPath(), hddvdname), false);
321 item.SetLabel(CServiceBroker::GetMediaManager().GetDiskLabel(strDrive));
322 item.GetVideoInfoTag()->m_strFileNameAndPath =
323 CServiceBroker::GetMediaManager().GetDiskUniqueId(strDrive);
325 if (!startFromBeginning && !item.GetVideoInfoTag()->m_strFileNameAndPath.empty())
326 item.SetStartOffset(STARTOFFSET_RESUME);
328 // get playername
329 std::string hdVideoPlayer = CServiceBroker::GetPlayerCoreFactory().GetDefaultPlayer(item);
331 // Single *.xpl or *.ifo files require an external player to handle playback.
332 // If no matching rule was found, VideoPlayer will be default player.
333 if (hdVideoPlayer != "VideoPlayer")
335 CLog::Log(LOGINFO, "HD DVD: External singlefile playback initiated: {}", hddvdname);
336 g_application.PlayFile(item, hdVideoPlayer, false);
337 return true;
338 } else
339 CLog::Log(LOGINFO,"HD DVD: No external player found. Fallback to internal one.");
342 // internal *.evo playback.
343 CLog::Log(LOGINFO,"HD DVD: Internal multifile playback initiated.");
344 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST::TYPE_VIDEO);
345 CServiceBroker::GetPlaylistPlayer().SetShuffle(PLAYLIST::TYPE_VIDEO, false);
346 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::TYPE_VIDEO, items);
347 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::TYPE_VIDEO);
348 CServiceBroker::GetPlaylistPlayer().Play(0, "");
349 return true;
352 // Video CDs can have multiple file formats. First we need to determine which one is used on the CD
353 std::string strExt;
354 if (StringUtils::EqualsNoCase(name, "MPEGAV"))
355 strExt = ".dat";
356 if (StringUtils::EqualsNoCase(name, "MPEG2"))
357 strExt = ".mpg";
359 // If a file format was extracted we are sure this is a VCD. Autoplay if settings indicate we should.
360 if (!strExt.empty() && bAllowVideo
361 && (bypassSettings || bAutorunDVDs))
363 CFileItemList items;
364 CDirectory::GetDirectory(pItem->GetPath(), items, strExt, DIR_FLAG_DEFAULTS);
365 if (items.Size())
367 items.Sort(SortByLabel, SortOrderAscending);
368 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST::TYPE_VIDEO);
369 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::TYPE_VIDEO, items);
370 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::TYPE_VIDEO);
371 CServiceBroker::GetPlaylistPlayer().Play(0, "");
372 return true;
375 /* Probably want this if/when we add some automedia action dialog...
376 else if (pItem->GetPath().Find("PICTURES") != -1 && bAllowPictures
377 && (bypassSettings))
379 bPlaying = true;
380 std::string strExec = StringUtils::Format("RecursiveSlideShow({})", pItem->GetPath());
381 CBuiltins::Execute(strExec);
382 return true;
389 // check video first
390 if (!nAddedToPlaylist && !bPlaying && (bypassSettings || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_DVDS_AUTORUN)))
392 // stack video files
393 CFileItemList tempItems;
394 tempItems.Append(vecItems);
395 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MYVIDEOS_STACKVIDEOS))
396 tempItems.Stack();
397 CFileItemList itemlist;
399 for (int i = 0; i < tempItems.Size(); i++)
401 CFileItemPtr pItem = tempItems[i];
402 if (!pItem->m_bIsFolder && pItem->IsVideo())
404 bPlaying = true;
405 if (pItem->IsStack())
407 //! @todo remove this once the app/player is capable of handling stacks immediately
408 CStackDirectory dir;
409 CFileItemList items;
410 dir.GetDirectory(pItem->GetURL(), items);
411 itemlist.Append(items);
413 else
414 itemlist.Add(pItem);
417 if (itemlist.Size())
419 if (!bAllowVideo)
421 if (!bypassSettings)
422 return false;
424 if (!g_passwordManager.IsMasterLockUnlocked(true))
425 return false;
427 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST::TYPE_VIDEO);
428 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::TYPE_VIDEO, itemlist);
429 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::TYPE_VIDEO);
430 CServiceBroker::GetPlaylistPlayer().Play(0, "");
433 // then music
434 if (!bPlaying && (bypassSettings || CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_AUDIOCDS_AUTOACTION) == AUTOCD_PLAY) && bAllowMusic)
436 for (int i = 0; i < vecItems.Size(); i++)
438 CFileItemPtr pItem = vecItems[i];
439 if (!pItem->m_bIsFolder && pItem->IsAudio())
441 nAddedToPlaylist++;
442 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::TYPE_MUSIC, pItem);
446 /* Probably want this if/when we add some automedia action dialog...
447 // and finally pictures
448 if (!nAddedToPlaylist && !bPlaying && bypassSettings && bAllowPictures)
450 for (int i = 0; i < vecItems.Size(); i++)
452 CFileItemPtr pItem = vecItems[i];
453 if (!pItem->m_bIsFolder && pItem->IsPicture())
455 bPlaying = true;
456 std::string strExec = StringUtils::Format("RecursiveSlideShow({})", strDrive);
457 CBuiltins::Execute(strExec);
458 break;
464 // check subdirs if we are not playing yet
465 if (!bPlaying)
467 for (int i = 0; i < vecItems.Size(); i++)
469 CFileItemPtr pItem = vecItems[i];
470 if (pItem->m_bIsFolder)
472 if (pItem->GetPath() != "." && pItem->GetPath() != ".." )
474 if (RunDisc(pDir, pItem->GetPath(), nAddedToPlaylist, false, bypassSettings, startFromBeginning))
476 bPlaying = true;
477 break;
480 } // if (non system) folder
481 } // for all items in directory
482 } // if root folder
484 return bPlaying;
487 void CAutorun::HandleAutorun()
489 #if !defined(TARGET_WINDOWS) && defined(HAS_OPTICAL_DRIVE)
490 const CDetectDVDMedia& mediadetect = CServiceBroker::GetDetectDVDMedia();
492 if (!m_bEnable)
494 mediadetect.m_evAutorun.Reset();
495 return ;
498 if (mediadetect.m_evAutorun.Wait(0ms))
500 if (!ExecuteAutorun(""))
501 CLog::Log(LOGDEBUG, "{}: Could not execute autorun", __func__);
502 mediadetect.m_evAutorun.Reset();
504 #endif
507 void CAutorun::Enable()
509 m_bEnable = true;
512 void CAutorun::Disable()
514 m_bEnable = false;
517 bool CAutorun::IsEnabled() const
519 return m_bEnable;
522 bool CAutorun::PlayDiscAskResume(const std::string& path)
524 return PlayDisc(path, true,
525 !CanResumePlayDVD(path) ||
526 HELPERS::ShowYesNoDialogText(CVariant{341}, CVariant{""}, CVariant{13404},
527 CVariant{12021}) == DialogResponse::CHOICE_YES);
530 bool CAutorun::CanResumePlayDVD(const std::string& path)
532 std::string strUniqueId = CServiceBroker::GetMediaManager().GetDiskUniqueId(path);
533 if (!strUniqueId.empty())
535 CVideoDatabase dbs;
536 dbs.Open();
537 CBookmark bookmark;
538 if (dbs.GetResumeBookMark(strUniqueId, bookmark))
539 return true;
541 return false;
544 void CAutorun::SettingOptionAudioCdActionsFiller(const SettingConstPtr& setting,
545 std::vector<IntegerSettingOption>& list,
546 int& current,
547 void* data)
549 list.emplace_back(g_localizeStrings.Get(16018), AUTOCD_NONE);
550 list.emplace_back(g_localizeStrings.Get(14098), AUTOCD_PLAY);
551 #ifdef HAS_CDDA_RIPPER
552 list.emplace_back(g_localizeStrings.Get(14096), AUTOCD_RIP);
553 #endif