2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
22 #include "GUIWindowVideoBase.h"
24 #include "video/VideoInfoDownloader.h"
25 #include "video/VideoInfoScanner.h"
26 #include "utils/RegExp.h"
27 #include "utils/Variant.h"
28 #include "addons/AddonManager.h"
29 #include "addons/GUIDialogAddonInfo.h"
30 #include "addons/IAddon.h"
31 #include "video/dialogs/GUIDialogVideoInfo.h"
32 #include "GUIWindowVideoNav.h"
33 #include "dialogs/GUIDialogSmartPlaylistEditor.h"
34 #include "dialogs/GUIDialogProgress.h"
35 #include "dialogs/GUIDialogYesNo.h"
36 #include "playlists/PlayListFactory.h"
37 #include "Application.h"
39 #include "PlayListPlayer.h"
40 #include "GUIPassword.h"
41 #include "filesystem/ZipManager.h"
42 #include "filesystem/StackDirectory.h"
43 #include "filesystem/MultiPathDirectory.h"
44 #include "video/dialogs/GUIDialogFileStacking.h"
45 #include "dialogs/GUIDialogMediaSource.h"
46 #include "windows/GUIWindowFileManager.h"
47 #include "filesystem/VideoDatabaseDirectory.h"
48 #include "PartyModeManager.h"
49 #include "guilib/GUIWindowManager.h"
50 #include "dialogs/GUIDialogOK.h"
51 #include "dialogs/GUIDialogSelect.h"
52 #include "guilib/GUIKeyboardFactory.h"
53 #include "filesystem/Directory.h"
54 #include "playlists/PlayList.h"
55 #include "profiles/ProfilesManager.h"
56 #include "settings/Settings.h"
57 #include "settings/AdvancedSettings.h"
58 #include "settings/MediaSettings.h"
59 #include "settings/dialogs/GUIDialogContentSettings.h"
60 #include "guilib/Key.h"
61 #include "guilib/LocalizeStrings.h"
62 #include "utils/StringUtils.h"
63 #include "utils/log.h"
64 #include "utils/FileUtils.h"
65 #include "interfaces/AnnouncementManager.h"
66 #include "network/upnp/UPnP.h"
67 #include "pvr/PVRManager.h"
68 #include "pvr/recordings/PVRRecordings.h"
69 #include "utils/URIUtils.h"
70 #include "GUIUserMessages.h"
71 #include "addons/Skin.h"
72 #include "storage/MediaManager.h"
75 #include "utils/EdenVideoArtUpdater.h"
76 #include "GUIInfoManager.h"
77 #include "utils/GroupUtils.h"
78 #include "filesystem/File.h"
81 using namespace XFILE;
82 using namespace PLAYLIST;
83 using namespace VIDEODATABASEDIRECTORY;
84 using namespace VIDEO;
85 using namespace ADDON;
88 #define CONTROL_BTNVIEWASICONS 2
89 #define CONTROL_BTNSORTBY 3
90 #define CONTROL_BTNSORTASC 4
91 #define CONTROL_BTNTYPE 5
92 #define CONTROL_LABELFILES 12
94 #define CONTROL_PLAY_DVD 6
96 #define PROPERTY_GROUP_BY "group.by"
97 #define PROPERTY_GROUP_MIXED "group.mixed"
99 CGUIWindowVideoBase::CGUIWindowVideoBase(int id, const CStdString &xmlFile)
100 : CGUIMediaWindow(id, xmlFile)
102 m_thumbLoader.SetObserver(this);
103 m_stackingAvailable = true;
104 m_dlgProgress = NULL;
107 CGUIWindowVideoBase::~CGUIWindowVideoBase()
111 bool CGUIWindowVideoBase::OnAction(const CAction &action)
113 if (action.GetID() == ACTION_SCAN_ITEM)
114 return OnContextButton(m_viewControl.GetSelectedItem(),CONTEXT_BUTTON_SCAN);
115 else if (action.GetID() == ACTION_SHOW_PLAYLIST)
117 if (g_playlistPlayer.GetCurrentPlaylist() == PLAYLIST_VIDEO ||
118 g_playlistPlayer.GetPlaylist(PLAYLIST_VIDEO).size() > 0)
120 g_windowManager.ActivateWindow(WINDOW_VIDEO_PLAYLIST);
125 return CGUIMediaWindow::OnAction(action);
128 bool CGUIWindowVideoBase::OnMessage(CGUIMessage& message)
130 switch ( message.GetMessage() )
132 case GUI_MSG_WINDOW_DEINIT:
133 if (m_thumbLoader.IsLoading())
134 m_thumbLoader.StopThread();
138 case GUI_MSG_WINDOW_INIT:
142 m_dlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
144 // save current window, unless the current window is the video playlist window
145 if (GetID() != WINDOW_VIDEO_PLAYLIST && CSettings::Get().GetInt("myvideos.startwindow") != GetID())
147 CSettings::Get().SetInt("myvideos.startwindow", GetID());
148 CSettings::Get().Save();
151 return CGUIMediaWindow::OnMessage(message);
155 case GUI_MSG_CLICKED:
157 int iControl = message.GetSenderId();
158 #if defined(HAS_DVD_DRIVE)
159 if (iControl == CONTROL_PLAY_DVD)
162 MEDIA_DETECT::CAutorun::PlayDiscAskResume(g_mediaManager.TranslateDevicePath(""));
166 if (iControl == CONTROL_BTNTYPE)
168 CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTNTYPE);
169 g_windowManager.SendMessage(msg);
171 int nSelected = msg.GetParam1();
172 int nNewWindow = WINDOW_VIDEO_FILES;
176 nNewWindow = WINDOW_VIDEO_FILES;
179 nNewWindow = WINDOW_VIDEO_NAV;
183 if (nNewWindow != GetID())
185 CSettings::Get().SetInt("myvideos.startwindow", nNewWindow);
186 CSettings::Get().Save();
187 g_windowManager.ChangeActiveWindow(nNewWindow);
188 CGUIMessage msg2(GUI_MSG_SETFOCUS, nNewWindow, CONTROL_BTNTYPE);
189 g_windowManager.SendMessage(msg2);
194 else if (m_viewControl.HasControl(iControl)) // list/thumb control
197 int iItem = m_viewControl.GetSelectedItem();
198 int iAction = message.GetParam1();
200 // iItem is checked for validity inside these routines
201 if (iAction == ACTION_QUEUE_ITEM || iAction == ACTION_MOUSE_MIDDLE_CLICK)
206 else if (iAction == ACTION_SHOW_INFO)
208 return OnInfo(iItem);
210 else if (iAction == ACTION_PLAYER_PLAY && !g_application.m_pPlayer->IsPlayingVideo())
212 return OnResumeItem(iItem);
214 else if (iAction == ACTION_DELETE_ITEM)
216 // is delete allowed?
217 if (CProfilesManager::Get().GetCurrentProfile().canWriteDatabases())
219 // must be at the title window
220 if (GetID() == WINDOW_VIDEO_NAV)
223 // or be at the files window and have file deletion enabled
224 else if (GetID() == WINDOW_VIDEO_FILES && CSettings::Get().GetBool("filelists.allowfiledeletion"))
227 // or be at the video playlists location
228 else if (m_vecItems->GetPath().Equals("special://videoplaylists/"))
243 return CGUIMediaWindow::OnMessage(message);
246 void CGUIWindowVideoBase::UpdateButtons()
248 // Remove labels from the window selection
249 CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_BTNTYPE);
250 g_windowManager.SendMessage(msg);
252 // Add labels to the window selection
253 CStdString strItem = g_localizeStrings.Get(744); // Files
254 CGUIMessage msg2(GUI_MSG_LABEL_ADD, GetID(), CONTROL_BTNTYPE);
255 msg2.SetLabel(strItem);
256 g_windowManager.SendMessage(msg2);
258 strItem = g_localizeStrings.Get(14022); // Library
259 msg2.SetLabel(strItem);
260 g_windowManager.SendMessage(msg2);
262 // Select the current window as default item
263 int nWindow = CSettings::Get().GetInt("myvideos.startwindow")-WINDOW_VIDEO_FILES;
264 CONTROL_SELECT_ITEM(CONTROL_BTNTYPE, nWindow);
266 CGUIMediaWindow::UpdateButtons();
269 void CGUIWindowVideoBase::OnInfo(CFileItem* pItem, const ADDON::ScraperPtr& scraper)
274 if (pItem->IsParentFolder() || pItem->m_bIsShareOrDrive || pItem->GetPath().Equals("add") ||
275 (pItem->IsPlayList() && !URIUtils::HasExtension(pItem->GetPath(), ".strm")))
278 // ShowIMDB can kill the item as this window can be closed while we do it,
279 // so take a copy of the item now
280 CFileItem item(*pItem);
281 if (item.IsVideoDb() && item.HasVideoInfoTag())
283 if (item.GetVideoInfoTag()->m_type == "season")
284 { // clear out the art - we're really grabbing the info on the show here
287 item.SetPath(item.GetVideoInfoTag()->GetPath());
291 if (item.m_bIsFolder && scraper && scraper->Content() != CONTENT_TVSHOWS)
294 CDirectory::GetDirectory(item.GetPath(), items, g_advancedSettings.m_videoExtensions);
297 // check for media files
298 bool bFoundFile(false);
299 for (int i = 0; i < items.Size(); ++i)
301 CFileItemPtr item2 = items[i];
303 if (item2->IsVideo() && !item2->IsPlayList() &&
304 !CUtil::ExcludeFileOrFolder(item2->GetPath(), g_advancedSettings.m_moviesExcludeFromScanRegExps))
306 item.SetPath(item2->GetPath());
307 item.m_bIsFolder = false;
313 // no video file in this folder
316 CGUIDialogOK::ShowAndGetInput(13346,20349,20022,20022);
322 // we need to also request any thumbs be applied to the folder item
323 if (pItem->m_bIsFolder)
324 item.SetProperty("set_folder_thumb", pItem->GetPath());
326 bool modified = ShowIMDB(&item, scraper);
328 (g_windowManager.GetActiveWindow() == WINDOW_VIDEO_FILES ||
329 g_windowManager.GetActiveWindow() == WINDOW_VIDEO_NAV)) // since we can be called from the music library we need this check
331 int itemNumber = m_viewControl.GetSelectedItem();
333 m_viewControl.SetSelectedItem(itemNumber);
337 // ShowIMDB is called as follows:
338 // 1. To lookup info on a file.
339 // 2. To lookup info on a folder (which may or may not contain a file)
340 // 3. To lookup info just for fun (no file or folder related)
342 // We just need the item object for this.
343 // A "blank" item object is sent for 3.
344 // If a folder is sent, currently it sets strFolder and bFolder
345 // this is only used for setting the folder thumb, however.
349 // 1. Check database to see if we have this information already
350 // 2. Else, check for a nfoFile to get the URL
351 // 3. Run a loop to check for refresh
352 // 4. If no URL is present do a search to get the URL
353 // 4. Once we have the URL, download the details
354 // 5. Once we have the details, add to the database if necessary (case 1,2)
355 // and show the information.
356 // 6. Check for a refresh, and if so, go to 3.
358 bool CGUIWindowVideoBase::ShowIMDB(CFileItem *item, const ScraperPtr &info2)
361 CLog::Log(LOGDEBUG,"CGUIWindowVideoBase::ShowIMDB");
362 CLog::Log(LOGDEBUG," strMovie = [%s]", strMovie.c_str());
363 CLog::Log(LOGDEBUG," strFile = [%s]", strFile.c_str());
364 CLog::Log(LOGDEBUG," strFolder = [%s]", strFolder.c_str());
365 CLog::Log(LOGDEBUG," bFolder = [%s]", ((int)bFolder ? "true" : "false"));
368 CGUIDialogProgress* pDlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
369 CGUIDialogSelect* pDlgSelect = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
370 CGUIDialogVideoInfo* pDlgInfo = (CGUIDialogVideoInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_INFO);
372 ScraperPtr info(info2); // use this as nfo might change it..
374 if (!pDlgProgress) return false;
375 if (!pDlgSelect) return false;
376 if (!pDlgInfo) return false;
378 // 1. Check for already downloaded information, and if we have it, display our dialog
379 // Return if no Refresh is needed.
382 CVideoInfoTag movieDetails;
385 m_database.Open(); // since we can be called from the music library
387 if (info->Content() == CONTENT_MOVIES)
389 bHasInfo = m_database.GetMovieInfo(item->GetPath(), movieDetails);
391 if (info->Content() == CONTENT_TVSHOWS)
393 if (item->m_bIsFolder)
395 bHasInfo = m_database.GetTvShowInfo(item->GetPath(), movieDetails);
400 if (item->HasVideoInfoTag())
401 EpisodeHint = item->GetVideoInfoTag()->m_iEpisode;
403 if ((idEpisode = m_database.GetEpisodeId(item->GetPath(),EpisodeHint)) > -1)
406 m_database.GetEpisodeInfo(item->GetPath(), movieDetails, idEpisode);
411 // As we cannot add an episode to a non-existing tvshow entry, we have to check the parent directory
412 // to see if it`s already in our video database. If it's not yet part of the database we will exit here.
415 // NOTE: This will fail for episodes on multipath shares, as the parent path isn't what is stored in the
416 // database. Possible solutions are to store the paths in the db separately and rely on the show
417 // stacking stuff, or to modify GetTvShowId to do support multipath:// shares
418 CStdString strParentDirectory;
419 URIUtils::GetParentPath(item->GetPath(), strParentDirectory);
420 if (m_database.GetTvShowId(strParentDirectory) < 0)
422 CLog::Log(LOGERROR,"%s: could not add episode [%s]. tvshow does not exist yet..", __FUNCTION__, item->GetPath().c_str());
428 if (info->Content() == CONTENT_MUSICVIDEOS)
430 bHasInfo = m_database.GetMusicVideoInfo(item->GetPath(), movieDetails);
434 else if(item->HasVideoInfoTag())
437 movieDetails = *item->GetVideoInfoTag();
440 bool needsRefresh = false;
443 if (!info || info->Content() == CONTENT_NONE) // disable refresh button
444 movieDetails.m_strIMDBNumber = "xx"+movieDetails.m_strIMDBNumber;
445 *item->GetVideoInfoTag() = movieDetails;
446 pDlgInfo->SetMovie(item);
448 needsRefresh = pDlgInfo->NeedRefresh();
450 return pDlgInfo->HasUpdatedThumb();
451 // check if the item in the video info dialog has changed and if so, get the new item
452 else if (pDlgInfo->GetCurrentListItem() != NULL)
454 item = pDlgInfo->GetCurrentListItem().get();
456 if (item->IsVideoDb() && item->HasVideoInfoTag())
457 item->SetPath(item->GetVideoInfoTag()->GetPath());
461 // quietly return if Internet lookups are disabled
462 if (!CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() && !g_passwordManager.bMasterUser)
468 if (g_application.IsVideoScanning())
470 CGUIDialogOK::ShowAndGetInput(13346,14057,-1,-1);
475 // 2. Look for a nfo File to get the search URL
476 SScanSettings settings;
477 info = m_database.GetScraperForPath(item->GetPath(),settings);
482 // Get the correct movie title
483 CStdString movieName = item->GetMovieName(settings.parent_name);
486 CVideoInfoScanner scanner;
487 bool hasDetails = false;
488 bool listNeedsUpdating = false;
489 bool ignoreNfo = false;
490 // 3. Run a loop so that if we Refresh we re-run this block
495 CNfoFile::NFOResult nfoResult = scanner.CheckForNFOFile(item,settings.parent_name_root,info,scrUrl);
496 if (nfoResult == CNfoFile::ERROR_NFO)
499 if (nfoResult != CNfoFile::NO_NFO)
505 if (!info->IsNoop() && (nfoResult == CNfoFile::URL_NFO || nfoResult == CNfoFile::COMBINED_NFO || nfoResult == CNfoFile::FULL_NFO))
507 if (CGUIDialogYesNo::ShowAndGetInput(13346,20446,20447,20022))
518 // 4. if we don't have an url, or need to refresh the search
519 // then do the web search
521 if (info->Content() == CONTENT_TVSHOWS && !item->m_bIsFolder)
524 if (!hasDetails && (scrUrl.m_url.size() == 0 || needsRefresh))
526 // 4a. show dialog that we're busy querying www.imdb.com
527 CStdString strHeading = StringUtils::Format(g_localizeStrings.Get(197),info->Name().c_str());
528 pDlgProgress->SetHeading(strHeading);
529 pDlgProgress->SetLine(0, movieName);
530 pDlgProgress->SetLine(1, "");
531 pDlgProgress->SetLine(2, "");
532 pDlgProgress->StartModal();
533 pDlgProgress->Progress();
535 // 4b. do the websearch
537 CVideoInfoDownloader imdb(info);
538 int returncode = imdb.FindMovie(movieName, movielist, pDlgProgress);
541 pDlgProgress->Close();
542 if (movielist.size() > 0)
545 if (info->Content() == CONTENT_TVSHOWS)
547 pDlgSelect->SetHeading(iString);
549 for (unsigned int i = 0; i < movielist.size(); ++i)
550 pDlgSelect->Add(movielist[i].strTitle);
551 pDlgSelect->EnableButton(true, 413); // manual
552 pDlgSelect->DoModal();
554 // and wait till user selects one
555 int iSelectedMovie = pDlgSelect->GetSelectedLabel();
556 if (iSelectedMovie >= 0)
558 scrUrl = movielist[iSelectedMovie];
559 CLog::Log(LOGDEBUG, "%s: user selected movie '%s' with URL '%s'",
560 __FUNCTION__, scrUrl.strTitle.c_str(), scrUrl.m_url[0].m_url.c_str());
562 else if (!pDlgSelect->IsButtonPressed())
565 return listNeedsUpdating; // user backed out
569 else if (returncode == -1 || !CVideoInfoScanner::DownloadFailed(pDlgProgress))
571 pDlgProgress->Close();
575 // 4c. Check if url is still empty - occurs if user has selected to do a manual
576 // lookup, or if the IMDb lookup failed or was cancelled.
577 if (!hasDetails && scrUrl.m_url.size() == 0)
579 // Check for cancel of the progress dialog
580 pDlgProgress->Close();
581 if (pDlgProgress->IsCanceled())
584 return listNeedsUpdating;
587 // Prompt the user to input the movieName
589 if (info->Content() == CONTENT_TVSHOWS)
591 if (!CGUIKeyboardFactory::ShowAndGetInput(movieName, g_localizeStrings.Get(iString), false))
594 return listNeedsUpdating; // user backed out
601 // 5. Download the movie information
602 // show dialog that we're downloading the movie info
604 // clear artwork and invalidate hashes
608 for (CGUIListItem::ArtMap::const_iterator i = item->GetArt().begin(); i != item->GetArt().end(); ++i)
609 db.InvalidateCachedTexture(i->second);
615 CStdString strPath=item->GetPath();
616 if (item->IsVideoDb())
618 CFileItemPtr newItem(new CFileItem(*item->GetVideoInfoTag()));
620 strPath = item->GetVideoInfoTag()->m_strPath;
624 CFileItemPtr newItem(new CFileItem(*item));
628 if (item->m_bIsFolder)
629 list.SetPath(URIUtils::GetParentPath(strPath));
631 list.SetPath(URIUtils::GetDirectory(strPath));
634 if (info->Content() == CONTENT_TVSHOWS)
636 if (item->m_bIsFolder)
641 if (info->Content() == CONTENT_MUSICVIDEOS)
643 pDlgProgress->SetHeading(iString);
644 pDlgProgress->SetLine(0, movieName);
645 pDlgProgress->SetLine(1, scrUrl.strTitle);
646 pDlgProgress->SetLine(2, "");
647 pDlgProgress->StartModal();
648 pDlgProgress->Progress();
651 if (info->Content() == CONTENT_MOVIES)
652 m_database.DeleteMovie(item->GetPath());
653 if (info->Content() == CONTENT_TVSHOWS && !item->m_bIsFolder)
654 m_database.DeleteEpisode(item->GetPath(),movieDetails.m_iDbId);
655 if (info->Content() == CONTENT_MUSICVIDEOS)
656 m_database.DeleteMusicVideo(item->GetPath());
657 if (info->Content() == CONTENT_TVSHOWS && item->m_bIsFolder)
659 if (pDlgInfo->RefreshAll())
660 m_database.DeleteTvShow(item->GetPath());
662 m_database.DeleteDetailsForTvShow(item->GetPath());
665 if (scanner.RetrieveVideoInfo(list,settings.parent_name_root,info->Content(),!ignoreNfo,&scrUrl,pDlgInfo->RefreshAll(),pDlgProgress))
667 if (info->Content() == CONTENT_MOVIES)
668 m_database.GetMovieInfo(item->GetPath(),movieDetails);
669 if (info->Content() == CONTENT_MUSICVIDEOS)
670 m_database.GetMusicVideoInfo(item->GetPath(),movieDetails);
671 if (info->Content() == CONTENT_TVSHOWS)
673 // update tvshow info to get updated episode numbers
674 if (item->m_bIsFolder)
675 m_database.GetTvShowInfo(item->GetPath(),movieDetails);
677 m_database.GetEpisodeInfo(item->GetPath(),movieDetails);
680 // got all movie details :-)
681 OutputDebugString("got details\n");
682 pDlgProgress->Close();
684 // now show the imdb info
685 OutputDebugString("show info\n");
687 // remove directory caches and reload images
688 CUtil::DeleteVideoDatabaseDirectoryCache();
689 CGUIMessage reload(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
692 *item->GetVideoInfoTag() = movieDetails;
693 pDlgInfo->SetMovie(item);
695 item->SetArt("thumb", pDlgInfo->GetThumbnail());
696 needsRefresh = pDlgInfo->NeedRefresh();
697 listNeedsUpdating = true;
701 pDlgProgress->Close();
702 if (pDlgProgress->IsCanceled())
705 return listNeedsUpdating; // user cancelled
707 CGUIDialogOK::ShowAndGetInput(195, movieName, 0, 0);
709 return listNeedsUpdating;
712 // 6. Check for a refresh
713 } while (needsRefresh);
715 return listNeedsUpdating;
718 void CGUIWindowVideoBase::OnQueueItem(int iItem)
720 // don't re-queue items from playlist window
721 if ( iItem < 0 || iItem >= m_vecItems->Size() || GetID() == WINDOW_VIDEO_PLAYLIST ) return ;
723 // we take a copy so that we can alter the queue state
724 CFileItemPtr item(new CFileItem(*m_vecItems->Get(iItem)));
725 if (item->IsRAR() || item->IsZIP())
728 // Allow queuing of unqueueable items
729 // when we try to queue them directly
730 if (!item->CanQueue())
731 item->SetCanQueue(true);
733 CFileItemList queuedItems;
734 AddItemToPlayList(item, queuedItems);
735 // if party mode, add items but DONT start playing
736 if (g_partyModeManager.IsEnabled(PARTYMODECONTEXT_VIDEO))
738 g_partyModeManager.AddUserSongs(queuedItems, false);
742 g_playlistPlayer.Add(PLAYLIST_VIDEO, queuedItems);
743 g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_VIDEO);
744 // video does not auto play on queue like music
745 m_viewControl.SetSelectedItem(iItem + 1);
748 void CGUIWindowVideoBase::AddItemToPlayList(const CFileItemPtr &pItem, CFileItemList &queuedItems)
750 if (!pItem->CanQueue() || pItem->IsRAR() || pItem->IsZIP() || pItem->IsParentFolder()) // no zip/rar enques thank you!
753 if (pItem->m_bIsFolder)
755 if (pItem->IsParentFolder())
758 // check if it's a folder with dvd or bluray files, then just add the relevant file
759 std::string mediapath(pItem->GetOpticalMediaPath());
760 if (!mediapath.empty())
762 CFileItemPtr item(new CFileItem(mediapath, false));
763 queuedItems.Add(item);
767 // Check if we add a locked share
768 if ( pItem->m_bIsShareOrDrive )
770 CFileItem item = *pItem;
771 if ( !g_passwordManager.IsItemUnlocked( &item, "video" ) )
777 GetDirectory(pItem->GetPath(), items);
778 FormatAndSort(items);
780 int watchedMode = CMediaSettings::Get().GetWatchedMode(items.GetContent());
781 bool unwatchedOnly = watchedMode == WatchedModeUnwatched;
782 bool watchedOnly = watchedMode == WatchedModeWatched;
783 for (int i = 0; i < items.Size(); ++i)
785 if (items[i]->m_bIsFolder)
787 CStdString strPath = items[i]->GetPath();
788 URIUtils::RemoveSlashAtEnd(strPath);
789 if (StringUtils::EndsWithNoCase(strPath, "sample")) // skip sample folders
794 else if (items[i]->HasVideoInfoTag() &&
795 ((unwatchedOnly && items[i]->GetVideoInfoTag()->m_playCount > 0) ||
796 (watchedOnly && items[i]->GetVideoInfoTag()->m_playCount <= 0)))
799 AddItemToPlayList(items[i], queuedItems);
805 if (pItem->IsPlayList())
807 auto_ptr<CPlayList> pPlayList (CPlayListFactory::Create(*pItem));
811 if (!pPlayList->Load(pItem->GetPath()))
813 CGUIDialogOK::ShowAndGetInput(6, 0, 477, 0);
814 return; //hmmm unable to load playlist?
817 CPlayList playlist = *pPlayList;
818 for (int i = 0; i < (int)playlist.size(); ++i)
820 AddItemToPlayList(playlist[i], queuedItems);
825 else if(pItem->IsInternetStream())
826 { // just queue the internet stream, it will be expanded on play
827 queuedItems.Add(pItem);
829 else if (pItem->IsPlugin() && pItem->GetProperty("isplayable") == "true")
830 { // a playable python files
831 queuedItems.Add(pItem);
833 else if (pItem->IsVideoDb())
834 { // this case is needed unless we allow IsVideo() to return true for videodb items,
835 // but then we have issues with playlists of videodb items
836 CFileItemPtr item(new CFileItem(*pItem->GetVideoInfoTag()));
837 queuedItems.Add(item);
839 else if (!pItem->IsNFO() && pItem->IsVideo())
841 queuedItems.Add(pItem);
846 void CGUIWindowVideoBase::GetResumeItemOffset(const CFileItem *item, int& startoffset, int& partNumber)
848 // do not resume livetv
849 if (item->IsLiveTV())
855 if (!item->IsNFO() && !item->IsPlayList())
857 if (item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_resumePoint.IsSet())
859 startoffset = (int)(item->GetVideoInfoTag()->m_resumePoint.timeInSeconds*75);
860 partNumber = item->GetVideoInfoTag()->m_resumePoint.partNumber;
865 CStdString strPath = item->GetPath();
866 if ((item->IsVideoDb() || item->IsDVD()) && item->HasVideoInfoTag())
867 strPath = item->GetVideoInfoTag()->m_strFileNameAndPath;
872 CLog::Log(LOGERROR, "%s - Cannot open VideoDatabase", __FUNCTION__);
875 if (db.GetResumeBookMark(strPath, bookmark))
877 startoffset = (int)(bookmark.timeInSeconds*75);
878 partNumber = bookmark.partNumber;
885 bool CGUIWindowVideoBase::HasResumeItemOffset(const CFileItem *item)
887 int startoffset = 0, partNumber = 0;
888 GetResumeItemOffset(item, startoffset, partNumber);
889 return startoffset > 0;
892 bool CGUIWindowVideoBase::OnClick(int iItem)
894 return CGUIMediaWindow::OnClick(iItem);
897 bool CGUIWindowVideoBase::OnSelect(int iItem)
899 if (iItem < 0 || iItem >= m_vecItems->Size())
902 CFileItemPtr item = m_vecItems->Get(iItem);
904 CStdString path = item->GetPath();
905 if (!item->m_bIsFolder && path != "add" && path != "addons://more/video" &&
906 !StringUtils::StartsWith(path, "newsmartplaylist://") &&
907 !StringUtils::StartsWith(path, "newplaylist://") &&
908 !StringUtils::StartsWith(path, "newtag://"))
909 return OnFileAction(iItem, CSettings::Get().GetInt("myvideos.selectaction"));
911 return CGUIMediaWindow::OnSelect(iItem);
914 bool CGUIWindowVideoBase::OnFileAction(int iItem, int action)
916 CFileItemPtr item = m_vecItems->Get(iItem);
918 // Reset the current start offset. The actual resume
919 // option is set in the switch, based on the action passed.
920 item->m_lStartOffset = 0;
924 case SELECT_ACTION_CHOOSE:
926 CContextButtons choices;
928 if (item->IsVideoDb())
930 CStdString itemPath(item->GetPath());
931 itemPath = item->GetVideoInfoTag()->m_strFileNameAndPath;
932 if (URIUtils::IsStack(itemPath) && CFileItem(CStackDirectory::GetFirstStackedFile(itemPath),false).IsDVDImage())
933 choices.Add(SELECT_ACTION_PLAYPART, 20324); // Play Part
936 CStdString resumeString = GetResumeString(*item);
937 if (!resumeString.empty())
939 choices.Add(SELECT_ACTION_RESUME, resumeString);
940 choices.Add(SELECT_ACTION_PLAY, 12021); // Start from beginning
943 choices.Add(SELECT_ACTION_PLAY, 208); // Play
945 choices.Add(SELECT_ACTION_INFO, 22081); // Info
946 choices.Add(SELECT_ACTION_MORE, 22082); // More
947 int value = CGUIDialogContextMenu::ShowAndGetChoice(choices);
951 return OnFileAction(iItem, value);
954 case SELECT_ACTION_PLAY_OR_RESUME:
955 return OnResumeItem(iItem);
956 case SELECT_ACTION_INFO:
960 case SELECT_ACTION_MORE:
963 case SELECT_ACTION_RESUME:
964 item->m_lStartOffset = STARTOFFSET_RESUME;
966 case SELECT_ACTION_PLAYPART:
967 if (!OnPlayStackPart(iItem))
970 case SELECT_ACTION_PLAY:
974 return OnClick(iItem);
977 bool CGUIWindowVideoBase::OnInfo(int iItem)
979 if (iItem < 0 || iItem >= m_vecItems->Size())
982 CFileItemPtr item = m_vecItems->Get(iItem);
984 if (item->GetPath().Equals("add") || item->IsParentFolder() ||
985 (item->IsPlayList() && !URIUtils::HasExtension(item->GetPath(), ".strm")))
988 if (!m_vecItems->IsPlugin() && (item->IsPlugin() || item->IsScript()))
989 return CGUIDialogAddonInfo::ShowForItem(item);
991 ADDON::ScraperPtr scraper;
992 if (!m_vecItems->IsPlugin() && !m_vecItems->IsRSS() && !m_vecItems->IsLiveTV())
995 if (item->IsVideoDb() &&
996 item->HasVideoInfoTag() &&
997 !item->GetVideoInfoTag()->m_strPath.empty())
999 strDir = item->GetVideoInfoTag()->m_strPath;
1002 strDir = URIUtils::GetDirectory(item->GetPath());
1004 SScanSettings settings;
1005 bool foundDirectly = false;
1006 scraper = m_database.GetScraperForPath(strDir, settings, foundDirectly);
1009 !(m_database.HasMovieInfo(item->GetPath()) ||
1010 m_database.HasTvShowInfo(strDir) ||
1011 m_database.HasEpisodeInfo(item->GetPath())))
1016 if (scraper && scraper->Content() == CONTENT_TVSHOWS && foundDirectly && !settings.parent_name_root) // dont lookup on root tvshow folder
1020 OnInfo(item.get(), scraper);
1025 void CGUIWindowVideoBase::OnRestartItem(int iItem)
1027 CGUIMediaWindow::OnClick(iItem);
1030 CStdString CGUIWindowVideoBase::GetResumeString(const CFileItem &item)
1032 CStdString resumeString;
1033 int startOffset = 0, startPart = 0;
1034 GetResumeItemOffset(&item, startOffset, startPart);
1035 if (startOffset > 0)
1037 resumeString = StringUtils::Format(g_localizeStrings.Get(12022).c_str(), StringUtils::SecondsToTimeString(startOffset/75).c_str());
1040 CStdString partString = StringUtils::Format(g_localizeStrings.Get(23051).c_str(), startPart);
1041 resumeString += " (" + partString + ")";
1044 return resumeString;
1047 bool CGUIWindowVideoBase::ShowResumeMenu(CFileItem &item)
1049 if (!item.m_bIsFolder && !item.IsLiveTV())
1051 CStdString resumeString = GetResumeString(item);
1052 if (!resumeString.empty())
1053 { // prompt user whether they wish to resume
1054 CContextButtons choices;
1055 choices.Add(1, resumeString);
1056 choices.Add(2, 12021); // start from the beginning
1057 int retVal = CGUIDialogContextMenu::ShowAndGetChoice(choices);
1059 return false; // don't do anything
1061 item.m_lStartOffset = STARTOFFSET_RESUME;
1067 bool CGUIWindowVideoBase::ShowPlaySelection(CFileItemPtr& item)
1069 /* if asked to resume somewhere, we should not show anything */
1070 if (item->m_lStartOffset)
1074 if (item->IsVideoDb())
1075 path = item->GetVideoInfoTag()->m_strFileNameAndPath;
1077 path = item->GetPath();
1079 if (URIUtils::GetFileName(path) == "index.bdmv")
1081 CStdString root = URIUtils::GetParentPath(path);
1082 URIUtils::RemoveSlashAtEnd(root);
1083 if(URIUtils::GetFileName(root) == "BDMV")
1085 CURL url("bluray://");
1086 url.SetHostName(URIUtils::GetParentPath(root));
1087 return ShowPlaySelection(item, url.Get());
1091 if (URIUtils::HasExtension(path, ".iso|.img"))
1093 CURL url2("udf://");
1094 url2.SetHostName(item->GetPath());
1095 url2.SetFileName("BDMV/index.bdmv");
1096 if (CFile::Exists(url2.Get()))
1098 url2.SetFileName("");
1100 CURL url("bluray://");
1101 url.SetHostName(url2.Get());
1102 return ShowPlaySelection(item, url.Get());
1108 bool CGUIWindowVideoBase::ShowPlaySelection(CFileItemPtr& item, const CStdString& directory)
1111 CFileItemList items;
1113 if (!XFILE::CDirectory::GetDirectory(directory, items, XFILE::CDirectory::CHints(), true))
1115 CLog::Log(LOGERROR, "CGUIWindowVideoBase::ShowPlaySelection - Failed to get play directory for %s", directory.c_str());
1119 if (items.Size() == 0)
1121 CLog::Log(LOGERROR, "CGUIWindowVideoBase::ShowPlaySelection - Failed to get any items %s", directory.c_str());
1125 CGUIDialogSelect* dialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1129 dialog->SetHeading(25006 /* Select playback item */);
1130 dialog->SetItems(&items);
1131 dialog->SetUseDetails(true);
1134 CFileItemPtr item_new = dialog->GetSelectedItem();
1135 if(!item_new || dialog->GetSelectedLabel() < 0)
1137 CLog::Log(LOGDEBUG, "CGUIWindowVideoBase::ShowPlaySelection - User aborted %s", directory.c_str());
1141 if(item_new->m_bIsFolder == false)
1143 item.reset(new CFileItem(*item));
1144 item->SetProperty("original_listitem_url", item->GetPath());
1145 item->SetPath(item_new->GetPath());
1150 if(!XFILE::CDirectory::GetDirectory(item_new->GetPath(), items, XFILE::CDirectory::CHints(), true) || items.Size() == 0)
1152 CLog::Log(LOGERROR, "CGUIWindowVideoBase::ShowPlaySelection - Failed to get any items %s", item_new->GetPath().c_str());
1160 bool CGUIWindowVideoBase::OnResumeItem(int iItem)
1162 if (iItem < 0 || iItem >= m_vecItems->Size()) return true;
1163 CFileItemPtr item = m_vecItems->Get(iItem);
1165 if (item->m_bIsFolder)
1167 // resuming directories isn't supported yet. play.
1172 CStdString resumeString = GetResumeString(*item);
1174 if (!resumeString.empty())
1176 CContextButtons choices;
1177 choices.Add(SELECT_ACTION_RESUME, resumeString);
1178 choices.Add(SELECT_ACTION_PLAY, 12021); // Start from beginning
1179 int value = CGUIDialogContextMenu::ShowAndGetChoice(choices);
1182 return OnFileAction(iItem, value);
1185 return OnFileAction(iItem, SELECT_ACTION_PLAY);
1188 void CGUIWindowVideoBase::GetContextButtons(int itemNumber, CContextButtons &buttons)
1191 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
1192 item = m_vecItems->Get(itemNumber);
1194 // contextual buttons
1195 if (item && !item->GetProperty("pluginreplacecontextitems").asBoolean())
1197 if (!item->IsParentFolder())
1199 CStdString path(item->GetPath());
1200 if (item->IsVideoDb() && item->HasVideoInfoTag())
1201 path = item->GetVideoInfoTag()->m_strFileNameAndPath;
1203 if (!item->GetPath().Equals("add") && !item->IsPlugin() &&
1204 !item->IsScript() && !item->IsAddonsPath() && !item->IsLiveTV())
1206 if (URIUtils::IsStack(path))
1209 if (m_database.GetStackTimes(path,times) || CFileItem(CStackDirectory::GetFirstStackedFile(path),false).IsDVDImage())
1210 buttons.Add(CONTEXT_BUTTON_PLAY_PART, 20324);
1213 // allow a folder to be ad-hoc queued and played by the default player
1214 if (item->m_bIsFolder || (item->IsPlayList() &&
1215 !g_advancedSettings.m_playlistAsFolders))
1217 buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 208);
1220 if (!m_vecItems->GetPath().empty() && !StringUtils::StartsWithNoCase(item->GetPath(), "newsmartplaylist://") && !StringUtils::StartsWithNoCase(item->GetPath(), "newtag://")
1221 && !m_vecItems->IsSourcesPath())
1223 buttons.Add(CONTEXT_BUTTON_QUEUE_ITEM, 13347); // Add to Playlist
1227 if (!m_vecItems->IsPlugin() && (item->IsPlugin() || item->IsScript()))
1228 buttons.Add(CONTEXT_BUTTON_INFO,24003); // Add-on info
1230 if (!item->m_bIsFolder && !(item->IsPlayList() && !g_advancedSettings.m_playlistAsFolders))
1232 VECPLAYERCORES vecCores;
1233 if (item->IsVideoDb())
1235 CFileItem item2(item->GetVideoInfoTag()->m_strFileNameAndPath, false);
1236 CPlayerCoreFactory::Get().GetPlayers(item2, vecCores);
1239 CPlayerCoreFactory::Get().GetPlayers(*item, vecCores);
1240 if (vecCores.size() > 1)
1241 buttons.Add(CONTEXT_BUTTON_PLAY_WITH, 15213);
1243 if (item->IsSmartPlayList())
1245 buttons.Add(CONTEXT_BUTTON_PLAY_PARTYMODE, 15216); // Play in Partymode
1248 // if autoresume is enabled then add restart video button
1249 // check to see if the Resume Video button is applicable
1250 // only if the video is NOT a DVD (in that case the resume button will be added by CGUIDialogContextMenu::GetContextButtons)
1251 if (!item->IsDVD() && HasResumeItemOffset(item.get()))
1253 buttons.Add(CONTEXT_BUTTON_RESUME_ITEM, GetResumeString(*(item.get()))); // Resume Video
1255 //if the item isn't a folder or script, is a member of a list rather than a single item
1256 //and we're not on the last element of the list,
1257 //then add add either 'play from here' or 'play only this' depending on default behaviour
1258 if (!(item->m_bIsFolder || item->IsScript()) && m_vecItems->Size() > 1 && itemNumber < m_vecItems->Size()-1)
1260 if (!CSettings::Get().GetBool("videoplayer.autoplaynextitem"))
1261 buttons.Add(CONTEXT_BUTTON_PLAY_AND_QUEUE, 13412);
1263 buttons.Add(CONTEXT_BUTTON_PLAY_ONLY_THIS, 13434);
1265 if (item->IsSmartPlayList() || m_vecItems->IsSmartPlayList())
1266 buttons.Add(CONTEXT_BUTTON_EDIT_SMART_PLAYLIST, 586);
1269 CGUIMediaWindow::GetContextButtons(itemNumber, buttons);
1272 bool CGUIWindowVideoBase::OnPlayStackPart(int iItem)
1274 if (iItem < 0 || iItem >= m_vecItems->Size())
1277 CFileItemPtr stack = m_vecItems->Get(iItem);
1278 CStdString path(stack->GetPath());
1279 if (stack->IsVideoDb())
1280 path = stack->GetVideoInfoTag()->m_strFileNameAndPath;
1282 if (!URIUtils::IsStack(path))
1285 CFileItemList parts;
1286 CDirectory::GetDirectory(path,parts);
1287 CGUIDialogFileStacking* dlg = (CGUIDialogFileStacking*)g_windowManager.GetWindow(WINDOW_DIALOG_FILESTACKING);
1288 if (!dlg) return true;
1289 dlg->SetNumberOfFiles(parts.Size());
1291 int selectedFile = dlg->GetSelectedFile();
1292 if (selectedFile > 0)
1295 if (CFileItem(CStackDirectory::GetFirstStackedFile(path),false).IsDVDImage())
1297 CStdString resumeString = CGUIWindowVideoBase::GetResumeString(*(parts[selectedFile - 1].get()));
1298 stack->m_lStartOffset = 0;
1299 if (!resumeString.empty())
1301 CContextButtons choices;
1302 choices.Add(SELECT_ACTION_RESUME, resumeString);
1303 choices.Add(SELECT_ACTION_PLAY, 12021); // Start from beginning
1304 int value = CGUIDialogContextMenu::ShowAndGetChoice(choices);
1305 if (value == SELECT_ACTION_RESUME)
1306 GetResumeItemOffset(parts[selectedFile - 1].get(), stack->m_lStartOffset, stack->m_lStartPartNumber);
1307 else if (value != SELECT_ACTION_PLAY)
1308 return false; // if not selected PLAY, then we changed our mind so return
1310 stack->m_lStartPartNumber = selectedFile;
1315 if (selectedFile > 1)
1318 if (m_database.GetStackTimes(path,times))
1319 stack->m_lStartOffset = times[selectedFile-2]*75; // wtf?
1322 stack->m_lStartOffset = 0;
1331 bool CGUIWindowVideoBase::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
1334 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
1335 item = m_vecItems->Get(itemNumber);
1338 case CONTEXT_BUTTON_SET_CONTENT:
1340 OnAssignContent(item->HasVideoInfoTag() && !item->GetVideoInfoTag()->m_strPath.empty() ? item->GetVideoInfoTag()->m_strPath : item->GetPath());
1343 case CONTEXT_BUTTON_PLAY_PART:
1345 if (OnPlayStackPart(itemNumber))
1347 // call CGUIMediaWindow::OnClick() as otherwise autoresume will kick in
1348 CGUIMediaWindow::OnClick(itemNumber);
1354 case CONTEXT_BUTTON_QUEUE_ITEM:
1355 OnQueueItem(itemNumber);
1358 case CONTEXT_BUTTON_PLAY_ITEM:
1359 PlayItem(itemNumber);
1362 case CONTEXT_BUTTON_PLAY_WITH:
1364 VECPLAYERCORES vecCores;
1365 if (item->IsVideoDb())
1367 CFileItem item2(*item->GetVideoInfoTag());
1368 CPlayerCoreFactory::Get().GetPlayers(item2, vecCores);
1371 CPlayerCoreFactory::Get().GetPlayers(*item, vecCores);
1372 g_application.m_eForcedNextPlayer = CPlayerCoreFactory::Get().SelectPlayerDialog(vecCores);
1373 if (g_application.m_eForcedNextPlayer != EPC_NONE)
1374 OnClick(itemNumber);
1378 case CONTEXT_BUTTON_PLAY_PARTYMODE:
1379 g_partyModeManager.Enable(PARTYMODECONTEXT_VIDEO, m_vecItems->Get(itemNumber)->GetPath());
1382 case CONTEXT_BUTTON_RESTART_ITEM:
1383 OnRestartItem(itemNumber);
1386 case CONTEXT_BUTTON_RESUME_ITEM:
1387 return OnFileAction(itemNumber, SELECT_ACTION_RESUME);
1389 case CONTEXT_BUTTON_INFO:
1393 case CONTEXT_BUTTON_SCAN:
1397 ADDON::ScraperPtr info;
1398 SScanSettings settings;
1399 GetScraperForItem(item.get(), info, settings);
1400 CStdString strPath = item->GetPath();
1401 if (item->IsVideoDb() && (!item->m_bIsFolder || item->GetVideoInfoTag()->m_strPath.empty()))
1404 if (item->IsVideoDb())
1405 strPath = item->GetVideoInfoTag()->m_strPath;
1407 if (!info || info->Content() == CONTENT_NONE)
1410 if (item->m_bIsFolder)
1412 m_database.SetPathHash(strPath,""); // to force scan
1413 OnScan(strPath, true);
1416 OnInfo(item.get(),info);
1420 case CONTEXT_BUTTON_DELETE:
1421 OnDeleteItem(itemNumber);
1423 case CONTEXT_BUTTON_EDIT_SMART_PLAYLIST:
1425 CStdString playlist = m_vecItems->Get(itemNumber)->IsSmartPlayList() ? m_vecItems->Get(itemNumber)->GetPath() : m_vecItems->GetPath(); // save path as activatewindow will destroy our items
1426 if (CGUIDialogSmartPlaylistEditor::EditPlaylist(playlist, "video"))
1427 Refresh(true); // need to update
1430 case CONTEXT_BUTTON_RENAME:
1431 OnRenameItem(itemNumber);
1433 case CONTEXT_BUTTON_MARK_WATCHED:
1435 int newSelection = m_viewControl.GetSelectedItem() + 1;
1436 CGUIDialogVideoInfo::MarkWatched(item, true);
1437 m_viewControl.SetSelectedItem(newSelection);
1439 CUtil::DeleteVideoDatabaseDirectoryCache();
1443 case CONTEXT_BUTTON_MARK_UNWATCHED:
1444 CGUIDialogVideoInfo::MarkWatched(item, false);
1445 CUtil::DeleteVideoDatabaseDirectoryCache();
1448 case CONTEXT_BUTTON_PLAY_AND_QUEUE:
1449 return OnPlayAndQueueMedia(item);
1450 case CONTEXT_BUTTON_PLAY_ONLY_THIS:
1451 return OnPlayMedia(itemNumber);
1455 return CGUIMediaWindow::OnContextButton(itemNumber, button);
1458 bool CGUIWindowVideoBase::OnPlayMedia(int iItem)
1460 if ( iItem < 0 || iItem >= (int)m_vecItems->Size() )
1463 CFileItemPtr pItem = m_vecItems->Get(iItem);
1466 if (g_partyModeManager.IsEnabled(PARTYMODECONTEXT_VIDEO))
1468 CPlayList playlistTemp;
1469 playlistTemp.Add(pItem);
1470 g_partyModeManager.AddUserSongs(playlistTemp, true);
1474 // Reset Playlistplayer, playback started now does
1475 // not use the playlistplayer.
1476 g_playlistPlayer.Reset();
1477 g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_NONE);
1479 CFileItem item(*pItem);
1480 if (pItem->IsVideoDb())
1482 item.SetPath(pItem->GetVideoInfoTag()->m_strFileNameAndPath);
1483 item.SetProperty("original_listitem_url", pItem->GetPath());
1485 CLog::Log(LOGDEBUG, "%s %s", __FUNCTION__, CURL::GetRedacted(item.GetPath()).c_str());
1487 if (StringUtils::StartsWith(item.GetPath(), "pvr://recordings/"))
1489 if (!g_PVRManager.IsStarted())
1492 /* For recordings we check here for a available stream URL */
1493 CFileItemPtr tag = g_PVRRecordings->GetByPath(item.GetPath());
1494 if (tag && tag->HasPVRRecordingInfoTag() && !tag->GetPVRRecordingInfoTag()->m_strStreamURL.empty())
1496 CStdString stream = tag->GetPVRRecordingInfoTag()->m_strStreamURL;
1498 /* Isolate the folder from the filename */
1499 size_t found = stream.find_last_of("/");
1500 if (found == CStdString::npos)
1501 found = stream.find_last_of("\\");
1503 if (found != CStdString::npos)
1505 /* Check here for asterix at the begin of the filename */
1506 if (stream[found+1] == '*')
1508 /* Create a "stack://" url with all files matching the extension */
1509 CStdString ext = URIUtils::GetExtension(stream);
1510 CStdString dir = stream.substr(0, found).c_str();
1512 CFileItemList items;
1513 CDirectory::GetDirectory(dir, items);
1514 items.Sort(SortByFile, SortOrderAscending);
1517 for (int i = 0; i < items.Size(); ++i)
1519 if (URIUtils::HasExtension(items[i]->GetPath(), ext))
1523 if (stack.size() > 0)
1525 /* If we have a stack change the path of the item to it */
1526 CStackDirectory dir;
1527 CStdString stackPath = dir.ConstructStackPath(items, stack);
1528 item.SetPath(stackPath);
1533 /* If no asterix is present play only the given stream URL */
1534 item.SetPath(stream);
1539 CLog::Log(LOGERROR, "CGUIWindowTV: Can't open recording, no valid filename!");
1540 CGUIDialogOK::ShowAndGetInput(19033,0,19036,0);
1551 bool CGUIWindowVideoBase::OnPlayAndQueueMedia(const CFileItemPtr &item)
1553 // Get the current playlist and make sure it is not shuffled
1554 int iPlaylist = m_guiState->GetPlaylist();
1555 if (iPlaylist != PLAYLIST_NONE && g_playlistPlayer.IsShuffled(iPlaylist))
1556 g_playlistPlayer.SetShuffle(iPlaylist, false);
1558 CFileItemPtr movieItem(new CFileItem(*item));
1559 if(!ShowPlaySelection(movieItem))
1562 // Call the base method to actually queue the items
1563 // and start playing the given item
1564 return CGUIMediaWindow::OnPlayAndQueueMedia(movieItem);
1567 void CGUIWindowVideoBase::PlayMovie(const CFileItem *item)
1569 CFileItemPtr movieItem(new CFileItem(*item));
1571 if(!ShowPlaySelection(movieItem))
1574 g_playlistPlayer.Reset();
1575 g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_VIDEO);
1576 CPlayList& playlist = g_playlistPlayer.GetPlaylist(PLAYLIST_VIDEO);
1578 playlist.Add(movieItem);
1580 if(m_thumbLoader.IsLoading())
1581 m_thumbLoader.StopAsync();
1584 g_playlistPlayer.Play(0);
1586 if(!g_application.m_pPlayer->IsPlayingVideo())
1587 m_thumbLoader.Load(*m_vecItems);
1590 void CGUIWindowVideoBase::OnDeleteItem(int iItem)
1592 if ( iItem < 0 || iItem >= m_vecItems->Size())
1595 OnDeleteItem(m_vecItems->Get(iItem));
1598 m_viewControl.SetSelectedItem(iItem);
1601 void CGUIWindowVideoBase::OnDeleteItem(CFileItemPtr item)
1603 // HACK: stacked files need to be treated as folders in order to be deleted
1604 if (item->IsStack())
1605 item->m_bIsFolder = true;
1606 if (CProfilesManager::Get().GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE &&
1607 CProfilesManager::Get().GetCurrentProfile().filesLocked())
1609 if (!g_passwordManager.IsMasterLockUnlocked(true))
1613 if ((CSettings::Get().GetBool("filelists.allowfiledeletion") ||
1614 m_vecItems->GetPath().Equals("special://videoplaylists/")) &&
1615 CUtil::SupportsWriteFileOperations(item->GetPath()))
1616 CFileUtils::DeleteItem(item);
1619 void CGUIWindowVideoBase::LoadPlayList(const CStdString& strPlayList, int iPlayList /* = PLAYLIST_VIDEO */)
1621 // if partymode is active, we disable it
1622 if (g_partyModeManager.IsEnabled())
1623 g_partyModeManager.Disable();
1625 // load a playlist like .m3u, .pls
1626 // first get correct factory to load playlist
1627 auto_ptr<CPlayList> pPlayList (CPlayListFactory::Create(strPlayList));
1628 if (pPlayList.get())
1631 if (!pPlayList->Load(strPlayList))
1633 CGUIDialogOK::ShowAndGetInput(6, 0, 477, 0);
1634 return; //hmmm unable to load playlist?
1638 if (g_application.ProcessAndStartPlaylist(strPlayList, *pPlayList, iPlayList))
1640 if (m_guiState.get())
1641 m_guiState->SetPlaylistDirectory("playlistvideo://");
1645 void CGUIWindowVideoBase::PlayItem(int iItem)
1647 // restrictions should be placed in the appropiate window code
1648 // only call the base code if the item passes since this clears
1649 // the currently playing temp playlist
1651 const CFileItemPtr pItem = m_vecItems->Get(iItem);
1652 // if its a folder, build a temp playlist
1653 if (pItem->m_bIsFolder && !pItem->IsPlugin())
1655 // take a copy so we can alter the queue state
1656 CFileItemPtr item(new CFileItem(*m_vecItems->Get(iItem)));
1658 // Allow queuing of unqueueable items
1659 // when we try to queue them directly
1660 if (!item->CanQueue())
1661 item->SetCanQueue(true);
1664 if (item->IsParentFolder())
1667 // recursively add items to list
1668 CFileItemList queuedItems;
1669 AddItemToPlayList(item, queuedItems);
1671 g_playlistPlayer.ClearPlaylist(PLAYLIST_VIDEO);
1672 g_playlistPlayer.Reset();
1673 g_playlistPlayer.Add(PLAYLIST_VIDEO, queuedItems);
1674 g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_VIDEO);
1675 g_playlistPlayer.Play();
1677 else if (pItem->IsPlayList())
1679 // load the playlist the old way
1680 LoadPlayList(pItem->GetPath(), PLAYLIST_VIDEO);
1684 // single item, play it
1689 bool CGUIWindowVideoBase::Update(const CStdString &strDirectory, bool updateFilterPath /* = true */)
1691 if (m_thumbLoader.IsLoading())
1692 m_thumbLoader.StopThread();
1694 if (!CGUIMediaWindow::Update(strDirectory, updateFilterPath))
1697 // might already be running from GetGroupedItems
1698 if (!m_thumbLoader.IsLoading())
1699 m_thumbLoader.Load(*m_vecItems);
1704 bool CGUIWindowVideoBase::GetDirectory(const CStdString &strDirectory, CFileItemList &items)
1706 bool bResult = CGUIMediaWindow::GetDirectory(strDirectory, items);
1708 // add in the "New Playlist" item if we're in the playlists folder
1709 if ((items.GetPath() == "special://videoplaylists/") && !items.Contains("newplaylist://"))
1711 CFileItemPtr newPlaylist(new CFileItem(CProfilesManager::Get().GetUserDataItem("PartyMode-Video.xsp"),false));
1712 newPlaylist->SetLabel(g_localizeStrings.Get(16035));
1713 newPlaylist->SetLabelPreformated(true);
1714 newPlaylist->m_bIsFolder = true;
1715 items.Add(newPlaylist);
1717 /* newPlaylist.reset(new CFileItem("newplaylist://", false));
1718 newPlaylist->SetLabel(g_localizeStrings.Get(525));
1719 newPlaylist->SetLabelPreformated(true);
1720 items.Add(newPlaylist);
1722 newPlaylist.reset(new CFileItem("newsmartplaylist://video", false));
1723 newPlaylist->SetLabel(g_localizeStrings.Get(21437)); // "new smart playlist..."
1724 newPlaylist->SetLabelPreformated(true);
1725 items.Add(newPlaylist);
1728 m_stackingAvailable = StackingAvailable(items);
1729 // we may also be in a tvshow files listing
1730 // (ideally this should be removed, and our stack regexps tidied up if necessary
1731 // No "normal" episodes should stack, and multi-parts should be supported)
1732 ADDON::ScraperPtr info = m_database.GetScraperForPath(strDirectory);
1733 if (info && info->Content() == CONTENT_TVSHOWS)
1734 m_stackingAvailable = false;
1736 if (m_stackingAvailable && !items.IsStack() && CSettings::Get().GetBool("myvideos.stackvideos"))
1742 bool CGUIWindowVideoBase::StackingAvailable(const CFileItemList &items)
1744 CURL url(items.GetPath());
1745 return !(items.IsTuxBox() || items.IsPlugin() ||
1746 items.IsAddonsPath() || items.IsRSS() ||
1747 items.IsInternetStream() || items.IsVideoDb() ||
1748 url.GetProtocol() == "playlistvideo");
1751 void CGUIWindowVideoBase::GetGroupedItems(CFileItemList &items)
1753 CGUIMediaWindow::GetGroupedItems(items);
1757 if (items.HasProperty(PROPERTY_GROUP_BY))
1758 group = items.GetProperty(PROPERTY_GROUP_BY).asString();
1759 if (items.HasProperty(PROPERTY_GROUP_MIXED))
1760 mixed = items.GetProperty(PROPERTY_GROUP_MIXED).asBoolean();
1762 // group == "none" completely supresses any grouping
1763 if (!StringUtils::EqualsNoCase(group, "none"))
1765 CQueryParams params;
1766 CVideoDatabaseDirectory dir;
1767 dir.GetQueryParams(items.GetPath(), params);
1768 VIDEODATABASEDIRECTORY::NODE_TYPE nodeType = CVideoDatabaseDirectory::GetDirectoryChildType(m_strFilterPath);
1769 if (items.GetContent().Equals("movies") && params.GetSetId() <= 0 &&
1770 nodeType == NODE_TYPE_TITLE_MOVIES &&
1771 (CSettings::Get().GetBool("videolibrary.groupmoviesets") || (StringUtils::EqualsNoCase(group, "sets") && mixed)))
1773 CFileItemList groupedItems;
1774 if (GroupUtils::Group(GroupBySet, m_strFilterPath, items, groupedItems, GroupAttributeIgnoreSingleItems))
1777 items.Append(groupedItems);
1782 // reload thumbs after filtering and grouping
1783 if (m_thumbLoader.IsLoading())
1784 m_thumbLoader.StopThread();
1786 m_thumbLoader.Load(items);
1789 bool CGUIWindowVideoBase::CheckFilterAdvanced(CFileItemList &items) const
1791 CStdString content = items.GetContent();
1792 if ((items.IsVideoDb() || CanContainFilter(m_strFilterPath)) &&
1793 (content.Equals("movies") || content.Equals("tvshows") || content.Equals("episodes") || content.Equals("musicvideos")))
1799 bool CGUIWindowVideoBase::CanContainFilter(const CStdString &strDirectory) const
1801 return StringUtils::StartsWithNoCase(strDirectory, "videodb://");
1804 void CGUIWindowVideoBase::AddToDatabase(int iItem)
1806 if (iItem < 0 || iItem >= m_vecItems->Size())
1809 CFileItemPtr pItem = m_vecItems->Get(iItem);
1810 if (pItem->IsParentFolder() || pItem->m_bIsFolder)
1813 CVideoInfoTag movie;
1817 // enter a new title
1818 CStdString strTitle = pItem->GetLabel();
1819 if (!CGUIKeyboardFactory::ShowAndGetInput(strTitle, g_localizeStrings.Get(528), false)) // Enter Title
1823 CGUIDialogSelect* pSelect = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1827 pSelect->SetHeading(530); // Select Genre
1829 CFileItemList items;
1830 if (!CDirectory::GetDirectory("videodb://movies/genres/", items))
1832 pSelect->SetItems(&items);
1833 pSelect->EnableButton(true, 531); // New Genre
1835 CStdString strGenre;
1836 int iSelected = pSelect->GetSelectedLabel();
1838 strGenre = items[iSelected]->GetLabel();
1839 else if (!pSelect->IsButtonPressed())
1842 // enter new genre string
1843 if (strGenre.empty())
1845 strGenre = g_localizeStrings.Get(532); // Manual Addition
1846 if (!CGUIKeyboardFactory::ShowAndGetInput(strGenre, g_localizeStrings.Get(533), false)) // Enter Genre
1847 return; // user backed out
1848 if (strGenre.empty())
1849 return; // no genre string
1853 movie.m_strTitle = strTitle;
1854 movie.m_genre = StringUtils::Split(strGenre, g_advancedSettings.m_videoItemSeparator);
1856 // everything is ok, so add to database
1858 int idMovie = m_database.AddMovie(pItem->GetPath());
1859 movie.m_strIMDBNumber = StringUtils::Format("xx%08i", idMovie);
1860 m_database.SetDetailsForMovie(pItem->GetPath(), movie, pItem->GetArt());
1864 CGUIDialogOK::ShowAndGetInput(20177, movie.m_strTitle, StringUtils::Join(movie.m_genre, g_advancedSettings.m_videoItemSeparator), movie.m_strIMDBNumber);
1866 // library view cache needs to be cleared
1867 CUtil::DeleteVideoDatabaseDirectoryCache();
1870 /// \brief Search the current directory for a string got from the virtual keyboard
1871 void CGUIWindowVideoBase::OnSearch()
1873 CStdString strSearch;
1874 if (!CGUIKeyboardFactory::ShowAndGetInput(strSearch, g_localizeStrings.Get(16017), false))
1877 StringUtils::ToLower(strSearch);
1880 m_dlgProgress->SetHeading(194);
1881 m_dlgProgress->SetLine(0, strSearch);
1882 m_dlgProgress->SetLine(1, "");
1883 m_dlgProgress->SetLine(2, "");
1884 m_dlgProgress->StartModal();
1885 m_dlgProgress->Progress();
1887 CFileItemList items;
1888 DoSearch(strSearch, items);
1891 m_dlgProgress->Close();
1895 CGUIDialogSelect* pDlgSelect = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1896 pDlgSelect->Reset();
1897 pDlgSelect->SetHeading(283);
1899 for (int i = 0; i < (int)items.Size(); i++)
1901 CFileItemPtr pItem = items[i];
1902 pDlgSelect->Add(pItem->GetLabel());
1905 pDlgSelect->DoModal();
1907 int iItem = pDlgSelect->GetSelectedLabel();
1911 CFileItemPtr pSelItem = items[iItem];
1913 OnSearchItemFound(pSelItem.get());
1917 CGUIDialogOK::ShowAndGetInput(194, 284, 0, 0);
1921 /// \brief React on the selected search item
1922 /// \param pItem Search result item
1923 void CGUIWindowVideoBase::OnSearchItemFound(const CFileItem* pSelItem)
1925 if (pSelItem->m_bIsFolder)
1927 CStdString strPath = pSelItem->GetPath();
1928 CStdString strParentPath;
1929 URIUtils::GetParentPath(strPath, strParentPath);
1931 Update(strParentPath);
1933 if (pSelItem->IsVideoDb() && CSettings::Get().GetBool("myvideos.flatten"))
1934 SetHistoryForPath("");
1936 SetHistoryForPath(strParentPath);
1938 strPath = pSelItem->GetPath();
1940 if (pSelItem->IsSmb() && !URIUtils::HasSlashAtEnd(strPath))
1943 for (int i = 0; i < m_vecItems->Size(); i++)
1945 CFileItemPtr pItem = m_vecItems->Get(i);
1946 if (pItem->GetPath() == strPath)
1948 m_viewControl.SetSelectedItem(i);
1955 CStdString strPath = URIUtils::GetDirectory(pSelItem->GetPath());
1959 if (pSelItem->IsVideoDb() && CSettings::Get().GetBool("myvideos.flatten"))
1960 SetHistoryForPath("");
1962 SetHistoryForPath(strPath);
1964 for (int i = 0; i < (int)m_vecItems->Size(); i++)
1966 CFileItemPtr pItem = m_vecItems->Get(i);
1967 CURL url(pItem->GetPath());
1968 if (pSelItem->IsVideoDb())
1970 if (url.Get() == pSelItem->GetPath())
1972 m_viewControl.SetSelectedItem(i);
1977 m_viewControl.SetFocused();
1980 int CGUIWindowVideoBase::GetScraperForItem(CFileItem *item, ADDON::ScraperPtr &info, SScanSettings& settings)
1985 if (m_vecItems->IsPlugin() || m_vecItems->IsRSS())
1990 else if(m_vecItems->IsLiveTV())
1996 bool foundDirectly = false;
1997 info = m_database.GetScraperForPath(item->HasVideoInfoTag() && !item->GetVideoInfoTag()->m_strPath.empty() ? item->GetVideoInfoTag()->m_strPath : item->GetPath(), settings, foundDirectly);
1998 return foundDirectly ? 1 : 0;
2001 void CGUIWindowVideoBase::OnScan(const CStdString& strPath, bool scanAll)
2003 g_application.StartVideoScan(strPath, scanAll);
2006 CStdString CGUIWindowVideoBase::GetStartFolder(const CStdString &dir)
2008 if (dir.Equals("$PLAYLISTS") || dir.Equals("Playlists"))
2009 return "special://videoplaylists/";
2010 else if (dir.Equals("Plugins") || dir.Equals("Addons"))
2011 return "addons://sources/video/";
2012 return CGUIMediaWindow::GetStartFolder(dir);
2015 void CGUIWindowVideoBase::AppendAndClearSearchItems(CFileItemList &searchItems, const CStdString &prependLabel, CFileItemList &results)
2017 if (!searchItems.Size())
2020 searchItems.Sort(SortByLabel, SortOrderAscending, CSettings::Get().GetBool("filelists.ignorethewhensorting") ? SortAttributeIgnoreArticle : SortAttributeNone);
2021 for (int i = 0; i < searchItems.Size(); i++)
2022 searchItems[i]->SetLabel(prependLabel + searchItems[i]->GetLabel());
2023 results.Append(searchItems);
2025 searchItems.Clear();
2028 bool CGUIWindowVideoBase::OnUnAssignContent(const CStdString &path, int label1, int label2, int label3)
2033 if (CGUIDialogYesNo::ShowAndGetInput(label1,label2,label3,20022,bCanceled))
2035 CGUIDialogProgress *progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
2036 db.RemoveContentForPath(path, progress);
2038 CUtil::DeleteVideoDatabaseDirectoryCache();
2045 ADDON::ScraperPtr info;
2046 SScanSettings settings;
2047 settings.exclude = true;
2048 db.SetScraperForPath(path,info,settings);
2056 void CGUIWindowVideoBase::OnAssignContent(const CStdString &path)
2062 SScanSettings settings;
2063 ADDON::ScraperPtr info = db.GetScraperForPath(path, settings);
2065 ADDON::ScraperPtr info2(info);
2067 if (CGUIDialogContentSettings::Show(info, settings))
2069 if(settings.exclude || (!info && info2))
2071 OnUnAssignContent(path,20375,20340,20341);
2073 else if (info != info2)
2075 if (OnUnAssignContent(path,20442,20443,20444))
2080 db.SetScraperForPath(path,info,settings);
2084 g_application.StartVideoScan(path, true);
2088 void CGUIWindowVideoBase::OnInitWindow()
2090 CGUIMediaWindow::OnInitWindow();
2091 if (CMediaSettings::Get().GetVideoNeedsUpdate() == 63 && !g_application.IsVideoScanning() &&
2092 g_infoManager.GetLibraryBool(LIBRARY_HAS_VIDEO))
2094 // rescan of video library required
2095 if (CGUIDialogYesNo::ShowAndGetInput(799, 12351, 12352, 12354))
2097 CEdenVideoArtUpdater::Start();
2098 CMediaSettings::Get().SetVideoNeedsUpdate(0); // once is enough
2099 CSettings::Get().Save();