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.
12 #include "GUIPassword.h"
13 #include "GUIUserMessages.h"
14 #include "PlayListPlayer.h"
15 #include "ServiceBroker.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"
36 #ifndef TARGET_WINDOWS
37 #include "storage/DetectDVDType.h"
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"
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
;
63 CAutorun::~CAutorun() = default;
65 bool CAutorun::ExecuteAutorun(const std::string
& path
)
67 if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_LOGIN_SCREEN
)
70 CCdInfo
* pInfo
= CServiceBroker::GetMediaManager().GetCdInfo(path
);
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
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();
91 success
= PlayDisc(path
, false, false);
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
))
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
);
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())
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
, "");
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 */)
144 CLog::Log(LOGDEBUG
, "CAutorun::{}: cannot run disc. is it properly mounted?", __FUNCTION__
);
148 bool bPlaying(false);
149 CFileItemList vecItems
;
151 const CURL
pathToUrl(strDrive
);
152 if ( !pDir
->GetDirectory( pathToUrl
, vecItems
) )
157 // Sorting necessary for easier HDDVD handling
158 vecItems
.Sort(SortByLabel
, SortOrderAscending
);
160 bool bAllowVideo
= true;
161 // bool bAllowPictures = true;
162 bool bAllowMusic
= true;
163 if (!g_passwordManager
.IsMasterLockUnlocked(false))
165 const std::shared_ptr
<CProfileManager
> profileManager
= CServiceBroker::GetSettingsComponent()->GetProfileManager();
167 bAllowVideo
= !profileManager
->GetCurrentProfile().videoLocked();
168 // bAllowPictures = !profileManager->GetCurrentProfile().picturesLocked();
169 bAllowMusic
= !profileManager
->GetCurrentProfile().musicLocked();
172 // is this a root folder we have to check the content to determine a disc type
175 std::string hddvdname
= "";
176 CFileItemPtr phddvdItem
;
177 bool bAutorunDVDs
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_DVDS_AUTORUN
);
179 // check root folders next, for normal structured dvd's
180 for (const auto& pItem
: vecItems
)
182 // is the current item a (non system) folder?
183 if (pItem
->m_bIsFolder
&& pItem
->GetPath() != "." && pItem
->GetPath() != "..")
185 std::string name
= pItem
->GetPath();
186 URIUtils::RemoveSlashAtEnd(name
);
187 name
= URIUtils::GetFileName(name
);
189 // Check if the current foldername indicates a DVD structure (name is "VIDEO_TS")
190 if (StringUtils::EqualsNoCase(name
, "VIDEO_TS") && bAllowVideo
191 && (bypassSettings
|| bAutorunDVDs
))
193 std::string path
= URIUtils::AddFileToFolder(pItem
->GetPath(), "VIDEO_TS.IFO");
194 if (!CFileUtils::Exists(path
))
195 path
= URIUtils::AddFileToFolder(pItem
->GetPath(), "video_ts.ifo");
196 CFileItemPtr
item(new CFileItem(path
, false));
197 item
->SetLabel(CServiceBroker::GetMediaManager().GetDiskLabel(strDrive
));
198 item
->GetVideoInfoTag()->m_strFileNameAndPath
=
199 CServiceBroker::GetMediaManager().GetDiskUniqueId(strDrive
);
201 if (!startFromBeginning
&& !item
->GetVideoInfoTag()->m_strFileNameAndPath
.empty())
202 item
->SetStartOffset(STARTOFFSET_RESUME
);
204 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST::TYPE_VIDEO
);
205 CServiceBroker::GetPlaylistPlayer().SetShuffle(PLAYLIST::TYPE_VIDEO
, false);
206 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::TYPE_VIDEO
, item
);
207 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::TYPE_VIDEO
);
208 CServiceBroker::GetPlaylistPlayer().Play(0, "");
212 // Check if the current foldername indicates a Blu-Ray structure (default is "BDMV").
213 // A BR should also include an "AACS" folder for encryption, Sony-BRs can also include update folders for PS3 (PS3_UPDATE / PS3_VPRM).
214 //! @todo for the time being, the DVD autorun settings are used to determine if the BR should be started automatically.
215 if (StringUtils::EqualsNoCase(name
, "BDMV") && bAllowVideo
216 && (bypassSettings
|| bAutorunDVDs
))
218 CFileItemPtr
item(new CFileItem(URIUtils::AddFileToFolder(pItem
->GetPath(), "index.bdmv"), false));
219 item
->SetLabel(CServiceBroker::GetMediaManager().GetDiskLabel(strDrive
));
220 item
->GetVideoInfoTag()->m_strFileNameAndPath
=
221 CServiceBroker::GetMediaManager().GetDiskUniqueId(strDrive
);
223 if (!startFromBeginning
&& !item
->GetVideoInfoTag()->m_strFileNameAndPath
.empty())
224 item
->SetStartOffset(STARTOFFSET_RESUME
);
226 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST::TYPE_VIDEO
);
227 CServiceBroker::GetPlaylistPlayer().SetShuffle(PLAYLIST::TYPE_VIDEO
, false);
228 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::TYPE_VIDEO
, item
);
229 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::TYPE_VIDEO
);
230 CServiceBroker::GetPlaylistPlayer().Play(0, "");
234 // Check if the current foldername indicates a HD DVD structure (default is "HVDVD_TS").
235 // Most HD DVD will also include an "ADV_OBJ" folder for advanced content. This folder should be handled first.
236 //! @todo for the time being, the DVD autorun settings are used to determine if the HD DVD should be started automatically.
237 CFileItemList items
, sitems
;
239 // Advanced Content HD DVD (most discs?)
240 if (StringUtils::EqualsNoCase(name
, "ADV_OBJ"))
242 CLog::Log(LOGINFO
,"HD DVD: Checking for playlist.");
243 // find playlist file
244 CDirectory::GetDirectory(pItem
->GetPath(), items
, "*.xpl", DIR_FLAG_DEFAULTS
);
247 // HD DVD Standard says the highest numbered playlist has to be handled first.
248 CLog::Log(LOGINFO
,"HD DVD: Playlist found. Set filetypes to *.xpl for external player.");
249 items
.Sort(SortByLabel
, SortOrderDescending
);
251 hddvdname
= URIUtils::GetFileName(items
[0]->GetPath());
252 CLog::Log(LOGINFO
, "HD DVD: {}", items
[0]->GetPath());
256 // Standard Content HD DVD (few discs?)
257 if (StringUtils::EqualsNoCase(name
, "HVDVD_TS") && bAllowVideo
258 && (bypassSettings
|| bAutorunDVDs
))
262 CLog::Log(LOGINFO
,"HD DVD: Checking for ifo.");
263 // find Video Manager or Title Set Information
264 CDirectory::GetDirectory(pItem
->GetPath(), items
, "HV*.ifo", DIR_FLAG_DEFAULTS
);
267 // HD DVD Standard says the lowest numbered ifo has to be handled first.
268 CLog::Log(LOGINFO
,"HD DVD: IFO found. Set filename to HV* and filetypes to *.ifo for external player.");
269 items
.Sort(SortByLabel
, SortOrderAscending
);
271 hddvdname
= URIUtils::GetFileName(items
[0]->GetPath());
272 CLog::Log(LOGINFO
, "HD DVD: {}", items
[0]->GetPath());
275 // Find and sort *.evo files for internal playback.
276 // While this algorithm works for all of my HD DVDs, it may fail on other discs. If there are very large extras which are
277 // alphabetically before the main movie they will be sorted to the top of the playlist and get played first.
278 CDirectory::GetDirectory(pItem
->GetPath(), items
, "*.evo", DIR_FLAG_DEFAULTS
);
281 // Sort *.evo files in alphabetical order.
282 items
.Sort(SortByLabel
, SortOrderAscending
);
285 // calculate average size of elements above 1gb
286 for (int j
= 0; j
< items
.Size(); j
++)
287 if (items
[j
]->m_dwSize
> 1000000000)
290 asize
= asize
+ items
[j
]->m_dwSize
;
293 asize
= asize
/ ecount
;
294 // Put largest files in alphabetical order to top of new list.
295 for (int j
= 0; j
< items
.Size(); j
++)
296 if (items
[j
]->m_dwSize
>= asize
)
297 sitems
.Add (items
[j
]);
298 // Sort *.evo files by size.
299 items
.Sort(SortBySize
, SortOrderDescending
);
300 // Add other files with descending size to bottom of new list.
301 for (int j
= 0; j
< items
.Size(); j
++)
302 if (items
[j
]->m_dwSize
< asize
)
303 sitems
.Add (items
[j
]);
304 // Replace list with optimized list.
311 CFileItem
item(URIUtils::AddFileToFolder(phddvdItem
->GetPath(), hddvdname
), false);
312 item
.SetLabel(CServiceBroker::GetMediaManager().GetDiskLabel(strDrive
));
313 item
.GetVideoInfoTag()->m_strFileNameAndPath
=
314 CServiceBroker::GetMediaManager().GetDiskUniqueId(strDrive
);
316 if (!startFromBeginning
&& !item
.GetVideoInfoTag()->m_strFileNameAndPath
.empty())
317 item
.SetStartOffset(STARTOFFSET_RESUME
);
320 std::string hdVideoPlayer
= CServiceBroker::GetPlayerCoreFactory().GetDefaultPlayer(item
);
322 // Single *.xpl or *.ifo files require an external player to handle playback.
323 // If no matching rule was found, VideoPlayer will be default player.
324 if (hdVideoPlayer
!= "VideoPlayer")
326 CLog::Log(LOGINFO
, "HD DVD: External singlefile playback initiated: {}", hddvdname
);
327 g_application
.PlayFile(item
, hdVideoPlayer
, false);
330 CLog::Log(LOGINFO
,"HD DVD: No external player found. Fallback to internal one.");
333 // internal *.evo playback.
334 CLog::Log(LOGINFO
,"HD DVD: Internal multifile playback initiated.");
335 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST::TYPE_VIDEO
);
336 CServiceBroker::GetPlaylistPlayer().SetShuffle(PLAYLIST::TYPE_VIDEO
, false);
337 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::TYPE_VIDEO
, items
);
338 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::TYPE_VIDEO
);
339 CServiceBroker::GetPlaylistPlayer().Play(0, "");
343 // Video CDs can have multiple file formats. First we need to determine which one is used on the CD
345 if (StringUtils::EqualsNoCase(name
, "MPEGAV"))
347 if (StringUtils::EqualsNoCase(name
, "MPEG2"))
350 // If a file format was extracted we are sure this is a VCD. Autoplay if settings indicate we should.
351 if (!strExt
.empty() && bAllowVideo
352 && (bypassSettings
|| bAutorunDVDs
))
355 CDirectory::GetDirectory(pItem
->GetPath(), items
, strExt
, DIR_FLAG_DEFAULTS
);
358 items
.Sort(SortByLabel
, SortOrderAscending
);
359 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST::TYPE_VIDEO
);
360 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::TYPE_VIDEO
, items
);
361 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::TYPE_VIDEO
);
362 CServiceBroker::GetPlaylistPlayer().Play(0, "");
366 /* Probably want this if/when we add some automedia action dialog...
367 else if (pItem->GetPath().Find("PICTURES") != -1 && bAllowPictures
371 std::string strExec = StringUtils::Format("RecursiveSlideShow({})", pItem->GetPath());
372 CBuiltins::Execute(strExec);
381 if (!nAddedToPlaylist
&& !bPlaying
&& (bypassSettings
|| CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_DVDS_AUTORUN
)))
384 CFileItemList tempItems
;
385 tempItems
.Append(vecItems
);
386 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MYVIDEOS_STACKVIDEOS
))
388 CFileItemList itemlist
;
390 for (int i
= 0; i
< tempItems
.Size(); i
++)
392 CFileItemPtr pItem
= tempItems
[i
];
393 if (!pItem
->m_bIsFolder
&& pItem
->IsVideo())
396 if (pItem
->IsStack())
398 //! @todo remove this once the app/player is capable of handling stacks immediately
401 dir
.GetDirectory(pItem
->GetURL(), items
);
402 itemlist
.Append(items
);
415 if (!g_passwordManager
.IsMasterLockUnlocked(true))
418 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST::TYPE_VIDEO
);
419 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::TYPE_VIDEO
, itemlist
);
420 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::TYPE_VIDEO
);
421 CServiceBroker::GetPlaylistPlayer().Play(0, "");
425 if (!bPlaying
&& (bypassSettings
|| CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_AUDIOCDS_AUTOACTION
) == AUTOCD_PLAY
) && bAllowMusic
)
427 for (int i
= 0; i
< vecItems
.Size(); i
++)
429 CFileItemPtr pItem
= vecItems
[i
];
430 if (!pItem
->m_bIsFolder
&& pItem
->IsAudio())
433 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::TYPE_MUSIC
, pItem
);
437 /* Probably want this if/when we add some automedia action dialog...
438 // and finally pictures
439 if (!nAddedToPlaylist && !bPlaying && bypassSettings && bAllowPictures)
441 for (int i = 0; i < vecItems.Size(); i++)
443 CFileItemPtr pItem = vecItems[i];
444 if (!pItem->m_bIsFolder && pItem->IsPicture())
447 std::string strExec = StringUtils::Format("RecursiveSlideShow({})", strDrive);
448 CBuiltins::Execute(strExec);
455 // check subdirs if we are not playing yet
458 for (int i
= 0; i
< vecItems
.Size(); i
++)
460 CFileItemPtr pItem
= vecItems
[i
];
461 if (pItem
->m_bIsFolder
)
463 if (pItem
->GetPath() != "." && pItem
->GetPath() != ".." )
465 if (RunDisc(pDir
, pItem
->GetPath(), nAddedToPlaylist
, false, bypassSettings
, startFromBeginning
))
471 } // if (non system) folder
472 } // for all items in directory
478 void CAutorun::HandleAutorun()
480 #if !defined(TARGET_WINDOWS) && defined(HAS_DVD_DRIVE)
481 const CDetectDVDMedia
& mediadetect
= CServiceBroker::GetDetectDVDMedia();
485 mediadetect
.m_evAutorun
.Reset();
489 if (mediadetect
.m_evAutorun
.Wait(0ms
))
491 if (!ExecuteAutorun(""))
492 CLog::Log(LOGDEBUG
, "{}: Could not execute autorun", __func__
);
493 mediadetect
.m_evAutorun
.Reset();
498 void CAutorun::Enable()
503 void CAutorun::Disable()
508 bool CAutorun::IsEnabled() const
513 bool CAutorun::PlayDiscAskResume(const std::string
& path
)
515 return PlayDisc(path
, true,
516 !CanResumePlayDVD(path
) ||
517 HELPERS::ShowYesNoDialogText(CVariant
{341}, CVariant
{""}, CVariant
{13404},
518 CVariant
{12021}) == DialogResponse::CHOICE_YES
);
521 bool CAutorun::CanResumePlayDVD(const std::string
& path
)
523 std::string strUniqueId
= CServiceBroker::GetMediaManager().GetDiskUniqueId(path
);
524 if (!strUniqueId
.empty())
529 if (dbs
.GetResumeBookMark(strUniqueId
, bookmark
))
535 void CAutorun::SettingOptionAudioCdActionsFiller(const SettingConstPtr
& setting
,
536 std::vector
<IntegerSettingOption
>& list
,
540 list
.emplace_back(g_localizeStrings
.Get(16018), AUTOCD_NONE
);
541 list
.emplace_back(g_localizeStrings
.Get(14098), AUTOCD_PLAY
);
542 #ifdef HAS_CDDA_RIPPER
543 list
.emplace_back(g_localizeStrings
.Get(14096), AUTOCD_RIP
);