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 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://")
157 pathToUrl
.SetProtocol("iso9660");
158 pathToUrl
.SetHostName(CServiceBroker::GetMediaManager().TranslateDevicePath(""));
161 if ( !pDir
->GetDirectory( pathToUrl
, vecItems
) )
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
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, "");
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, "");
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
);
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
);
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
))
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
);
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
);
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
);
290 // Sort *.evo files in alphabetical order.
291 items
.Sort(SortByLabel
, SortOrderAscending
);
294 // calculate average size of elements above 1gb
295 for (int j
= 0; j
< items
.Size(); j
++)
296 if (items
[j
]->m_dwSize
> 1000000000)
299 asize
= asize
+ items
[j
]->m_dwSize
;
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.
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
);
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);
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, "");
352 // Video CDs can have multiple file formats. First we need to determine which one is used on the CD
354 if (StringUtils::EqualsNoCase(name
, "MPEGAV"))
356 if (StringUtils::EqualsNoCase(name
, "MPEG2"))
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
))
364 CDirectory::GetDirectory(pItem
->GetPath(), items
, strExt
, DIR_FLAG_DEFAULTS
);
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, "");
375 /* Probably want this if/when we add some automedia action dialog...
376 else if (pItem->GetPath().Find("PICTURES") != -1 && bAllowPictures
380 std::string strExec = StringUtils::Format("RecursiveSlideShow({})", pItem->GetPath());
381 CBuiltins::Execute(strExec);
390 if (!nAddedToPlaylist
&& !bPlaying
&& (bypassSettings
|| CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_DVDS_AUTORUN
)))
393 CFileItemList tempItems
;
394 tempItems
.Append(vecItems
);
395 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MYVIDEOS_STACKVIDEOS
))
397 CFileItemList itemlist
;
399 for (int i
= 0; i
< tempItems
.Size(); i
++)
401 CFileItemPtr pItem
= tempItems
[i
];
402 if (!pItem
->m_bIsFolder
&& pItem
->IsVideo())
405 if (pItem
->IsStack())
407 //! @todo remove this once the app/player is capable of handling stacks immediately
410 dir
.GetDirectory(pItem
->GetURL(), items
);
411 itemlist
.Append(items
);
424 if (!g_passwordManager
.IsMasterLockUnlocked(true))
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, "");
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())
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())
456 std::string strExec = StringUtils::Format("RecursiveSlideShow({})", strDrive);
457 CBuiltins::Execute(strExec);
464 // check subdirs if we are not playing yet
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
))
480 } // if (non system) folder
481 } // for all items in directory
487 void CAutorun::HandleAutorun()
489 #if !defined(TARGET_WINDOWS) && defined(HAS_OPTICAL_DRIVE)
490 const CDetectDVDMedia
& mediadetect
= CServiceBroker::GetDetectDVDMedia();
494 mediadetect
.m_evAutorun
.Reset();
498 if (mediadetect
.m_evAutorun
.Wait(0ms
))
500 if (!ExecuteAutorun(""))
501 CLog::Log(LOGDEBUG
, "{}: Could not execute autorun", __func__
);
502 mediadetect
.m_evAutorun
.Reset();
507 void CAutorun::Enable()
512 void CAutorun::Disable()
517 bool CAutorun::IsEnabled() const
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())
538 if (dbs
.GetResumeBookMark(strUniqueId
, bookmark
))
544 void CAutorun::SettingOptionAudioCdActionsFiller(const SettingConstPtr
& setting
,
545 std::vector
<IntegerSettingOption
>& list
,
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
);