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 "Autorun.h"
10
11 #include <stdlib.h>
12
13 #include "Application.h"
14 #include "GUIPassword.h"
15 #include "GUIUserMessages.h"
16 #include "PlayListPlayer.h"
17 #include "ServiceBroker.h"
18 #include "cores/playercorefactory/PlayerCoreFactory.h"
19 #include "filesystem/StackDirectory.h"
20 #include "filesystem/Directory.h"
21 #include "filesystem/DirectoryFactory.h"
22 #include "filesystem/File.h"
23 #include "messaging/ApplicationMessenger.h"
24 #include "messaging/helpers/DialogHelper.h"
25 #include "profiles/ProfileManager.h"
26 #include "settings/Settings.h"
27 #include "settings/SettingsComponent.h"
28 #include "settings/lib/Setting.h"
29 #include "settings/lib/SettingDefinitions.h"
30 #include "playlists/PlayList.h"
31 #include "guilib/GUIComponent.h"
32 #include "guilib/GUIWindowManager.h"
33 #include "guilib/LocalizeStrings.h"
34 #include "storage/MediaManager.h"
35 #include "video/VideoDatabase.h"
36 #include "utils/StringUtils.h"
37 #include "utils/URIUtils.h"
38 #include "utils/log.h"
39 #include "utils/Variant.h"
40
41 #ifdef HAS_CDDA_RIPPER
42 #include "cdrip/CDDARipper.h"
43 #endif
44
45 using namespace XFILE;
46 using namespace PLAYLIST;
47 using namespace MEDIA_DETECT;
48 using namespace KODI::MESSAGING;
49
50 using KODI::MESSAGING::HELPERS::DialogResponse;
51
CAutorun()52 CAutorun::CAutorun()
53 {
54 m_bEnable = true;
55 }
56
57 CAutorun::~CAutorun() = default;
58
ExecuteAutorun(const std::string & path,bool bypassSettings,bool ignoreplaying,bool startFromBeginning)59 void CAutorun::ExecuteAutorun(const std::string& path, bool bypassSettings, bool ignoreplaying, bool startFromBeginning )
60 {
61 if (!ignoreplaying)
62 {
63 if (g_application.GetAppPlayer().IsPlayingAudio() ||
64 g_application.GetAppPlayer().IsPlayingVideo() ||
65 CServiceBroker::GetGUI()->GetWindowManager().HasModalDialog(true))
66 {
67 return;
68 }
69 }
70
71 if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_LOGIN_SCREEN)
72 return;
73
74 CCdInfo* pInfo = CServiceBroker::GetMediaManager().GetCdInfo(path);
75
76 if ( pInfo == NULL )
77 return ;
78
79 g_application.ResetScreenSaver();
80 g_application.WakeUpScreenSaverAndDPMS(); // turn off the screensaver if it's active
81
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())
85 {
86 CCDDARipper::GetInstance().RipCD();
87 }
88 else
89 #endif
90
91 PlayDisc(path, bypassSettings, startFromBeginning);
92 }
93
PlayDisc(const std::string & path,bool bypassSettings,bool startFromBeginning)94 bool CAutorun::PlayDisc(const std::string& path, bool bypassSettings, bool startFromBeginning)
95 {
96 if ( !bypassSettings && CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_AUDIOCDS_AUTOACTION) != AUTOCD_PLAY && !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_DVDS_AUTORUN))
97 return false;
98
99 int nSize = CServiceBroker::GetPlaylistPlayer().GetPlaylist( PLAYLIST_MUSIC ).size();
100 int nAddedToPlaylist = 0;
101
102 std::string mediaPath;
103
104 CCdInfo* pInfo = CServiceBroker::GetMediaManager().GetCdInfo(path);
105 if (pInfo == NULL)
106 return false;
107
108 if (mediaPath.empty() && pInfo->IsAudio(1))
109 mediaPath = "cdda://local/";
110
111 if (mediaPath.empty() && (pInfo->IsISOUDF(1) || pInfo->IsISOHFS(1) || pInfo->IsIso9660(1) || pInfo->IsIso9660Interactive(1)))
112 mediaPath = "iso9660://";
113
114 if (mediaPath.empty())
115 mediaPath = path;
116
117 if (mediaPath.empty() || mediaPath == "iso9660://")
118 mediaPath = CServiceBroker::GetMediaManager().GetDiscPath();
119
120 const CURL pathToUrl(mediaPath);
121 std::unique_ptr<IDirectory> pDir ( CDirectoryFactory::Create( pathToUrl ));
122 bool bPlaying = RunDisc(pDir.get(), mediaPath, nAddedToPlaylist, true, bypassSettings, startFromBeginning);
123
124 if ( !bPlaying && nAddedToPlaylist > 0 )
125 {
126 CGUIMessage msg( GUI_MSG_PLAYLIST_CHANGED, 0, 0 );
127 CServiceBroker::GetGUI()->GetWindowManager().SendMessage( msg );
128 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST_MUSIC);
129 // Start playing the items we inserted
130 return CServiceBroker::GetPlaylistPlayer().Play(nSize, "");
131 }
132
133 return bPlaying;
134 }
135
136 /**
137 * This method tries to determine what type of disc is located in the given drive and starts to play the content appropriately.
138 */
RunDisc(IDirectory * pDir,const std::string & strDrive,int & nAddedToPlaylist,bool bRoot,bool bypassSettings,bool startFromBeginning)139 bool CAutorun::RunDisc(IDirectory* pDir, const std::string& strDrive, int& nAddedToPlaylist, bool bRoot, bool bypassSettings /* = false */, bool startFromBeginning /* = false */)
140 {
141 if (!pDir)
142 {
143 CLog::Log(LOGDEBUG, "CAutorun::{}: cannot run disc. is it properly mounted?", __FUNCTION__);
144 return false;
145 }
146
147 bool bPlaying(false);
148 CFileItemList vecItems;
149
150 const CURL pathToUrl(strDrive);
151 if ( !pDir->GetDirectory( pathToUrl, vecItems ) )
152 {
153 return false;
154 }
155
156 // Sorting necessary for easier HDDVD handling
157 vecItems.Sort(SortByLabel, SortOrderAscending);
158
159 bool bAllowVideo = true;
160 // bool bAllowPictures = true;
161 bool bAllowMusic = true;
162 if (!g_passwordManager.IsMasterLockUnlocked(false))
163 {
164 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
165
166 bAllowVideo = !profileManager->GetCurrentProfile().videoLocked();
167 // bAllowPictures = !profileManager->GetCurrentProfile().picturesLocked();
168 bAllowMusic = !profileManager->GetCurrentProfile().musicLocked();
169 }
170
171 // is this a root folder we have to check the content to determine a disc type
172 if (bRoot)
173 {
174 std::string hddvdname = "";
175 CFileItemPtr phddvdItem;
176 bool bAutorunDVDs = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_DVDS_AUTORUN);
177
178 // check root folders next, for normal structured dvd's
179 for (const auto& pItem : vecItems)
180 {
181 // is the current item a (non system) folder?
182 if (pItem->m_bIsFolder && pItem->GetPath() != "." && pItem->GetPath() != "..")
183 {
184 std::string name = pItem->GetPath();
185 URIUtils::RemoveSlashAtEnd(name);
186 name = URIUtils::GetFileName(name);
187
188 // Check if the current foldername indicates a DVD structure (name is "VIDEO_TS")
189 if (StringUtils::EqualsNoCase(name, "VIDEO_TS") && bAllowVideo
190 && (bypassSettings || bAutorunDVDs))
191 {
192 std::string path = URIUtils::AddFileToFolder(pItem->GetPath(), "VIDEO_TS.IFO");
193 if(!CFile::Exists(path))
194 path = URIUtils::AddFileToFolder(pItem->GetPath(), "video_ts.ifo");
195 CFileItemPtr item(new CFileItem(path, false));
196 item->SetLabel(CServiceBroker::GetMediaManager().GetDiskLabel(strDrive));
197 item->GetVideoInfoTag()->m_strFileNameAndPath =
198 CServiceBroker::GetMediaManager().GetDiskUniqueId(strDrive);
199
200 if (!startFromBeginning && !item->GetVideoInfoTag()->m_strFileNameAndPath.empty())
201 item->m_lStartOffset = STARTOFFSET_RESUME;
202
203 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST_VIDEO);
204 CServiceBroker::GetPlaylistPlayer().SetShuffle (PLAYLIST_VIDEO, false);
205 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST_VIDEO, item);
206 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST_VIDEO);
207 CServiceBroker::GetPlaylistPlayer().Play(0, "");
208 return true;
209 }
210
211 // Check if the current foldername indicates a Blu-Ray structure (default is "BDMV").
212 // A BR should also include an "AACS" folder for encryption, Sony-BRs can also include update folders for PS3 (PS3_UPDATE / PS3_VPRM).
213 //! @todo for the time being, the DVD autorun settings are used to determine if the BR should be started automatically.
214 if (StringUtils::EqualsNoCase(name, "BDMV") && bAllowVideo
215 && (bypassSettings || bAutorunDVDs))
216 {
217 CFileItemPtr item(new CFileItem(URIUtils::AddFileToFolder(pItem->GetPath(), "index.bdmv"), false));
218 item->SetLabel(CServiceBroker::GetMediaManager().GetDiskLabel(strDrive));
219 item->GetVideoInfoTag()->m_strFileNameAndPath =
220 CServiceBroker::GetMediaManager().GetDiskUniqueId(strDrive);
221
222 if (!startFromBeginning && !item->GetVideoInfoTag()->m_strFileNameAndPath.empty())
223 item->m_lStartOffset = STARTOFFSET_RESUME;
224
225 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST_VIDEO);
226 CServiceBroker::GetPlaylistPlayer().SetShuffle (PLAYLIST_VIDEO, false);
227 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST_VIDEO, item);
228 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST_VIDEO);
229 CServiceBroker::GetPlaylistPlayer().Play(0, "");
230 return true;
231 }
232
233 // Check if the current foldername indicates a HD DVD structure (default is "HVDVD_TS").
234 // Most HD DVD will also include an "ADV_OBJ" folder for advanced content. This folder should be handled first.
235 //! @todo for the time being, the DVD autorun settings are used to determine if the HD DVD should be started automatically.
236 CFileItemList items, sitems;
237
238 // Advanced Content HD DVD (most discs?)
239 if (StringUtils::EqualsNoCase(name, "ADV_OBJ"))
240 {
241 CLog::Log(LOGINFO,"HD DVD: Checking for playlist.");
242 // find playlist file
243 CDirectory::GetDirectory(pItem->GetPath(), items, "*.xpl", DIR_FLAG_DEFAULTS);
244 if (items.Size())
245 {
246 // HD DVD Standard says the highest numbered playlist has to be handled first.
247 CLog::Log(LOGINFO,"HD DVD: Playlist found. Set filetypes to *.xpl for external player.");
248 items.Sort(SortByLabel, SortOrderDescending);
249 phddvdItem = pItem;
250 hddvdname = URIUtils::GetFileName(items[0]->GetPath());
251 CLog::Log(LOGINFO,"HD DVD: %s", items[0]->GetPath().c_str());
252 }
253 }
254
255 // Standard Content HD DVD (few discs?)
256 if (StringUtils::EqualsNoCase(name, "HVDVD_TS") && bAllowVideo
257 && (bypassSettings || bAutorunDVDs))
258 {
259 if (hddvdname == "")
260 {
261 CLog::Log(LOGINFO,"HD DVD: Checking for ifo.");
262 // find Video Manager or Title Set Information
263 CDirectory::GetDirectory(pItem->GetPath(), items, "HV*.ifo", DIR_FLAG_DEFAULTS);
264 if (items.Size())
265 {
266 // HD DVD Standard says the lowest numbered ifo has to be handled first.
267 CLog::Log(LOGINFO,"HD DVD: IFO found. Set filename to HV* and filetypes to *.ifo for external player.");
268 items.Sort(SortByLabel, SortOrderAscending);
269 phddvdItem = pItem;
270 hddvdname = URIUtils::GetFileName(items[0]->GetPath());
271 CLog::Log(LOGINFO,"HD DVD: %s",items[0]->GetPath().c_str());
272 }
273 }
274 // Find and sort *.evo files for internal playback.
275 // While this algorithm works for all of my HD DVDs, it may fail on other discs. If there are very large extras which are
276 // alphabetically before the main movie they will be sorted to the top of the playlist and get played first.
277 CDirectory::GetDirectory(pItem->GetPath(), items, "*.evo", DIR_FLAG_DEFAULTS);
278 if (items.Size())
279 {
280 // Sort *.evo files in alphabetical order.
281 items.Sort(SortByLabel, SortOrderAscending);
282 int64_t asize = 0;
283 int ecount = 0;
284 // calculate average size of elements above 1gb
285 for (int j = 0; j < items.Size(); j++)
286 if (items[j]->m_dwSize > 1000000000)
287 {
288 ecount++;
289 asize = asize + items[j]->m_dwSize;
290 }
291 if (ecount > 0)
292 asize = asize / ecount;
293 // Put largest files in alphabetical order to top of new list.
294 for (int j = 0; j < items.Size(); j++)
295 if (items[j]->m_dwSize >= asize)
296 sitems.Add (items[j]);
297 // Sort *.evo files by size.
298 items.Sort(SortBySize, SortOrderDescending);
299 // Add other files with descending size to bottom of new list.
300 for (int j = 0; j < items.Size(); j++)
301 if (items[j]->m_dwSize < asize)
302 sitems.Add (items[j]);
303 // Replace list with optimized list.
304 items.Clear();
305 items.Copy (sitems);
306 sitems.Clear();
307 }
308 if (hddvdname != "")
309 {
310 CFileItem item(URIUtils::AddFileToFolder(phddvdItem->GetPath(), hddvdname), false);
311 item.SetLabel(CServiceBroker::GetMediaManager().GetDiskLabel(strDrive));
312 item.GetVideoInfoTag()->m_strFileNameAndPath =
313 CServiceBroker::GetMediaManager().GetDiskUniqueId(strDrive);
314
315 if (!startFromBeginning && !item.GetVideoInfoTag()->m_strFileNameAndPath.empty())
316 item.m_lStartOffset = STARTOFFSET_RESUME;
317
318 // get playername
319 std::string hdVideoPlayer = CServiceBroker::GetPlayerCoreFactory().GetDefaultPlayer(item);
320
321 // Single *.xpl or *.ifo files require an external player to handle playback.
322 // If no matching rule was found, VideoPlayer will be default player.
323 if (hdVideoPlayer != "VideoPlayer")
324 {
325 CLog::Log(LOGINFO,"HD DVD: External singlefile playback initiated: %s",hddvdname.c_str());
326 g_application.PlayFile(item, hdVideoPlayer, false);
327 return true;
328 } else
329 CLog::Log(LOGINFO,"HD DVD: No external player found. Fallback to internal one.");
330 }
331
332 // internal *.evo playback.
333 CLog::Log(LOGINFO,"HD DVD: Internal multifile playback initiated.");
334 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST_VIDEO);
335 CServiceBroker::GetPlaylistPlayer().SetShuffle (PLAYLIST_VIDEO, false);
336 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST_VIDEO, items);
337 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST_VIDEO);
338 CServiceBroker::GetPlaylistPlayer().Play(0, "");
339 return true;
340 }
341
342 // Video CDs can have multiple file formats. First we need to determine which one is used on the CD
343 std::string strExt;
344 if (StringUtils::EqualsNoCase(name, "MPEGAV"))
345 strExt = ".dat";
346 if (StringUtils::EqualsNoCase(name, "MPEG2"))
347 strExt = ".mpg";
348
349 // If a file format was extracted we are sure this is a VCD. Autoplay if settings indicate we should.
350 if (!strExt.empty() && bAllowVideo
351 && (bypassSettings || bAutorunDVDs))
352 {
353 CFileItemList items;
354 CDirectory::GetDirectory(pItem->GetPath(), items, strExt, DIR_FLAG_DEFAULTS);
355 if (items.Size())
356 {
357 items.Sort(SortByLabel, SortOrderAscending);
358 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST_VIDEO);
359 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST_VIDEO, items);
360 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST_VIDEO);
361 CServiceBroker::GetPlaylistPlayer().Play(0, "");
362 return true;
363 }
364 }
365 /* Probably want this if/when we add some automedia action dialog...
366 else if (pItem->GetPath().Find("PICTURES") != -1 && bAllowPictures
367 && (bypassSettings))
368 {
369 bPlaying = true;
370 std::string strExec = StringUtils::Format("RecursiveSlideShow(%s)", pItem->GetPath().c_str());
371 CBuiltins::Execute(strExec);
372 return true;
373 }
374 */
375 }
376 }
377 }
378
379 // check video first
380 if (!nAddedToPlaylist && !bPlaying && (bypassSettings || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_DVDS_AUTORUN)))
381 {
382 // stack video files
383 CFileItemList tempItems;
384 tempItems.Append(vecItems);
385 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MYVIDEOS_STACKVIDEOS))
386 tempItems.Stack();
387 CFileItemList itemlist;
388
389 for (int i = 0; i < tempItems.Size(); i++)
390 {
391 CFileItemPtr pItem = tempItems[i];
392 if (!pItem->m_bIsFolder && pItem->IsVideo())
393 {
394 bPlaying = true;
395 if (pItem->IsStack())
396 {
397 //! @todo remove this once the app/player is capable of handling stacks immediately
398 CStackDirectory dir;
399 CFileItemList items;
400 dir.GetDirectory(pItem->GetURL(), items);
401 itemlist.Append(items);
402 }
403 else
404 itemlist.Add(pItem);
405 }
406 }
407 if (itemlist.Size())
408 {
409 if (!bAllowVideo)
410 {
411 if (!bypassSettings)
412 return false;
413
414 if (!g_passwordManager.IsMasterLockUnlocked(true))
415 return false;
416 }
417 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST_VIDEO);
418 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST_VIDEO, itemlist);
419 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST_VIDEO);
420 CServiceBroker::GetPlaylistPlayer().Play(0, "");
421 }
422 }
423 // then music
424 if (!bPlaying && (bypassSettings || CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_AUDIOCDS_AUTOACTION) == AUTOCD_PLAY) && bAllowMusic)
425 {
426 for (int i = 0; i < vecItems.Size(); i++)
427 {
428 CFileItemPtr pItem = vecItems[i];
429 if (!pItem->m_bIsFolder && pItem->IsAudio())
430 {
431 nAddedToPlaylist++;
432 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST_MUSIC, pItem);
433 }
434 }
435 }
436 /* Probably want this if/when we add some automedia action dialog...
437 // and finally pictures
438 if (!nAddedToPlaylist && !bPlaying && bypassSettings && bAllowPictures)
439 {
440 for (int i = 0; i < vecItems.Size(); i++)
441 {
442 CFileItemPtr pItem = vecItems[i];
443 if (!pItem->m_bIsFolder && pItem->IsPicture())
444 {
445 bPlaying = true;
446 std::string strExec = StringUtils::Format("RecursiveSlideShow(%s)", strDrive.c_str());
447 CBuiltins::Execute(strExec);
448 break;
449 }
450 }
451 }
452 */
453
454 // check subdirs if we are not playing yet
455 if (!bPlaying)
456 {
457 for (int i = 0; i < vecItems.Size(); i++)
458 {
459 CFileItemPtr pItem = vecItems[i];
460 if (pItem->m_bIsFolder)
461 {
462 if (pItem->GetPath() != "." && pItem->GetPath() != ".." )
463 {
464 if (RunDisc(pDir, pItem->GetPath(), nAddedToPlaylist, false, bypassSettings, startFromBeginning))
465 {
466 bPlaying = true;
467 break;
468 }
469 }
470 } // if (non system) folder
471 } // for all items in directory
472 } // if root folder
473
474 return bPlaying;
475 }
476
HandleAutorun()477 void CAutorun::HandleAutorun()
478 {
479 #ifndef TARGET_WINDOWS
480 if (!m_bEnable)
481 {
482 CDetectDVDMedia::m_evAutorun.Reset();
483 return ;
484 }
485
486 if (CDetectDVDMedia::m_evAutorun.WaitMSec(0))
487 {
488 ExecuteAutorun();
489 CDetectDVDMedia::m_evAutorun.Reset();
490 }
491 #endif
492 }
493
Enable()494 void CAutorun::Enable()
495 {
496 m_bEnable = true;
497 }
498
Disable()499 void CAutorun::Disable()
500 {
501 m_bEnable = false;
502 }
503
IsEnabled() const504 bool CAutorun::IsEnabled() const
505 {
506 return m_bEnable;
507 }
508
PlayDiscAskResume(const std::string & path)509 bool CAutorun::PlayDiscAskResume(const std::string& path)
510 {
511 return PlayDisc(path, true, !CanResumePlayDVD(path) ||
512 HELPERS::ShowYesNoDialogText(CVariant{341}, CVariant{""}, CVariant{13404}, CVariant{12021}) ==
513 DialogResponse::YES);
514 }
515
CanResumePlayDVD(const std::string & path)516 bool CAutorun::CanResumePlayDVD(const std::string& path)
517 {
518 std::string strUniqueId = CServiceBroker::GetMediaManager().GetDiskUniqueId(path);
519 if (!strUniqueId.empty())
520 {
521 CVideoDatabase dbs;
522 dbs.Open();
523 CBookmark bookmark;
524 if (dbs.GetResumeBookMark(strUniqueId, bookmark))
525 return true;
526 }
527 return false;
528 }
529
SettingOptionAudioCdActionsFiller(const SettingConstPtr & setting,std::vector<IntegerSettingOption> & list,int & current,void * data)530 void CAutorun::SettingOptionAudioCdActionsFiller(const SettingConstPtr& setting,
531 std::vector<IntegerSettingOption>& list,
532 int& current,
533 void* data)
534 {
535 list.emplace_back(g_localizeStrings.Get(16018), AUTOCD_NONE);
536 list.emplace_back(g_localizeStrings.Get(14098), AUTOCD_PLAY);
537 #ifdef HAS_CDDA_RIPPER
538 list.emplace_back(g_localizeStrings.Get(14096), AUTOCD_RIP);
539 #endif
540 }
541