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 "FileItemList.h"
13 #include "GUIPassword.h"
14 #include "GUIUserMessages.h"
15 #include "PlayListPlayer.h"
16 #include "ServiceBroker.h"
18 #include "application/Application.h"
19 #include "application/ApplicationComponents.h"
20 #include "application/ApplicationPowerHandling.h"
21 #include "cores/playercorefactory/PlayerCoreFactory.h"
22 #include "filesystem/Directory.h"
23 #include "filesystem/DirectoryFactory.h"
24 #include "filesystem/StackDirectory.h"
25 #include "guilib/GUIComponent.h"
26 #include "guilib/GUIWindowManager.h"
27 #include "guilib/LocalizeStrings.h"
28 #include "messaging/helpers/DialogHelper.h"
29 #include "music/MusicFileItemClassify.h"
30 #include "playlists/PlayList.h"
31 #include "profiles/ProfileManager.h"
32 #include "settings/Settings.h"
33 #include "settings/SettingsComponent.h"
34 #include "settings/lib/Setting.h"
35 #include "settings/lib/SettingDefinitions.h"
36 #include "video/VideoFileItemClassify.h"
39 #ifndef TARGET_WINDOWS
40 #include "storage/DetectDVDType.h"
42 #include "storage/MediaManager.h"
43 #include "utils/FileUtils.h"
44 #include "utils/StringUtils.h"
45 #include "utils/URIUtils.h"
46 #include "utils/Variant.h"
47 #include "utils/log.h"
48 #include "video/VideoDatabase.h"
50 #ifdef HAS_CDDA_RIPPER
51 #include "cdrip/CDDARipper.h"
54 using namespace XFILE
;
55 using namespace MEDIA_DETECT
;
57 using namespace KODI::MESSAGING
;
58 using namespace KODI::VIDEO
;
59 using namespace std::chrono_literals
;
61 using KODI::MESSAGING::HELPERS::DialogResponse
;
68 CAutorun::~CAutorun() = default;
70 bool CAutorun::ExecuteAutorun(const std::string
& path
)
72 if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_LOGIN_SCREEN
)
75 CCdInfo
* pInfo
= CServiceBroker::GetMediaManager().GetCdInfo(path
);
80 auto& components
= CServiceBroker::GetAppComponents();
81 const auto appPower
= components
.GetComponent
<CApplicationPowerHandling
>();
82 appPower
->ResetScreenSaver();
83 appPower
->WakeUpScreenSaverAndDPMS(); // turn off the screensaver if it's active
87 #ifdef HAS_CDDA_RIPPER
88 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_AUDIOCDS_AUTOACTION
) == AUTOCD_RIP
&&
89 pInfo
->IsAudio(1) && !CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetCurrentProfile().musicLocked())
91 success
= KODI::CDRIP::CCDDARipper::GetInstance().RipCD();
96 success
= PlayDisc(path
, false, false);
100 bool CAutorun::PlayDisc(const std::string
& path
, bool bypassSettings
, bool startFromBeginning
)
102 if ( !bypassSettings
&& CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_AUDIOCDS_AUTOACTION
) != AUTOCD_PLAY
&& !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_DVDS_AUTORUN
))
105 int nSize
= CServiceBroker::GetPlaylistPlayer().GetPlaylist(PLAYLIST::Id::TYPE_MUSIC
).size();
106 int nAddedToPlaylist
= 0;
108 std::string mediaPath
;
110 CCdInfo
* pInfo
= CServiceBroker::GetMediaManager().GetCdInfo(path
);
114 if (pInfo
->IsAudio(1))
115 mediaPath
= "cdda://local/";
117 if (mediaPath
.empty() && (pInfo
->IsISOUDF(1) || pInfo
->IsISOHFS(1) || pInfo
->IsIso9660(1) || pInfo
->IsIso9660Interactive(1)))
118 mediaPath
= "iso9660://";
120 if (mediaPath
.empty())
123 if (mediaPath
.empty() || mediaPath
== "iso9660://")
124 mediaPath
= CServiceBroker::GetMediaManager().GetDiscPath();
126 const CURL
pathToUrl(mediaPath
);
127 std::unique_ptr
<IDirectory
> pDir ( CDirectoryFactory::Create( pathToUrl
));
128 bool bPlaying
= RunDisc(pDir
.get(), mediaPath
, nAddedToPlaylist
, true, bypassSettings
, startFromBeginning
);
130 if ( !bPlaying
&& nAddedToPlaylist
> 0 )
132 CGUIMessage
msg( GUI_MSG_PLAYLIST_CHANGED
, 0, 0 );
133 CServiceBroker::GetGUI()->GetWindowManager().SendMessage( msg
);
134 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::Id::TYPE_MUSIC
);
135 // Start playing the items we inserted
136 return CServiceBroker::GetPlaylistPlayer().Play(nSize
, "");
143 * This method tries to determine what type of disc is located in the given drive and starts to play the content appropriately.
145 bool CAutorun::RunDisc(IDirectory
* pDir
, const std::string
& strDrive
, int& nAddedToPlaylist
, bool bRoot
, bool bypassSettings
/* = false */, bool startFromBeginning
/* = false */)
149 CLog::Log(LOGDEBUG
, "CAutorun::{}: cannot run disc. is it properly mounted?", __FUNCTION__
);
153 bool bPlaying(false);
154 CFileItemList vecItems
;
156 CURL pathToUrl
{strDrive
};
157 // if the url being requested is a generic "iso9660://" we need to enrich it with the actual drive.
158 // use the hostname section to prepend the drive path
159 if (pathToUrl
.GetRedacted() == "iso9660://")
162 pathToUrl
.SetProtocol("iso9660");
163 pathToUrl
.SetHostName(CServiceBroker::GetMediaManager().TranslateDevicePath(""));
166 if ( !pDir
->GetDirectory( pathToUrl
, vecItems
) )
171 // Sorting necessary for easier HDDVD handling
172 vecItems
.Sort(SortByLabel
, SortOrderAscending
);
174 bool bAllowVideo
= true;
175 // bool bAllowPictures = true;
176 bool bAllowMusic
= true;
177 if (!g_passwordManager
.IsMasterLockUnlocked(false))
179 const std::shared_ptr
<CProfileManager
> profileManager
= CServiceBroker::GetSettingsComponent()->GetProfileManager();
181 bAllowVideo
= !profileManager
->GetCurrentProfile().videoLocked();
182 // bAllowPictures = !profileManager->GetCurrentProfile().picturesLocked();
183 bAllowMusic
= !profileManager
->GetCurrentProfile().musicLocked();
186 // is this a root folder we have to check the content to determine a disc type
189 std::string hddvdname
= "";
190 CFileItemPtr phddvdItem
;
191 bool bAutorunDVDs
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_DVDS_AUTORUN
);
193 // check root folders next, for normal structured dvd's
194 for (const auto& pItem
: vecItems
)
196 // is the current item a (non system) folder?
197 if (pItem
->m_bIsFolder
&& pItem
->GetPath() != "." && pItem
->GetPath() != "..")
199 std::string name
= pItem
->GetPath();
200 URIUtils::RemoveSlashAtEnd(name
);
201 name
= URIUtils::GetFileName(name
);
203 // Check if the current foldername indicates a DVD structure (name is "VIDEO_TS")
204 if (StringUtils::EqualsNoCase(name
, "VIDEO_TS") && bAllowVideo
205 && (bypassSettings
|| bAutorunDVDs
))
207 std::string path
= URIUtils::AddFileToFolder(pItem
->GetPath(), "VIDEO_TS.IFO");
208 if (!CFileUtils::Exists(path
))
209 path
= URIUtils::AddFileToFolder(pItem
->GetPath(), "video_ts.ifo");
210 CFileItemPtr
item(new CFileItem(path
, false));
211 item
->SetLabel(CServiceBroker::GetMediaManager().GetDiskLabel(strDrive
));
212 item
->GetVideoInfoTag()->m_strFileNameAndPath
=
213 CServiceBroker::GetMediaManager().GetDiskUniqueId(strDrive
);
215 if (!startFromBeginning
&& !item
->GetVideoInfoTag()->m_strFileNameAndPath
.empty())
216 item
->SetStartOffset(STARTOFFSET_RESUME
);
218 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST::Id::TYPE_VIDEO
);
219 CServiceBroker::GetPlaylistPlayer().SetShuffle(PLAYLIST::Id::TYPE_VIDEO
, false);
220 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::Id::TYPE_VIDEO
, item
);
221 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::Id::TYPE_VIDEO
);
222 CServiceBroker::GetPlaylistPlayer().Play(0, "");
226 // Check if the current foldername indicates a Blu-Ray structure (default is "BDMV").
227 // A BR should also include an "AACS" folder for encryption, Sony-BRs can also include update folders for PS3 (PS3_UPDATE / PS3_VPRM).
228 //! @todo for the time being, the DVD autorun settings are used to determine if the BR should be started automatically.
229 if (StringUtils::EqualsNoCase(name
, "BDMV") && bAllowVideo
230 && (bypassSettings
|| bAutorunDVDs
))
232 CFileItemPtr
item(new CFileItem(URIUtils::AddFileToFolder(pItem
->GetPath(), "index.bdmv"), false));
233 item
->SetLabel(CServiceBroker::GetMediaManager().GetDiskLabel(strDrive
));
234 item
->GetVideoInfoTag()->m_strFileNameAndPath
=
235 CServiceBroker::GetMediaManager().GetDiskUniqueId(strDrive
);
237 if (!startFromBeginning
&& !item
->GetVideoInfoTag()->m_strFileNameAndPath
.empty())
238 item
->SetStartOffset(STARTOFFSET_RESUME
);
240 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST::Id::TYPE_VIDEO
);
241 CServiceBroker::GetPlaylistPlayer().SetShuffle(PLAYLIST::Id::TYPE_VIDEO
, false);
242 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::Id::TYPE_VIDEO
, item
);
243 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::Id::TYPE_VIDEO
);
244 CServiceBroker::GetPlaylistPlayer().Play(0, "");
248 // Check if the current foldername indicates a HD DVD structure (default is "HVDVD_TS").
249 // Most HD DVD will also include an "ADV_OBJ" folder for advanced content. This folder should be handled first.
250 //! @todo for the time being, the DVD autorun settings are used to determine if the HD DVD should be started automatically.
251 CFileItemList items
, sitems
;
253 // Advanced Content HD DVD (most discs?)
254 if (StringUtils::EqualsNoCase(name
, "ADV_OBJ"))
256 CLog::Log(LOGINFO
,"HD DVD: Checking for playlist.");
257 // find playlist file
258 CDirectory::GetDirectory(pItem
->GetPath(), items
, "*.xpl", DIR_FLAG_DEFAULTS
);
261 // HD DVD Standard says the highest numbered playlist has to be handled first.
262 CLog::Log(LOGINFO
,"HD DVD: Playlist found. Set filetypes to *.xpl for external player.");
263 items
.Sort(SortByLabel
, SortOrderDescending
);
265 hddvdname
= URIUtils::GetFileName(items
[0]->GetPath());
266 CLog::Log(LOGINFO
, "HD DVD: {}", items
[0]->GetPath());
270 // Standard Content HD DVD (few discs?)
271 if (StringUtils::EqualsNoCase(name
, "HVDVD_TS") && bAllowVideo
272 && (bypassSettings
|| bAutorunDVDs
))
276 CLog::Log(LOGINFO
,"HD DVD: Checking for ifo.");
277 // find Video Manager or Title Set Information
278 CDirectory::GetDirectory(pItem
->GetPath(), items
, "HV*.ifo", DIR_FLAG_DEFAULTS
);
281 // HD DVD Standard says the lowest numbered ifo has to be handled first.
282 CLog::Log(LOGINFO
,"HD DVD: IFO found. Set filename to HV* and filetypes to *.ifo for external player.");
283 items
.Sort(SortByLabel
, SortOrderAscending
);
285 hddvdname
= URIUtils::GetFileName(items
[0]->GetPath());
286 CLog::Log(LOGINFO
, "HD DVD: {}", items
[0]->GetPath());
289 // Find and sort *.evo files for internal playback.
290 // While this algorithm works for all of my HD DVDs, it may fail on other discs. If there are very large extras which are
291 // alphabetically before the main movie they will be sorted to the top of the playlist and get played first.
292 CDirectory::GetDirectory(pItem
->GetPath(), items
, "*.evo", DIR_FLAG_DEFAULTS
);
295 // Sort *.evo files in alphabetical order.
296 items
.Sort(SortByLabel
, SortOrderAscending
);
299 // calculate average size of elements above 1gb
300 for (int j
= 0; j
< items
.Size(); j
++)
301 if (items
[j
]->m_dwSize
> 1000000000)
304 asize
= asize
+ items
[j
]->m_dwSize
;
307 asize
= asize
/ ecount
;
308 // Put largest files in alphabetical order to top of new list.
309 for (int j
= 0; j
< items
.Size(); j
++)
310 if (items
[j
]->m_dwSize
>= asize
)
311 sitems
.Add (items
[j
]);
312 // Sort *.evo files by size.
313 items
.Sort(SortBySize
, SortOrderDescending
);
314 // Add other files with descending size to bottom of new list.
315 for (int j
= 0; j
< items
.Size(); j
++)
316 if (items
[j
]->m_dwSize
< asize
)
317 sitems
.Add (items
[j
]);
318 // Replace list with optimized list.
325 CFileItem
item(URIUtils::AddFileToFolder(phddvdItem
->GetPath(), hddvdname
), false);
326 item
.SetLabel(CServiceBroker::GetMediaManager().GetDiskLabel(strDrive
));
327 item
.GetVideoInfoTag()->m_strFileNameAndPath
=
328 CServiceBroker::GetMediaManager().GetDiskUniqueId(strDrive
);
330 if (!startFromBeginning
&& !item
.GetVideoInfoTag()->m_strFileNameAndPath
.empty())
331 item
.SetStartOffset(STARTOFFSET_RESUME
);
334 std::string hdVideoPlayer
= CServiceBroker::GetPlayerCoreFactory().GetDefaultPlayer(item
);
336 // Single *.xpl or *.ifo files require an external player to handle playback.
337 // If no matching rule was found, VideoPlayer will be default player.
338 if (hdVideoPlayer
!= "VideoPlayer")
340 CLog::Log(LOGINFO
, "HD DVD: External singlefile playback initiated: {}", hddvdname
);
341 g_application
.PlayFile(item
, hdVideoPlayer
, false);
344 CLog::Log(LOGINFO
,"HD DVD: No external player found. Fallback to internal one.");
347 // internal *.evo playback.
348 CLog::Log(LOGINFO
,"HD DVD: Internal multifile playback initiated.");
349 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST::Id::TYPE_VIDEO
);
350 CServiceBroker::GetPlaylistPlayer().SetShuffle(PLAYLIST::Id::TYPE_VIDEO
, false);
351 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::Id::TYPE_VIDEO
, items
);
352 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::Id::TYPE_VIDEO
);
353 CServiceBroker::GetPlaylistPlayer().Play(0, "");
357 // Video CDs can have multiple file formats. First we need to determine which one is used on the CD
359 if (StringUtils::EqualsNoCase(name
, "MPEGAV"))
361 if (StringUtils::EqualsNoCase(name
, "MPEG2"))
364 // If a file format was extracted we are sure this is a VCD. Autoplay if settings indicate we should.
365 if (!strExt
.empty() && bAllowVideo
366 && (bypassSettings
|| bAutorunDVDs
))
369 CDirectory::GetDirectory(pItem
->GetPath(), items
, strExt
, DIR_FLAG_DEFAULTS
);
372 items
.Sort(SortByLabel
, SortOrderAscending
);
373 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST::Id::TYPE_VIDEO
);
374 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::Id::TYPE_VIDEO
, items
);
375 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::Id::TYPE_VIDEO
);
376 CServiceBroker::GetPlaylistPlayer().Play(0, "");
380 /* Probably want this if/when we add some automedia action dialog...
381 else if (pItem->GetPath().Find("PICTURES") != -1 && bAllowPictures
385 std::string strExec = StringUtils::Format("RecursiveSlideShow({})", pItem->GetPath());
386 CBuiltins::Execute(strExec);
395 if (!nAddedToPlaylist
&& !bPlaying
&& (bypassSettings
|| CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_DVDS_AUTORUN
)))
398 CFileItemList tempItems
;
399 tempItems
.Append(vecItems
);
400 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MYVIDEOS_STACKVIDEOS
))
402 CFileItemList itemlist
;
404 for (int i
= 0; i
< tempItems
.Size(); i
++)
406 CFileItemPtr pItem
= tempItems
[i
];
407 if (!pItem
->m_bIsFolder
&& IsVideo(*pItem
))
410 if (pItem
->IsStack())
412 //! @todo remove this once the app/player is capable of handling stacks immediately
415 dir
.GetDirectory(pItem
->GetURL(), items
);
416 itemlist
.Append(items
);
429 if (!g_passwordManager
.IsMasterLockUnlocked(true))
432 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST::Id::TYPE_VIDEO
);
433 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::Id::TYPE_VIDEO
, itemlist
);
434 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::Id::TYPE_VIDEO
);
435 CServiceBroker::GetPlaylistPlayer().Play(0, "");
439 if (!bPlaying
&& (bypassSettings
|| CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_AUDIOCDS_AUTOACTION
) == AUTOCD_PLAY
) && bAllowMusic
)
441 for (int i
= 0; i
< vecItems
.Size(); i
++)
443 CFileItemPtr pItem
= vecItems
[i
];
444 if (!pItem
->m_bIsFolder
&& MUSIC::IsAudio(*pItem
))
447 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::Id::TYPE_MUSIC
, pItem
);
451 /* Probably want this if/when we add some automedia action dialog...
452 // and finally pictures
453 if (!nAddedToPlaylist && !bPlaying && bypassSettings && bAllowPictures)
455 for (int i = 0; i < vecItems.Size(); i++)
457 CFileItemPtr pItem = vecItems[i];
458 if (!pItem->m_bIsFolder && pItem->IsPicture())
461 std::string strExec = StringUtils::Format("RecursiveSlideShow({})", strDrive);
462 CBuiltins::Execute(strExec);
469 // check subdirs if we are not playing yet
472 for (int i
= 0; i
< vecItems
.Size(); i
++)
474 CFileItemPtr pItem
= vecItems
[i
];
475 if (pItem
->m_bIsFolder
)
477 if (pItem
->GetPath() != "." && pItem
->GetPath() != ".." )
479 if (RunDisc(pDir
, pItem
->GetPath(), nAddedToPlaylist
, false, bypassSettings
, startFromBeginning
))
485 } // if (non system) folder
486 } // for all items in directory
492 void CAutorun::HandleAutorun()
494 #if !defined(TARGET_WINDOWS) && defined(HAS_OPTICAL_DRIVE)
495 const CDetectDVDMedia
& mediadetect
= CServiceBroker::GetDetectDVDMedia();
499 mediadetect
.m_evAutorun
.Reset();
503 if (mediadetect
.m_evAutorun
.Wait(0ms
))
505 if (!ExecuteAutorun(""))
506 CLog::Log(LOGDEBUG
, "{}: Could not execute autorun", __func__
);
507 mediadetect
.m_evAutorun
.Reset();
512 void CAutorun::Enable()
517 void CAutorun::Disable()
522 bool CAutorun::IsEnabled() const
527 bool CAutorun::PlayDiscAskResume(const std::string
& path
)
529 return PlayDisc(path
, true,
530 !CanResumePlayDVD(path
) ||
531 HELPERS::ShowYesNoDialogText(CVariant
{341}, CVariant
{""}, CVariant
{13404},
532 CVariant
{12021}) == DialogResponse::CHOICE_YES
);
535 bool CAutorun::CanResumePlayDVD(const std::string
& path
)
537 std::string strUniqueId
= CServiceBroker::GetMediaManager().GetDiskUniqueId(path
);
538 if (!strUniqueId
.empty())
543 if (dbs
.GetResumeBookMark(strUniqueId
, bookmark
))
549 void CAutorun::SettingOptionAudioCdActionsFiller(const SettingConstPtr
& setting
,
550 std::vector
<IntegerSettingOption
>& list
,
554 list
.emplace_back(g_localizeStrings
.Get(16018), AUTOCD_NONE
);
555 list
.emplace_back(g_localizeStrings
.Get(14098), AUTOCD_PLAY
);
556 #ifdef HAS_CDDA_RIPPER
557 list
.emplace_back(g_localizeStrings
.Get(14096), AUTOCD_RIP
);