2 * Copyright (C) 2005-2012 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 "settings/Settings.h"
56 #include "settings/AdvancedSettings.h"
57 #include "settings/GUISettings.h"
58 #include "settings/GUIDialogContentSettings.h"
59 #include "guilib/LocalizeStrings.h"
60 #include "utils/StringUtils.h"
61 #include "utils/log.h"
62 #include "utils/FileUtils.h"
63 #include "interfaces/AnnouncementManager.h"
64 #include "pvr/PVRManager.h"
65 #include "pvr/recordings/PVRRecordings.h"
66 #include "utils/URIUtils.h"
67 #include "GUIUserMessages.h"
68 #include "addons/Skin.h"
69 #include "storage/MediaManager.h"
72 #include "utils/EdenVideoArtUpdater.h"
73 #include "GUIInfoManager.h"
74 #include "utils/GroupUtils.h"
75 #include "filesystem/File.h"
78 using namespace XFILE;
79 using namespace PLAYLIST;
80 using namespace VIDEODATABASEDIRECTORY;
81 using namespace VIDEO;
82 using namespace ADDON;
85 #define CONTROL_BTNVIEWASICONS 2
86 #define CONTROL_BTNSORTBY 3
87 #define CONTROL_BTNSORTASC 4
88 #define CONTROL_BTNTYPE 5
89 #define CONTROL_LABELFILES 12
91 #define CONTROL_PLAY_DVD 6
92 #define CONTROL_STACK 7
93 #define CONTROL_BTNSCAN 8
95 CGUIWindowVideoBase::CGUIWindowVideoBase(int id, const CStdString &xmlFile)
96 : CGUIMediaWindow(id, xmlFile)
98 m_thumbLoader.SetObserver(this);
99 m_thumbLoader.SetStreamDetailsObserver(this);
100 m_stackingAvailable = true;
103 CGUIWindowVideoBase::~CGUIWindowVideoBase()
107 bool CGUIWindowVideoBase::OnAction(const CAction &action)
109 if (action.GetID() == ACTION_SCAN_ITEM)
110 return OnContextButton(m_viewControl.GetSelectedItem(),CONTEXT_BUTTON_SCAN);
111 else if (action.GetID() == ACTION_SHOW_PLAYLIST)
113 if (g_playlistPlayer.GetCurrentPlaylist() == PLAYLIST_VIDEO ||
114 g_playlistPlayer.GetPlaylist(PLAYLIST_VIDEO).size() > 0)
116 g_windowManager.ActivateWindow(WINDOW_VIDEO_PLAYLIST);
121 return CGUIMediaWindow::OnAction(action);
124 bool CGUIWindowVideoBase::OnMessage(CGUIMessage& message)
126 switch ( message.GetMessage() )
128 case GUI_MSG_WINDOW_DEINIT:
129 if (m_thumbLoader.IsLoading())
130 m_thumbLoader.StopThread();
134 case GUI_MSG_WINDOW_INIT:
138 m_dlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
140 // save current window, unless the current window is the video playlist window
141 if (GetID() != WINDOW_VIDEO_PLAYLIST && g_settings.m_iVideoStartWindow != GetID())
143 g_settings.m_iVideoStartWindow = GetID();
147 return CGUIMediaWindow::OnMessage(message);
151 case GUI_MSG_CLICKED:
153 int iControl = message.GetSenderId();
154 if (iControl == CONTROL_STACK)
156 g_settings.m_videoStacking = !g_settings.m_videoStacking;
159 Update( m_vecItems->GetPath() );
161 #if defined(HAS_DVD_DRIVE)
162 else if (iControl == CONTROL_PLAY_DVD)
165 MEDIA_DETECT::CAutorun::PlayDiscAskResume(g_mediaManager.TranslateDevicePath(""));
168 else if (iControl == CONTROL_BTNTYPE)
170 CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTNTYPE);
171 g_windowManager.SendMessage(msg);
173 int nSelected = msg.GetParam1();
174 int nNewWindow = WINDOW_VIDEO_FILES;
178 nNewWindow = WINDOW_VIDEO_FILES;
181 nNewWindow = WINDOW_VIDEO_NAV;
185 if (nNewWindow != GetID())
187 g_settings.m_iVideoStartWindow = nNewWindow;
189 g_windowManager.ChangeActiveWindow(nNewWindow);
190 CGUIMessage msg2(GUI_MSG_SETFOCUS, nNewWindow, CONTROL_BTNTYPE);
191 g_windowManager.SendMessage(msg2);
196 else if (m_viewControl.HasControl(iControl)) // list/thumb control
199 int iItem = m_viewControl.GetSelectedItem();
200 int iAction = message.GetParam1();
202 // iItem is checked for validity inside these routines
203 if (iAction == ACTION_QUEUE_ITEM || iAction == ACTION_MOUSE_MIDDLE_CLICK)
208 else if (iAction == ACTION_SHOW_INFO)
210 return OnInfo(iItem);
212 else if (iAction == ACTION_PLAYER_PLAY && !g_application.IsPlayingVideo())
214 return OnResumeItem(iItem);
216 else if (iAction == ACTION_DELETE_ITEM)
218 // is delete allowed?
219 if (g_settings.GetCurrentProfile().canWriteDatabases())
221 // must be at the title window
222 if (GetID() == WINDOW_VIDEO_NAV)
225 // or be at the files window and have file deletion enabled
226 else if (GetID() == WINDOW_VIDEO_FILES && g_guiSettings.GetBool("filelists.allowfiledeletion"))
229 // or be at the video playlists location
230 else if (m_vecItems->GetPath().Equals("special://videoplaylists/"))
245 return CGUIMediaWindow::OnMessage(message);
248 void CGUIWindowVideoBase::UpdateButtons()
250 // Remove labels from the window selection
251 CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_BTNTYPE);
252 g_windowManager.SendMessage(msg);
254 // Add labels to the window selection
255 CStdString strItem = g_localizeStrings.Get(744); // Files
256 CGUIMessage msg2(GUI_MSG_LABEL_ADD, GetID(), CONTROL_BTNTYPE);
257 msg2.SetLabel(strItem);
258 g_windowManager.SendMessage(msg2);
260 strItem = g_localizeStrings.Get(14022); // Library
261 msg2.SetLabel(strItem);
262 g_windowManager.SendMessage(msg2);
264 // Select the current window as default item
265 int nWindow = g_settings.m_iVideoStartWindow-WINDOW_VIDEO_FILES;
266 CONTROL_SELECT_ITEM(CONTROL_BTNTYPE, nWindow);
268 CONTROL_ENABLE(CONTROL_BTNSCAN);
270 SET_CONTROL_LABEL(CONTROL_STACK, 14000); // Stack
271 SET_CONTROL_SELECTED(GetID(), CONTROL_STACK, g_settings.m_videoStacking);
272 CONTROL_ENABLE_ON_CONDITION(CONTROL_STACK, m_stackingAvailable);
274 CGUIMediaWindow::UpdateButtons();
277 void CGUIWindowVideoBase::OnInfo(CFileItem* pItem, const ADDON::ScraperPtr& scraper)
282 if (pItem->IsParentFolder() || pItem->m_bIsShareOrDrive || pItem->GetPath().Equals("add") ||
283 (pItem->IsPlayList() && !URIUtils::GetExtension(pItem->GetPath()).Equals(".strm")))
286 // ShowIMDB can kill the item as this window can be closed while we do it,
287 // so take a copy of the item now
288 CFileItem item(*pItem);
289 if (item.IsVideoDb() && item.HasVideoInfoTag())
291 if (item.GetVideoInfoTag()->m_type == "season")
292 { // clear out the art - we're really grabbing the info on the show here
295 item.SetPath(item.GetVideoInfoTag()->GetPath());
299 if (item.m_bIsFolder && scraper && scraper->Content() != CONTENT_TVSHOWS)
302 CDirectory::GetDirectory(item.GetPath(), items, g_settings.m_videoExtensions);
305 // check for media files
306 bool bFoundFile(false);
307 for (int i = 0; i < items.Size(); ++i)
309 CFileItemPtr item2 = items[i];
311 if (item2->IsVideo() && !item2->IsPlayList() &&
312 !CUtil::ExcludeFileOrFolder(item2->GetPath(), g_advancedSettings.m_moviesExcludeFromScanRegExps))
314 item.SetPath(item2->GetPath());
315 item.m_bIsFolder = false;
321 // no video file in this folder
324 CGUIDialogOK::ShowAndGetInput(13346,20349,20022,20022);
330 // we need to also request any thumbs be applied to the folder item
331 if (pItem->m_bIsFolder)
332 item.SetProperty("set_folder_thumb", pItem->GetPath());
334 bool modified = ShowIMDB(&item, scraper);
336 (g_windowManager.GetActiveWindow() == WINDOW_VIDEO_FILES ||
337 g_windowManager.GetActiveWindow() == WINDOW_VIDEO_NAV)) // since we can be called from the music library we need this check
339 int itemNumber = m_viewControl.GetSelectedItem();
341 m_viewControl.SetSelectedItem(itemNumber);
345 // ShowIMDB is called as follows:
346 // 1. To lookup info on a file.
347 // 2. To lookup info on a folder (which may or may not contain a file)
348 // 3. To lookup info just for fun (no file or folder related)
350 // We just need the item object for this.
351 // A "blank" item object is sent for 3.
352 // If a folder is sent, currently it sets strFolder and bFolder
353 // this is only used for setting the folder thumb, however.
357 // 1. Check database to see if we have this information already
358 // 2. Else, check for a nfoFile to get the URL
359 // 3. Run a loop to check for refresh
360 // 4. If no URL is present do a search to get the URL
361 // 4. Once we have the URL, download the details
362 // 5. Once we have the details, add to the database if necessary (case 1,2)
363 // and show the information.
364 // 6. Check for a refresh, and if so, go to 3.
366 bool CGUIWindowVideoBase::ShowIMDB(CFileItem *item, const ScraperPtr &info2)
369 CLog::Log(LOGDEBUG,"CGUIWindowVideoBase::ShowIMDB");
370 CLog::Log(LOGDEBUG," strMovie = [%s]", strMovie.c_str());
371 CLog::Log(LOGDEBUG," strFile = [%s]", strFile.c_str());
372 CLog::Log(LOGDEBUG," strFolder = [%s]", strFolder.c_str());
373 CLog::Log(LOGDEBUG," bFolder = [%s]", ((int)bFolder ? "true" : "false"));
376 CGUIDialogProgress* pDlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
377 CGUIDialogSelect* pDlgSelect = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
378 CGUIDialogVideoInfo* pDlgInfo = (CGUIDialogVideoInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_INFO);
380 ScraperPtr info(info2); // use this as nfo might change it..
382 if (!pDlgProgress) return false;
383 if (!pDlgSelect) return false;
384 if (!pDlgInfo) return false;
386 // 1. Check for already downloaded information, and if we have it, display our dialog
387 // Return if no Refresh is needed.
390 CVideoInfoTag movieDetails;
393 m_database.Open(); // since we can be called from the music library
395 if (info->Content() == CONTENT_MOVIES)
397 bHasInfo = m_database.GetMovieInfo(item->GetPath(), movieDetails);
399 if (info->Content() == CONTENT_TVSHOWS)
401 if (item->m_bIsFolder)
403 bHasInfo = m_database.GetTvShowInfo(item->GetPath(), movieDetails);
408 if (item->HasVideoInfoTag())
409 EpisodeHint = item->GetVideoInfoTag()->m_iEpisode;
411 if ((idEpisode = m_database.GetEpisodeId(item->GetPath(),EpisodeHint)) > -1)
414 m_database.GetEpisodeInfo(item->GetPath(), movieDetails, idEpisode);
419 // As we cannot add an episode to a non-existing tvshow entry, we have to check the parent directory
420 // to see if it`s already in our video database. If it's not yet part of the database we will exit here.
423 // NOTE: This will fail for episodes on multipath shares, as the parent path isn't what is stored in the
424 // database. Possible solutions are to store the paths in the db separately and rely on the show
425 // stacking stuff, or to modify GetTvShowId to do support multipath:// shares
426 CStdString strParentDirectory;
427 URIUtils::GetParentPath(item->GetPath(), strParentDirectory);
428 if (m_database.GetTvShowId(strParentDirectory) < 0)
430 CLog::Log(LOGERROR,"%s: could not add episode [%s]. tvshow does not exist yet..", __FUNCTION__, item->GetPath().c_str());
436 if (info->Content() == CONTENT_MUSICVIDEOS)
438 bHasInfo = m_database.GetMusicVideoInfo(item->GetPath(), movieDetails);
442 else if(item->HasVideoInfoTag())
445 movieDetails = *item->GetVideoInfoTag();
448 bool needsRefresh = false;
451 if (!info || info->Content() == CONTENT_NONE) // disable refresh button
452 movieDetails.m_strIMDBNumber = "xx"+movieDetails.m_strIMDBNumber;
453 *item->GetVideoInfoTag() = movieDetails;
454 pDlgInfo->SetMovie(item);
456 needsRefresh = pDlgInfo->NeedRefresh();
458 return pDlgInfo->HasUpdatedThumb();
461 // quietly return if Internet lookups are disabled
462 if (!g_settings.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 (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;
528 strHeading.Format(g_localizeStrings.Get(197),info->Name().c_str());
529 pDlgProgress->SetHeading(strHeading);
530 pDlgProgress->SetLine(0, movieName);
531 pDlgProgress->SetLine(1, "");
532 pDlgProgress->SetLine(2, "");
533 pDlgProgress->StartModal();
534 pDlgProgress->Progress();
536 // 4b. do the websearch
538 CVideoInfoDownloader imdb(info);
539 int returncode = imdb.FindMovie(movieName, movielist, pDlgProgress);
542 pDlgProgress->Close();
543 if (movielist.size() > 0)
546 if (info->Content() == CONTENT_TVSHOWS)
548 pDlgSelect->SetHeading(iString);
550 for (unsigned int i = 0; i < movielist.size(); ++i)
551 pDlgSelect->Add(movielist[i].strTitle);
552 pDlgSelect->EnableButton(true, 413); // manual
553 pDlgSelect->DoModal();
555 // and wait till user selects one
556 int iSelectedMovie = pDlgSelect->GetSelectedLabel();
557 if (iSelectedMovie >= 0)
559 scrUrl = movielist[iSelectedMovie];
560 CLog::Log(LOGDEBUG, "%s: user selected movie '%s' with URL '%s'",
561 __FUNCTION__, scrUrl.strTitle.c_str(), scrUrl.m_url[0].m_url.c_str());
563 else if (!pDlgSelect->IsButtonPressed())
566 return listNeedsUpdating; // user backed out
570 else if (returncode == -1 || !CVideoInfoScanner::DownloadFailed(pDlgProgress))
572 pDlgProgress->Close();
576 // 4c. Check if url is still empty - occurs if user has selected to do a manual
577 // lookup, or if the IMDb lookup failed or was cancelled.
578 if (!hasDetails && scrUrl.m_url.size() == 0)
580 // Check for cancel of the progress dialog
581 pDlgProgress->Close();
582 if (pDlgProgress->IsCanceled())
585 return listNeedsUpdating;
588 // Prompt the user to input the movieName
590 if (info->Content() == CONTENT_TVSHOWS)
592 if (!CGUIKeyboardFactory::ShowAndGetInput(movieName, g_localizeStrings.Get(iString), false))
595 return listNeedsUpdating; // user backed out
602 // 5. Download the movie information
603 // show dialog that we're downloading the movie info
606 item->SetArt("thumb", "");
607 item->SetArt("fanart", "");
610 CStdString strPath=item->GetPath();
611 if (item->IsVideoDb())
613 CFileItemPtr newItem(new CFileItem(*item->GetVideoInfoTag()));
615 strPath = item->GetVideoInfoTag()->m_strPath;
619 CFileItemPtr newItem(new CFileItem(*item));
623 if (item->m_bIsFolder)
624 list.SetPath(URIUtils::GetParentPath(strPath));
628 URIUtils::GetDirectory(strPath, path);
633 if (info->Content() == CONTENT_TVSHOWS)
635 if (item->m_bIsFolder)
640 if (info->Content() == CONTENT_MUSICVIDEOS)
642 pDlgProgress->SetHeading(iString);
643 pDlgProgress->SetLine(0, movieName);
644 pDlgProgress->SetLine(1, scrUrl.strTitle);
645 pDlgProgress->SetLine(2, "");
646 pDlgProgress->StartModal();
647 pDlgProgress->Progress();
650 if (info->Content() == CONTENT_MOVIES)
651 m_database.DeleteMovie(item->GetPath());
652 if (info->Content() == CONTENT_TVSHOWS && !item->m_bIsFolder)
653 m_database.DeleteEpisode(item->GetPath(),movieDetails.m_iDbId);
654 if (info->Content() == CONTENT_MUSICVIDEOS)
655 m_database.DeleteMusicVideo(item->GetPath());
656 if (info->Content() == CONTENT_TVSHOWS && item->m_bIsFolder)
658 if (pDlgInfo->RefreshAll())
659 m_database.DeleteTvShow(item->GetPath());
661 m_database.DeleteDetailsForTvShow(item->GetPath());
664 if (scanner.RetrieveVideoInfo(list,settings.parent_name_root,info->Content(),!ignoreNfo,&scrUrl,pDlgInfo->RefreshAll(),pDlgProgress))
666 if (info->Content() == CONTENT_MOVIES)
667 m_database.GetMovieInfo(item->GetPath(),movieDetails);
668 if (info->Content() == CONTENT_MUSICVIDEOS)
669 m_database.GetMusicVideoInfo(item->GetPath(),movieDetails);
670 if (info->Content() == CONTENT_TVSHOWS)
672 // update tvshow info to get updated episode numbers
673 if (item->m_bIsFolder)
674 m_database.GetTvShowInfo(item->GetPath(),movieDetails);
676 m_database.GetEpisodeInfo(item->GetPath(),movieDetails);
679 // got all movie details :-)
680 OutputDebugString("got details\n");
681 pDlgProgress->Close();
683 // now show the imdb info
684 OutputDebugString("show info\n");
686 // remove directory caches and reload images
687 CUtil::DeleteVideoDatabaseDirectoryCache();
688 CGUIMessage reload(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
691 *item->GetVideoInfoTag() = movieDetails;
692 pDlgInfo->SetMovie(item);
694 item->SetArt("thumb", pDlgInfo->GetThumbnail());
695 needsRefresh = pDlgInfo->NeedRefresh();
696 listNeedsUpdating = true;
700 pDlgProgress->Close();
701 if (pDlgProgress->IsCanceled())
704 return listNeedsUpdating; // user cancelled
706 CGUIDialogOK::ShowAndGetInput(195, movieName, 0, 0);
708 return listNeedsUpdating;
711 // 6. Check for a refresh
712 } while (needsRefresh);
714 return listNeedsUpdating;
717 void CGUIWindowVideoBase::OnQueueItem(int iItem)
719 // don't re-queue items from playlist window
720 if ( iItem < 0 || iItem >= m_vecItems->Size() || GetID() == WINDOW_VIDEO_PLAYLIST ) return ;
722 // we take a copy so that we can alter the queue state
723 CFileItemPtr item(new CFileItem(*m_vecItems->Get(iItem)));
724 if (item->IsRAR() || item->IsZIP())
727 // Allow queuing of unqueueable items
728 // when we try to queue them directly
729 if (!item->CanQueue())
730 item->SetCanQueue(true);
732 CFileItemList queuedItems;
733 AddItemToPlayList(item, queuedItems);
734 // if party mode, add items but DONT start playing
735 if (g_partyModeManager.IsEnabled(PARTYMODECONTEXT_VIDEO))
737 g_partyModeManager.AddUserSongs(queuedItems, false);
741 g_playlistPlayer.Add(PLAYLIST_VIDEO, queuedItems);
742 g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_VIDEO);
743 // video does not auto play on queue like music
744 m_viewControl.SetSelectedItem(iItem + 1);
747 void CGUIWindowVideoBase::AddItemToPlayList(const CFileItemPtr &pItem, CFileItemList &queuedItems)
749 if (!pItem->CanQueue() || pItem->IsRAR() || pItem->IsZIP() || pItem->IsParentFolder()) // no zip/rar enques thank you!
752 if (pItem->m_bIsFolder)
754 if (pItem->IsParentFolder())
757 // Check if we add a locked share
758 if ( pItem->m_bIsShareOrDrive )
760 CFileItem item = *pItem;
761 if ( !g_passwordManager.IsItemUnlocked( &item, "video" ) )
767 GetDirectory(pItem->GetPath(), items);
768 FormatAndSort(items);
770 int watchedMode = g_settings.GetWatchMode(items.GetContent());
771 bool unwatchedOnly = watchedMode == VIDEO_SHOW_UNWATCHED;
772 bool watchedOnly = watchedMode == VIDEO_SHOW_WATCHED;
773 for (int i = 0; i < items.Size(); ++i)
775 if (items[i]->m_bIsFolder)
777 CStdString strPath = items[i]->GetPath();
778 URIUtils::RemoveSlashAtEnd(strPath);
780 if (strPath.size() > 6)
782 CStdString strSub = strPath.substr(strPath.size()-6);
783 if (strPath.Mid(strPath.size()-6).Equals("sample")) // skip sample folders
787 else if (items[i]->HasVideoInfoTag() &&
788 ((unwatchedOnly && items[i]->GetVideoInfoTag()->m_playCount > 0) ||
789 (watchedOnly && items[i]->GetVideoInfoTag()->m_playCount <= 0)))
792 AddItemToPlayList(items[i], queuedItems);
798 if (pItem->IsPlayList())
800 auto_ptr<CPlayList> pPlayList (CPlayListFactory::Create(*pItem));
804 if (!pPlayList->Load(pItem->GetPath()))
806 CGUIDialogOK::ShowAndGetInput(6, 0, 477, 0);
807 return; //hmmm unable to load playlist?
810 CPlayList playlist = *pPlayList;
811 for (int i = 0; i < (int)playlist.size(); ++i)
813 AddItemToPlayList(playlist[i], queuedItems);
818 else if(pItem->IsInternetStream())
819 { // just queue the internet stream, it will be expanded on play
820 queuedItems.Add(pItem);
822 else if (pItem->IsPlugin() && pItem->GetProperty("isplayable") == "true")
823 { // a playable python files
824 queuedItems.Add(pItem);
826 else if (pItem->IsVideoDb())
827 { // this case is needed unless we allow IsVideo() to return true for videodb items,
828 // but then we have issues with playlists of videodb items
829 CFileItemPtr item(new CFileItem(*pItem->GetVideoInfoTag()));
830 queuedItems.Add(item);
832 else if (!pItem->IsNFO() && pItem->IsVideo())
834 queuedItems.Add(pItem);
839 void CGUIWindowVideoBase::GetResumeItemOffset(const CFileItem *item, int& startoffset, int& partNumber)
841 // do not resume livetv
842 if (item->IsLiveTV())
848 if (!item->IsNFO() && !item->IsPlayList())
850 if (item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_resumePoint.IsSet())
852 startoffset = (int)(item->GetVideoInfoTag()->m_resumePoint.timeInSeconds*75);
853 partNumber = item->GetVideoInfoTag()->m_resumePoint.partNumber;
858 CStdString strPath = item->GetPath();
859 if ((item->IsVideoDb() || item->IsDVD()) && item->HasVideoInfoTag())
860 strPath = item->GetVideoInfoTag()->m_strFileNameAndPath;
865 CLog::Log(LOGERROR, "%s - Cannot open VideoDatabase", __FUNCTION__);
868 if (db.GetResumeBookMark(strPath, bookmark))
870 startoffset = (int)(bookmark.timeInSeconds*75);
871 partNumber = bookmark.partNumber;
878 bool CGUIWindowVideoBase::HasResumeItemOffset(const CFileItem *item)
880 int startoffset = 0, partNumber = 0;
881 GetResumeItemOffset(item, startoffset, partNumber);
882 return startoffset > 0;
885 bool CGUIWindowVideoBase::OnClick(int iItem)
887 return CGUIMediaWindow::OnClick(iItem);
890 bool CGUIWindowVideoBase::OnSelect(int iItem)
892 if (iItem < 0 || iItem >= m_vecItems->Size())
895 CFileItemPtr item = m_vecItems->Get(iItem);
897 CStdString path = item->GetPath();
898 if (!item->m_bIsFolder && path != "add" && path != "addons://more/video" &&
899 path.Left(19) != "newsmartplaylist://" && path.Left(14) != "newplaylist://" && path.Left(9) != "newtag://")
900 return OnFileAction(iItem, g_guiSettings.GetInt("myvideos.selectaction"));
902 return CGUIMediaWindow::OnSelect(iItem);
905 bool CGUIWindowVideoBase::OnFileAction(int iItem, int action)
907 CFileItemPtr item = m_vecItems->Get(iItem);
909 // Reset the current start offset. The actual resume
910 // option is set in the switch, based on the action passed.
911 item->m_lStartOffset = 0;
915 case SELECT_ACTION_CHOOSE:
917 CContextButtons choices;
919 if (item->IsVideoDb())
921 CStdString itemPath(item->GetPath());
922 itemPath = item->GetVideoInfoTag()->m_strFileNameAndPath;
923 if (URIUtils::IsStack(itemPath) && CFileItem(CStackDirectory::GetFirstStackedFile(itemPath),false).IsDVDImage())
924 choices.Add(SELECT_ACTION_PLAYPART, 20324); // Play Part
927 CStdString resumeString = GetResumeString(*item);
928 if (!resumeString.IsEmpty())
930 choices.Add(SELECT_ACTION_RESUME, resumeString);
931 choices.Add(SELECT_ACTION_PLAY, 12021); // Start from beginning
934 choices.Add(SELECT_ACTION_PLAY, 208); // Play
936 choices.Add(SELECT_ACTION_INFO, 22081); // Info
937 choices.Add(SELECT_ACTION_MORE, 22082); // More
938 int value = CGUIDialogContextMenu::ShowAndGetChoice(choices);
942 return OnFileAction(iItem, value);
945 case SELECT_ACTION_PLAY_OR_RESUME:
946 return OnResumeItem(iItem);
947 case SELECT_ACTION_INFO:
951 case SELECT_ACTION_MORE:
954 case SELECT_ACTION_RESUME:
955 item->m_lStartOffset = STARTOFFSET_RESUME;
957 case SELECT_ACTION_PLAYPART:
958 if (!OnPlayStackPart(iItem))
961 case SELECT_ACTION_PLAY:
965 return OnClick(iItem);
968 bool CGUIWindowVideoBase::OnInfo(int iItem)
970 if (iItem < 0 || iItem >= m_vecItems->Size())
973 CFileItemPtr item = m_vecItems->Get(iItem);
975 if (item->GetPath().Equals("add") || item->IsParentFolder() ||
976 (item->IsPlayList() && !URIUtils::GetExtension(item->GetPath()).Equals(".strm")))
979 if (!m_vecItems->IsPlugin() && (item->IsPlugin() || item->IsScript()))
980 return CGUIDialogAddonInfo::ShowForItem(item);
982 ADDON::ScraperPtr scraper;
983 if (!m_vecItems->IsPlugin() && !m_vecItems->IsRSS() && !m_vecItems->IsLiveTV())
986 if (item->IsVideoDb() &&
987 item->HasVideoInfoTag() &&
988 !item->GetVideoInfoTag()->m_strPath.IsEmpty())
990 strDir = item->GetVideoInfoTag()->m_strPath;
993 URIUtils::GetDirectory(item->GetPath(),strDir);
995 SScanSettings settings;
996 bool foundDirectly = false;
997 scraper = m_database.GetScraperForPath(strDir, settings, foundDirectly);
1000 !(m_database.HasMovieInfo(item->GetPath()) ||
1001 m_database.HasTvShowInfo(strDir) ||
1002 m_database.HasEpisodeInfo(item->GetPath())))
1007 if (scraper && scraper->Content() == CONTENT_TVSHOWS && foundDirectly && !settings.parent_name_root) // dont lookup on root tvshow folder
1011 OnInfo(item.get(), scraper);
1016 void CGUIWindowVideoBase::OnRestartItem(int iItem)
1018 CGUIMediaWindow::OnClick(iItem);
1021 CStdString CGUIWindowVideoBase::GetResumeString(const CFileItem &item)
1023 CStdString resumeString;
1024 int startOffset = 0, startPart = 0;
1025 GetResumeItemOffset(&item, startOffset, startPart);
1026 if (startOffset > 0)
1028 resumeString.Format(g_localizeStrings.Get(12022).c_str(), StringUtils::SecondsToTimeString(startOffset/75).c_str());
1031 CStdString partString;
1032 partString.Format(g_localizeStrings.Get(23051).c_str(), startPart);
1033 resumeString += " (" + partString + ")";
1036 return resumeString;
1039 bool CGUIWindowVideoBase::ShowResumeMenu(CFileItem &item)
1041 if (!item.m_bIsFolder && !item.IsLiveTV())
1043 CStdString resumeString = GetResumeString(item);
1044 if (!resumeString.IsEmpty())
1045 { // prompt user whether they wish to resume
1046 CContextButtons choices;
1047 choices.Add(1, resumeString);
1048 choices.Add(2, 12021); // start from the beginning
1049 int retVal = CGUIDialogContextMenu::ShowAndGetChoice(choices);
1051 return false; // don't do anything
1053 item.m_lStartOffset = STARTOFFSET_RESUME;
1059 bool CGUIWindowVideoBase::ShowPlaySelection(CFileItemPtr& item)
1061 /* if asked to resume somewhere, we should not show anything */
1062 if (item->m_lStartOffset)
1065 if (item->IsBDFile())
1067 CStdString root = URIUtils::GetParentPath(item->GetPath());
1068 URIUtils::RemoveSlashAtEnd(root);
1069 if(URIUtils::GetFileName(root) == "BDMV")
1071 CURL url("bluray://");
1072 url.SetHostName(URIUtils::GetParentPath(root));
1073 return ShowPlaySelection(item, url.Get());
1077 CStdString ext = URIUtils::GetExtension(item->GetPath());
1079 if (ext == ".iso" || ext == ".img")
1081 CURL url2("udf://");
1082 url2.SetHostName(item->GetPath());
1083 url2.SetFileName("BDMV/index.bdmv");
1084 if (CFile::Exists(url2.Get()))
1086 url2.SetFileName("");
1088 CURL url("bluray://");
1089 url.SetHostName(url2.Get());
1090 return ShowPlaySelection(item, url.Get());
1096 bool CGUIWindowVideoBase::ShowPlaySelection(CFileItemPtr& item, const CStdString& directory)
1099 CFileItemList items;
1101 if (!XFILE::CDirectory::GetDirectory(directory, items, XFILE::CDirectory::CHints(), true))
1103 CLog::Log(LOGERROR, "CGUIWindowVideoBase::ShowPlaySelection - Failed to get play directory for %s", directory.c_str());
1107 if (items.Size() == 0)
1109 CLog::Log(LOGERROR, "CGUIWindowVideoBase::ShowPlaySelection - Failed to get any items %s", directory.c_str());
1113 CGUIDialogSelect* dialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1117 dialog->SetHeading(25006 /* Select playback item */);
1118 dialog->SetItems(&items);
1119 dialog->SetUseDetails(true);
1122 CFileItemPtr item_new = dialog->GetSelectedItem();
1123 if(!item_new || dialog->GetSelectedLabel() < 0)
1125 CLog::Log(LOGDEBUG, "CGUIWindowVideoBase::ShowPlaySelection - User aborted %s", directory.c_str());
1129 if(item_new->m_bIsFolder == false)
1131 item.reset(new CFileItem(*item));
1132 item->SetPath(item_new->GetPath());
1137 if(!XFILE::CDirectory::GetDirectory(item_new->GetPath(), items, XFILE::CDirectory::CHints(), true) || items.Size() == 0)
1139 CLog::Log(LOGERROR, "CGUIWindowVideoBase::ShowPlaySelection - Failed to get any items %s", item_new->GetPath().c_str());
1147 bool CGUIWindowVideoBase::OnResumeItem(int iItem)
1149 if (iItem < 0 || iItem >= m_vecItems->Size()) return true;
1150 CFileItemPtr item = m_vecItems->Get(iItem);
1152 if (item->m_bIsFolder)
1154 // resuming directories isn't supported yet. play.
1159 CStdString resumeString = GetResumeString(*item);
1161 if (!resumeString.IsEmpty())
1163 CContextButtons choices;
1164 choices.Add(SELECT_ACTION_RESUME, resumeString);
1165 choices.Add(SELECT_ACTION_PLAY, 12021); // Start from beginning
1166 int value = CGUIDialogContextMenu::ShowAndGetChoice(choices);
1169 return OnFileAction(iItem, value);
1172 return OnFileAction(iItem, SELECT_ACTION_PLAY);
1175 void CGUIWindowVideoBase::OnStreamDetails(const CStreamDetails &details, const CStdString &strFileName, long lFileId)
1181 db.SetStreamDetailsForFile(details, strFileName);
1183 db.SetStreamDetailsForFileId(details, lFileId);
1189 void CGUIWindowVideoBase::GetContextButtons(int itemNumber, CContextButtons &buttons)
1192 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
1193 item = m_vecItems->Get(itemNumber);
1195 // contextual buttons
1196 if (item && !item->GetProperty("pluginreplacecontextitems").asBoolean())
1198 if (!item->IsParentFolder())
1200 CStdString path(item->GetPath());
1201 if (item->IsVideoDb() && item->HasVideoInfoTag())
1202 path = item->GetVideoInfoTag()->m_strFileNameAndPath;
1204 if (!item->GetPath().Equals("add") && !item->IsPlugin() &&
1205 !item->IsScript() && !item->IsAddonsPath() && !item->IsLiveTV())
1207 if (URIUtils::IsStack(path))
1210 if (m_database.GetStackTimes(path,times) || CFileItem(CStackDirectory::GetFirstStackedFile(path),false).IsDVDImage())
1211 buttons.Add(CONTEXT_BUTTON_PLAY_PART, 20324);
1214 if (!m_vecItems->GetPath().IsEmpty() && !item->GetPath().Left(19).Equals("newsmartplaylist://") && !item->GetPath().Left(9).Equals("newtag://")
1215 && !m_vecItems->IsSourcesPath())
1217 buttons.Add(CONTEXT_BUTTON_QUEUE_ITEM, 13347); // Add to Playlist
1220 // allow a folder to be ad-hoc queued and played by the default player
1221 if (item->m_bIsFolder || (item->IsPlayList() &&
1222 !g_advancedSettings.m_playlistAsFolders))
1224 buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 208);
1228 if (!m_vecItems->IsPlugin() && (item->IsPlugin() || item->IsScript()))
1229 buttons.Add(CONTEXT_BUTTON_INFO,24003); // Add-on info
1231 if (!item->m_bIsFolder && !(item->IsPlayList() && !g_advancedSettings.m_playlistAsFolders))
1233 VECPLAYERCORES vecCores;
1234 if (item->IsVideoDb())
1236 CFileItem item2(item->GetVideoInfoTag()->m_strFileNameAndPath, false);
1237 CPlayerCoreFactory::GetPlayers(item2, vecCores);
1240 CPlayerCoreFactory::GetPlayers(*item, vecCores);
1241 if (vecCores.size() > 1)
1242 buttons.Add(CONTEXT_BUTTON_PLAY_WITH, 15213);
1244 if (item->IsSmartPlayList())
1246 buttons.Add(CONTEXT_BUTTON_PLAY_PARTYMODE, 15216); // Play in Partymode
1249 // if autoresume is enabled then add restart video button
1250 // check to see if the Resume Video button is applicable
1251 // only if the video is NOT a DVD (in that case the resume button will be added by CGUIDialogContextMenu::GetContextButtons)
1252 if (!item->IsDVD() && HasResumeItemOffset(item.get()))
1254 buttons.Add(CONTEXT_BUTTON_RESUME_ITEM, GetResumeString(*(item.get()))); // Resume Video
1256 //if the item isn't a folder or script, is a member of a list rather than a single item
1257 //and we're not on the last element of the list,
1258 //then add add either 'play from here' or 'play only this' depending on default behaviour
1259 if (!(item->m_bIsFolder || item->IsScript()) && m_vecItems->Size() > 1 && itemNumber < m_vecItems->Size()-1)
1261 if (!g_guiSettings.GetBool("videoplayer.autoplaynextitem"))
1262 buttons.Add(CONTEXT_BUTTON_PLAY_AND_QUEUE, 13412);
1264 buttons.Add(CONTEXT_BUTTON_PLAY_ONLY_THIS, 13434);
1266 if (item->IsSmartPlayList() || m_vecItems->IsSmartPlayList())
1267 buttons.Add(CONTEXT_BUTTON_EDIT_SMART_PLAYLIST, 586);
1270 CGUIMediaWindow::GetContextButtons(itemNumber, buttons);
1273 void CGUIWindowVideoBase::GetNonContextButtons(int itemNumber, CContextButtons &buttons)
1275 if (g_playlistPlayer.GetPlaylist(PLAYLIST_VIDEO).size() > 0)
1276 buttons.Add(CONTEXT_BUTTON_NOW_PLAYING, 13350);
1279 bool CGUIWindowVideoBase::OnPlayStackPart(int iItem)
1281 if (iItem < 0 || iItem >= m_vecItems->Size())
1284 CFileItemPtr stack = m_vecItems->Get(iItem);
1285 CStdString path(stack->GetPath());
1286 if (stack->IsVideoDb())
1287 path = stack->GetVideoInfoTag()->m_strFileNameAndPath;
1289 if (!URIUtils::IsStack(path))
1292 CFileItemList parts;
1293 CDirectory::GetDirectory(path,parts);
1294 CGUIDialogFileStacking* dlg = (CGUIDialogFileStacking*)g_windowManager.GetWindow(WINDOW_DIALOG_FILESTACKING);
1295 if (!dlg) return true;
1296 dlg->SetNumberOfFiles(parts.Size());
1298 int selectedFile = dlg->GetSelectedFile();
1299 if (selectedFile > 0)
1302 if (CFileItem(CStackDirectory::GetFirstStackedFile(path),false).IsDVDImage())
1304 CStdString resumeString = CGUIWindowVideoBase::GetResumeString(*(parts[selectedFile - 1].get()));
1305 stack->m_lStartOffset = 0;
1306 if (!resumeString.IsEmpty())
1308 CContextButtons choices;
1309 choices.Add(SELECT_ACTION_RESUME, resumeString);
1310 choices.Add(SELECT_ACTION_PLAY, 12021); // Start from beginning
1311 int value = CGUIDialogContextMenu::ShowAndGetChoice(choices);
1312 if (value == SELECT_ACTION_RESUME)
1313 GetResumeItemOffset(parts[selectedFile - 1].get(), stack->m_lStartOffset, stack->m_lStartPartNumber);
1314 else if (value != SELECT_ACTION_PLAY)
1315 return false; // if not selected PLAY, then we changed our mind so return
1317 stack->m_lStartPartNumber = selectedFile;
1322 if (selectedFile > 1)
1325 if (m_database.GetStackTimes(path,times))
1326 stack->m_lStartOffset = times[selectedFile-2]*75; // wtf?
1329 stack->m_lStartOffset = 0;
1338 bool CGUIWindowVideoBase::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
1341 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
1342 item = m_vecItems->Get(itemNumber);
1345 case CONTEXT_BUTTON_SET_CONTENT:
1347 OnAssignContent(item->HasVideoInfoTag() && !item->GetVideoInfoTag()->m_strPath.IsEmpty() ? item->GetVideoInfoTag()->m_strPath : item->GetPath());
1350 case CONTEXT_BUTTON_PLAY_PART:
1352 if (OnPlayStackPart(itemNumber))
1354 // call CGUIMediaWindow::OnClick() as otherwise autoresume will kick in
1355 CGUIMediaWindow::OnClick(itemNumber);
1361 case CONTEXT_BUTTON_QUEUE_ITEM:
1362 OnQueueItem(itemNumber);
1365 case CONTEXT_BUTTON_PLAY_ITEM:
1366 PlayItem(itemNumber);
1369 case CONTEXT_BUTTON_PLAY_WITH:
1371 VECPLAYERCORES vecCores;
1372 if (item->IsVideoDb())
1374 CFileItem item2(*item->GetVideoInfoTag());
1375 CPlayerCoreFactory::GetPlayers(item2, vecCores);
1378 CPlayerCoreFactory::GetPlayers(*item, vecCores);
1379 g_application.m_eForcedNextPlayer = CPlayerCoreFactory::SelectPlayerDialog(vecCores);
1380 if (g_application.m_eForcedNextPlayer != EPC_NONE)
1381 OnClick(itemNumber);
1385 case CONTEXT_BUTTON_PLAY_PARTYMODE:
1386 g_partyModeManager.Enable(PARTYMODECONTEXT_VIDEO, m_vecItems->Get(itemNumber)->GetPath());
1389 case CONTEXT_BUTTON_RESTART_ITEM:
1390 OnRestartItem(itemNumber);
1393 case CONTEXT_BUTTON_RESUME_ITEM:
1394 return OnFileAction(itemNumber, SELECT_ACTION_RESUME);
1396 case CONTEXT_BUTTON_NOW_PLAYING:
1397 g_windowManager.ActivateWindow(WINDOW_VIDEO_PLAYLIST);
1400 case CONTEXT_BUTTON_INFO:
1404 case CONTEXT_BUTTON_STOP_SCANNING:
1406 g_application.StopVideoScan();
1409 case CONTEXT_BUTTON_SCAN:
1410 case CONTEXT_BUTTON_UPDATE_TVSHOW:
1414 ADDON::ScraperPtr info;
1415 SScanSettings settings;
1416 GetScraperForItem(item.get(), info, settings);
1417 CStdString strPath = item->GetPath();
1418 if (item->IsVideoDb() && (!item->m_bIsFolder || item->GetVideoInfoTag()->m_strPath.IsEmpty()))
1421 if (item->IsVideoDb())
1422 strPath = item->GetVideoInfoTag()->m_strPath;
1424 if (!info || info->Content() == CONTENT_NONE)
1427 if (item->m_bIsFolder)
1429 m_database.SetPathHash(strPath,""); // to force scan
1430 OnScan(strPath, true);
1433 OnInfo(item.get(),info);
1437 case CONTEXT_BUTTON_DELETE:
1438 OnDeleteItem(itemNumber);
1440 case CONTEXT_BUTTON_EDIT_SMART_PLAYLIST:
1442 CStdString playlist = m_vecItems->Get(itemNumber)->IsSmartPlayList() ? m_vecItems->Get(itemNumber)->GetPath() : m_vecItems->GetPath(); // save path as activatewindow will destroy our items
1443 if (CGUIDialogSmartPlaylistEditor::EditPlaylist(playlist, "video"))
1444 Refresh(true); // need to update
1447 case CONTEXT_BUTTON_RENAME:
1448 OnRenameItem(itemNumber);
1450 case CONTEXT_BUTTON_MARK_WATCHED:
1452 int newSelection = m_viewControl.GetSelectedItem() + 1;
1453 MarkWatched(item,true);
1454 m_viewControl.SetSelectedItem(newSelection);
1456 CUtil::DeleteVideoDatabaseDirectoryCache();
1460 case CONTEXT_BUTTON_MARK_UNWATCHED:
1461 MarkWatched(item,false);
1462 CUtil::DeleteVideoDatabaseDirectoryCache();
1465 case CONTEXT_BUTTON_PLAY_AND_QUEUE:
1466 return OnPlayAndQueueMedia(item);
1467 case CONTEXT_BUTTON_PLAY_ONLY_THIS:
1468 return OnPlayMedia(itemNumber);
1472 return CGUIMediaWindow::OnContextButton(itemNumber, button);
1475 bool CGUIWindowVideoBase::OnPlayMedia(int iItem)
1477 if ( iItem < 0 || iItem >= (int)m_vecItems->Size() )
1480 CFileItemPtr pItem = m_vecItems->Get(iItem);
1483 if (g_partyModeManager.IsEnabled(PARTYMODECONTEXT_VIDEO))
1485 CPlayList playlistTemp;
1486 playlistTemp.Add(pItem);
1487 g_partyModeManager.AddUserSongs(playlistTemp, true);
1491 // Reset Playlistplayer, playback started now does
1492 // not use the playlistplayer.
1493 g_playlistPlayer.Reset();
1494 g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_NONE);
1496 CFileItem item(*pItem);
1497 if (pItem->IsVideoDb())
1499 item.SetPath(pItem->GetVideoInfoTag()->m_strFileNameAndPath);
1500 item.SetProperty("original_listitem_url", pItem->GetPath());
1502 CLog::Log(LOGDEBUG, "%s %s", __FUNCTION__, item.GetPath().c_str());
1504 if (item.GetPath().Left(17) == "pvr://recordings/")
1506 if (!g_PVRManager.IsStarted())
1509 /* For recordings we check here for a available stream URL */
1510 CFileItemPtr tag = g_PVRRecordings->GetByPath(item.GetPath());
1511 if (tag && tag->HasPVRRecordingInfoTag() && !tag->GetPVRRecordingInfoTag()->m_strStreamURL.IsEmpty())
1513 CStdString stream = tag->GetPVRRecordingInfoTag()->m_strStreamURL;
1515 /* Isolate the folder from the filename */
1516 size_t found = stream.find_last_of("/");
1517 if (found == CStdString::npos)
1518 found = stream.find_last_of("\\");
1520 if (found != CStdString::npos)
1522 /* Check here for asterix at the begin of the filename */
1523 if (stream[found+1] == '*')
1525 /* Create a "stack://" url with all files matching the extension */
1526 CStdString ext = URIUtils::GetExtension(stream);
1527 CStdString dir = stream.substr(0, found).c_str();
1529 CFileItemList items;
1530 CDirectory::GetDirectory(dir, items);
1531 items.Sort(SORT_METHOD_FILE, SortOrderAscending);
1534 for (int i = 0; i < items.Size(); ++i)
1536 if (URIUtils::GetExtension(items[i]->GetPath()) == ext)
1540 if (stack.size() > 0)
1542 /* If we have a stack change the path of the item to it */
1543 CStackDirectory dir;
1544 CStdString stackPath = dir.ConstructStackPath(items, stack);
1545 item.SetPath(stackPath);
1550 /* If no asterix is present play only the given stream URL */
1551 item.SetPath(stream);
1556 CLog::Log(LOGERROR, "CGUIWindowTV: Can't open recording, no valid filename!");
1557 CGUIDialogOK::ShowAndGetInput(19033,0,19036,0);
1568 bool CGUIWindowVideoBase::OnPlayAndQueueMedia(const CFileItemPtr &item)
1570 // Get the current playlist and make sure it is not shuffled
1571 int iPlaylist = m_guiState->GetPlaylist();
1572 if (iPlaylist != PLAYLIST_NONE && g_playlistPlayer.IsShuffled(iPlaylist))
1573 g_playlistPlayer.SetShuffle(iPlaylist, false);
1575 // Call the base method to actually queue the items
1576 // and start playing the given item
1577 return CGUIMediaWindow::OnPlayAndQueueMedia(item);
1580 void CGUIWindowVideoBase::PlayMovie(const CFileItem *item)
1582 CFileItemPtr movieItem(new CFileItem(*item));
1584 if(!ShowPlaySelection(movieItem))
1587 g_playlistPlayer.Reset();
1588 g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_VIDEO);
1589 CPlayList& playlist = g_playlistPlayer.GetPlaylist(PLAYLIST_VIDEO);
1591 playlist.Add(movieItem);
1593 if(m_thumbLoader.IsLoading())
1594 m_thumbLoader.StopAsync();
1597 g_playlistPlayer.Play(0);
1599 if(!g_application.IsPlayingVideo())
1600 m_thumbLoader.Load(*m_vecItems);
1603 void CGUIWindowVideoBase::OnDeleteItem(int iItem)
1605 if ( iItem < 0 || iItem >= m_vecItems->Size())
1608 OnDeleteItem(m_vecItems->Get(iItem));
1611 m_viewControl.SetSelectedItem(iItem);
1614 void CGUIWindowVideoBase::OnDeleteItem(CFileItemPtr item)
1616 // HACK: stacked files need to be treated as folders in order to be deleted
1617 if (item->IsStack())
1618 item->m_bIsFolder = true;
1619 if (g_settings.GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE &&
1620 g_settings.GetCurrentProfile().filesLocked())
1622 if (!g_passwordManager.IsMasterLockUnlocked(true))
1626 if ((g_guiSettings.GetBool("filelists.allowfiledeletion") ||
1627 m_vecItems->GetPath().Equals("special://videoplaylists/")) &&
1628 CUtil::SupportsWriteFileOperations(item->GetPath()))
1629 CFileUtils::DeleteItem(item);
1632 void CGUIWindowVideoBase::MarkWatched(const CFileItemPtr &item, bool bMark)
1634 if (!g_settings.GetCurrentProfile().canWriteDatabases())
1636 // dont allow update while scanning
1637 if (g_application.IsVideoScanning())
1639 CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1643 CVideoDatabase database;
1644 if (database.Open())
1646 CFileItemList items;
1647 if (item->m_bIsFolder)
1649 CStdString strPath = item->GetPath();
1650 CDirectory::GetDirectory(strPath, items);
1655 for (int i=0;i<items.Size();++i)
1657 CFileItemPtr pItem=items[i];
1658 if (pItem->m_bIsFolder)
1660 MarkWatched(pItem, bMark);
1664 if (pItem->HasVideoInfoTag() &&
1665 (( bMark && pItem->GetVideoInfoTag()->m_playCount) ||
1666 (!bMark && !(pItem->GetVideoInfoTag()->m_playCount))))
1669 // Clear resume bookmark
1671 database.ClearBookMarksOfFile(pItem->GetPath(), CBookmark::RESUME);
1673 database.SetPlayCount(*pItem, bMark ? 1 : 0);
1680 //Add change a title's name
1681 void CGUIWindowVideoBase::UpdateVideoTitle(const CFileItem* pItem)
1683 // dont allow update while scanning
1684 if (g_application.IsVideoScanning())
1686 CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1690 CVideoInfoTag detail;
1691 CVideoDatabase database;
1693 CVideoDatabaseDirectory dir;
1694 CQueryParams params;
1695 dir.GetQueryParams(pItem->GetPath(),params);
1696 int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
1698 VIDEODB_CONTENT_TYPE iType=VIDEODB_CONTENT_MOVIES;
1699 if (pItem->HasVideoInfoTag() && (!pItem->GetVideoInfoTag()->m_strShowTitle.IsEmpty() ||
1700 pItem->GetVideoInfoTag()->m_iEpisode > 0))
1702 iType = VIDEODB_CONTENT_TVSHOWS;
1704 if (pItem->HasVideoInfoTag() && pItem->GetVideoInfoTag()->m_iSeason > -1 && !pItem->m_bIsFolder)
1705 iType = VIDEODB_CONTENT_EPISODES;
1706 if (pItem->HasVideoInfoTag() && !pItem->GetVideoInfoTag()->m_artist.empty())
1707 iType = VIDEODB_CONTENT_MUSICVIDEOS;
1708 if (params.GetSetId() != -1 && params.GetMovieId() == -1)
1709 iType = VIDEODB_CONTENT_MOVIE_SETS;
1710 if (iType == VIDEODB_CONTENT_MOVIES)
1711 database.GetMovieInfo("", detail, pItem->GetVideoInfoTag()->m_iDbId);
1712 if (iType == VIDEODB_CONTENT_MOVIE_SETS)
1713 database.GetSetInfo(params.GetSetId(), detail);
1714 if (iType == VIDEODB_CONTENT_EPISODES)
1715 database.GetEpisodeInfo(pItem->GetPath(),detail,pItem->GetVideoInfoTag()->m_iDbId);
1716 if (iType == VIDEODB_CONTENT_TVSHOWS)
1717 database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath,detail,pItem->GetVideoInfoTag()->m_iDbId);
1718 if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
1719 database.GetMusicVideoInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath,detail,pItem->GetVideoInfoTag()->m_iDbId);
1721 CStdString strInput;
1722 strInput = detail.m_strTitle;
1725 if (!CGUIKeyboardFactory::ShowAndGetInput(strInput, g_localizeStrings.Get(16105), false))
1728 database.UpdateMovieTitle(iDbId, strInput, iType);
1731 void CGUIWindowVideoBase::LoadPlayList(const CStdString& strPlayList, int iPlayList /* = PLAYLIST_VIDEO */)
1733 // if partymode is active, we disable it
1734 if (g_partyModeManager.IsEnabled())
1735 g_partyModeManager.Disable();
1737 // load a playlist like .m3u, .pls
1738 // first get correct factory to load playlist
1739 auto_ptr<CPlayList> pPlayList (CPlayListFactory::Create(strPlayList));
1740 if (pPlayList.get())
1743 if (!pPlayList->Load(strPlayList))
1745 CGUIDialogOK::ShowAndGetInput(6, 0, 477, 0);
1746 return; //hmmm unable to load playlist?
1750 if (g_application.ProcessAndStartPlaylist(strPlayList, *pPlayList, iPlayList))
1752 if (m_guiState.get())
1753 m_guiState->SetPlaylistDirectory("playlistvideo://");
1757 void CGUIWindowVideoBase::PlayItem(int iItem)
1759 // restrictions should be placed in the appropiate window code
1760 // only call the base code if the item passes since this clears
1761 // the currently playing temp playlist
1763 const CFileItemPtr pItem = m_vecItems->Get(iItem);
1764 // if its a folder, build a temp playlist
1765 if (pItem->m_bIsFolder && !pItem->IsPlugin())
1767 // take a copy so we can alter the queue state
1768 CFileItemPtr item(new CFileItem(*m_vecItems->Get(iItem)));
1770 // Allow queuing of unqueueable items
1771 // when we try to queue them directly
1772 if (!item->CanQueue())
1773 item->SetCanQueue(true);
1776 if (item->IsParentFolder())
1779 // recursively add items to list
1780 CFileItemList queuedItems;
1781 AddItemToPlayList(item, queuedItems);
1783 g_playlistPlayer.ClearPlaylist(PLAYLIST_VIDEO);
1784 g_playlistPlayer.Reset();
1785 g_playlistPlayer.Add(PLAYLIST_VIDEO, queuedItems);
1786 g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_VIDEO);
1787 g_playlistPlayer.Play();
1789 else if (pItem->IsPlayList())
1791 // load the playlist the old way
1792 LoadPlayList(pItem->GetPath(), PLAYLIST_VIDEO);
1796 // single item, play it
1801 bool CGUIWindowVideoBase::Update(const CStdString &strDirectory, bool updateFilterPath /* = true */)
1803 if (m_thumbLoader.IsLoading())
1804 m_thumbLoader.StopThread();
1806 if (!CGUIMediaWindow::Update(strDirectory, updateFilterPath))
1809 // might already be running from GetGroupedItems
1810 if (!m_thumbLoader.IsLoading())
1811 m_thumbLoader.Load(*m_vecItems);
1816 bool CGUIWindowVideoBase::GetDirectory(const CStdString &strDirectory, CFileItemList &items)
1818 bool bResult = CGUIMediaWindow::GetDirectory(strDirectory, items);
1820 // add in the "New Playlist" item if we're in the playlists folder
1821 if ((items.GetPath() == "special://videoplaylists/") && !items.Contains("newplaylist://"))
1823 CFileItemPtr newPlaylist(new CFileItem(g_settings.GetUserDataItem("PartyMode-Video.xsp"),false));
1824 newPlaylist->SetLabel(g_localizeStrings.Get(16035));
1825 newPlaylist->SetLabelPreformated(true);
1826 newPlaylist->m_bIsFolder = true;
1827 items.Add(newPlaylist);
1829 /* newPlaylist.reset(new CFileItem("newplaylist://", false));
1830 newPlaylist->SetLabel(g_localizeStrings.Get(525));
1831 newPlaylist->SetLabelPreformated(true);
1832 items.Add(newPlaylist);
1834 newPlaylist.reset(new CFileItem("newsmartplaylist://video", false));
1835 newPlaylist->SetLabel(g_localizeStrings.Get(21437)); // "new smart playlist..."
1836 newPlaylist->SetLabelPreformated(true);
1837 items.Add(newPlaylist);
1840 m_stackingAvailable = StackingAvailable(items);
1841 // we may also be in a tvshow files listing
1842 // (ideally this should be removed, and our stack regexps tidied up if necessary
1843 // No "normal" episodes should stack, and multi-parts should be supported)
1844 ADDON::ScraperPtr info = m_database.GetScraperForPath(strDirectory);
1845 if (info && info->Content() == CONTENT_TVSHOWS)
1846 m_stackingAvailable = false;
1848 if (m_stackingAvailable && !items.IsStack() && g_settings.m_videoStacking)
1854 bool CGUIWindowVideoBase::StackingAvailable(const CFileItemList &items) const
1856 CURL url(items.GetPath());
1857 return !(items.IsTuxBox() || items.IsPlugin() ||
1858 items.IsAddonsPath() || items.IsRSS() ||
1859 items.IsInternetStream() || items.IsVideoDb() ||
1860 url.GetProtocol() == "playlistvideo");
1863 void CGUIWindowVideoBase::GetGroupedItems(CFileItemList &items)
1865 CGUIMediaWindow::GetGroupedItems(items);
1867 CQueryParams params;
1868 CVideoDatabaseDirectory dir;
1869 dir.GetQueryParams(items.GetPath(), params);
1870 VIDEODATABASEDIRECTORY::NODE_TYPE nodeType = CVideoDatabaseDirectory::GetDirectoryChildType(m_strFilterPath);
1871 if (items.GetContent().Equals("movies") && params.GetSetId() <= 0 &&
1872 nodeType == NODE_TYPE_TITLE_MOVIES &&
1873 g_guiSettings.GetBool("videolibrary.groupmoviesets"))
1875 CFileItemList groupedItems;
1876 if (GroupUtils::Group(GroupBySet, items, groupedItems, GroupAttributeIgnoreSingleItems))
1879 items.Append(groupedItems);
1883 // reload thumbs after filtering and grouping
1884 if (m_thumbLoader.IsLoading())
1885 m_thumbLoader.StopThread();
1887 m_thumbLoader.Load(items);
1890 bool CGUIWindowVideoBase::CheckFilterAdvanced(CFileItemList &items) const
1892 CStdString content = items.GetContent();
1893 if ((items.IsVideoDb() || CanContainFilter(m_strFilterPath)) &&
1894 (content.Equals("movies") || content.Equals("tvshows") || content.Equals("episodes") || content.Equals("musicvideos")))
1900 bool CGUIWindowVideoBase::CanContainFilter(const CStdString &strDirectory) const
1902 return StringUtils::StartsWith(strDirectory, "videodb://");
1905 void CGUIWindowVideoBase::AddToDatabase(int iItem)
1907 if (iItem < 0 || iItem >= m_vecItems->Size())
1910 CFileItemPtr pItem = m_vecItems->Get(iItem);
1911 if (pItem->IsParentFolder() || pItem->m_bIsFolder)
1914 CVideoInfoTag movie;
1918 // enter a new title
1919 CStdString strTitle = pItem->GetLabel();
1920 if (!CGUIKeyboardFactory::ShowAndGetInput(strTitle, g_localizeStrings.Get(528), false)) // Enter Title
1924 CGUIDialogSelect* pSelect = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1928 pSelect->SetHeading(530); // Select Genre
1930 CFileItemList items;
1931 if (!CDirectory::GetDirectory("videodb://1/1/", items))
1933 pSelect->SetItems(&items);
1934 pSelect->EnableButton(true, 531); // New Genre
1936 CStdString strGenre;
1937 int iSelected = pSelect->GetSelectedLabel();
1939 strGenre = items[iSelected]->GetLabel();
1940 else if (!pSelect->IsButtonPressed())
1943 // enter new genre string
1944 if (strGenre.IsEmpty())
1946 strGenre = g_localizeStrings.Get(532); // Manual Addition
1947 if (!CGUIKeyboardFactory::ShowAndGetInput(strGenre, g_localizeStrings.Get(533), false)) // Enter Genre
1948 return; // user backed out
1949 if (strGenre.IsEmpty())
1950 return; // no genre string
1954 movie.m_strTitle = strTitle;
1955 movie.m_genre = StringUtils::Split(strGenre, g_advancedSettings.m_videoItemSeparator);
1957 // everything is ok, so add to database
1959 int idMovie = m_database.AddMovie(pItem->GetPath());
1960 movie.m_strIMDBNumber.Format("xx%08i", idMovie);
1961 m_database.SetDetailsForMovie(pItem->GetPath(), movie, pItem->GetArt());
1965 CGUIDialogOK::ShowAndGetInput(20177, movie.m_strTitle, StringUtils::Join(movie.m_genre, g_advancedSettings.m_videoItemSeparator), movie.m_strIMDBNumber);
1967 // library view cache needs to be cleared
1968 CUtil::DeleteVideoDatabaseDirectoryCache();
1971 /// \brief Search the current directory for a string got from the virtual keyboard
1972 void CGUIWindowVideoBase::OnSearch()
1974 CStdString strSearch;
1975 if (!CGUIKeyboardFactory::ShowAndGetInput(strSearch, g_localizeStrings.Get(16017), false))
1978 strSearch.ToLower();
1981 m_dlgProgress->SetHeading(194);
1982 m_dlgProgress->SetLine(0, strSearch);
1983 m_dlgProgress->SetLine(1, "");
1984 m_dlgProgress->SetLine(2, "");
1985 m_dlgProgress->StartModal();
1986 m_dlgProgress->Progress();
1988 CFileItemList items;
1989 DoSearch(strSearch, items);
1992 m_dlgProgress->Close();
1996 CGUIDialogSelect* pDlgSelect = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1997 pDlgSelect->Reset();
1998 pDlgSelect->SetHeading(283);
2000 for (int i = 0; i < (int)items.Size(); i++)
2002 CFileItemPtr pItem = items[i];
2003 pDlgSelect->Add(pItem->GetLabel());
2006 pDlgSelect->DoModal();
2008 int iItem = pDlgSelect->GetSelectedLabel();
2012 CFileItemPtr pSelItem = items[iItem];
2014 OnSearchItemFound(pSelItem.get());
2018 CGUIDialogOK::ShowAndGetInput(194, 284, 0, 0);
2022 /// \brief React on the selected search item
2023 /// \param pItem Search result item
2024 void CGUIWindowVideoBase::OnSearchItemFound(const CFileItem* pSelItem)
2026 if (pSelItem->m_bIsFolder)
2028 CStdString strPath = pSelItem->GetPath();
2029 CStdString strParentPath;
2030 URIUtils::GetParentPath(strPath, strParentPath);
2032 Update(strParentPath);
2034 if (pSelItem->IsVideoDb() && g_settings.m_bMyVideoNavFlatten)
2035 SetHistoryForPath("");
2037 SetHistoryForPath(strParentPath);
2039 strPath = pSelItem->GetPath();
2041 if (pSelItem->IsSmb() && !URIUtils::HasSlashAtEnd(strPath))
2044 for (int i = 0; i < m_vecItems->Size(); i++)
2046 CFileItemPtr pItem = m_vecItems->Get(i);
2047 if (pItem->GetPath() == strPath)
2049 m_viewControl.SetSelectedItem(i);
2057 URIUtils::GetDirectory(pSelItem->GetPath(), strPath);
2061 if (pSelItem->IsVideoDb() && g_settings.m_bMyVideoNavFlatten)
2062 SetHistoryForPath("");
2064 SetHistoryForPath(strPath);
2066 for (int i = 0; i < (int)m_vecItems->Size(); i++)
2068 CFileItemPtr pItem = m_vecItems->Get(i);
2069 if (pItem->GetPath() == pSelItem->GetPath())
2071 m_viewControl.SetSelectedItem(i);
2076 m_viewControl.SetFocused();
2079 int CGUIWindowVideoBase::GetScraperForItem(CFileItem *item, ADDON::ScraperPtr &info, SScanSettings& settings)
2084 if (m_vecItems->IsPlugin() || m_vecItems->IsRSS())
2089 else if(m_vecItems->IsLiveTV())
2095 bool foundDirectly = false;
2096 info = m_database.GetScraperForPath(item->HasVideoInfoTag() && !item->GetVideoInfoTag()->m_strPath.IsEmpty() ? item->GetVideoInfoTag()->m_strPath : item->GetPath(), settings, foundDirectly);
2097 return foundDirectly ? 1 : 0;
2100 void CGUIWindowVideoBase::OnScan(const CStdString& strPath, bool scanAll)
2102 g_application.StartVideoScan(strPath, scanAll);
2105 CStdString CGUIWindowVideoBase::GetStartFolder(const CStdString &dir)
2107 if (dir.Equals("$PLAYLISTS") || dir.Equals("Playlists"))
2108 return "special://videoplaylists/";
2109 else if (dir.Equals("Plugins") || dir.Equals("Addons"))
2110 return "addons://sources/video/";
2111 return CGUIMediaWindow::GetStartFolder(dir);
2114 void CGUIWindowVideoBase::AppendAndClearSearchItems(CFileItemList &searchItems, const CStdString &prependLabel, CFileItemList &results)
2116 if (!searchItems.Size())
2119 searchItems.Sort(g_guiSettings.GetBool("filelists.ignorethewhensorting") ? SORT_METHOD_LABEL_IGNORE_THE : SORT_METHOD_LABEL, SortOrderAscending);
2120 for (int i = 0; i < searchItems.Size(); i++)
2121 searchItems[i]->SetLabel(prependLabel + searchItems[i]->GetLabel());
2122 results.Append(searchItems);
2124 searchItems.Clear();
2127 bool CGUIWindowVideoBase::OnUnAssignContent(const CStdString &path, int label1, int label2, int label3)
2132 if (CGUIDialogYesNo::ShowAndGetInput(label1,label2,label3,20022,bCanceled))
2134 CGUIDialogProgress *progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
2135 db.RemoveContentForPath(path, progress);
2137 CUtil::DeleteVideoDatabaseDirectoryCache();
2144 ADDON::ScraperPtr info;
2145 SScanSettings settings;
2146 settings.exclude = true;
2147 db.SetScraperForPath(path,info,settings);
2155 void CGUIWindowVideoBase::OnAssignContent(const CStdString &path)
2161 SScanSettings settings;
2162 ADDON::ScraperPtr info = db.GetScraperForPath(path, settings);
2164 ADDON::ScraperPtr info2(info);
2166 if (CGUIDialogContentSettings::Show(info, settings))
2168 if(settings.exclude || (!info && info2))
2170 OnUnAssignContent(path,20375,20340,20341);
2172 else if (info != info2)
2174 if (OnUnAssignContent(path,20442,20443,20444))
2179 db.SetScraperForPath(path,info,settings);
2183 g_application.StartVideoScan(path, true);
2187 void CGUIWindowVideoBase::OnInitWindow()
2189 CGUIMediaWindow::OnInitWindow();
2190 if (g_settings.m_videoNeedsUpdate == 63 && !g_application.IsVideoScanning() &&
2191 g_infoManager.GetLibraryBool(LIBRARY_HAS_VIDEO))
2193 // rescan of video library required
2194 if (CGUIDialogYesNo::ShowAndGetInput(799, 12351, 12352, 12354))
2196 CEdenVideoArtUpdater::Start();
2197 g_settings.m_videoNeedsUpdate = 0; // once is enough