2 * Copyright (C) 2005-2008 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, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
23 #include "GUIWindowVideoBase.h"
25 #include "video/VideoInfoDownloader.h"
26 #include "utils/RegExp.h"
27 #include "utils/Variant.h"
28 #include "addons/AddonManager.h"
29 #include "addons/IAddon.h"
30 #include "video/dialogs/GUIDialogVideoInfo.h"
31 #include "GUIWindowVideoNav.h"
32 #include "dialogs/GUIDialogFileBrowser.h"
33 #include "video/dialogs/GUIDialogVideoScan.h"
34 #include "dialogs/GUIDialogSmartPlaylistEditor.h"
35 #include "dialogs/GUIDialogProgress.h"
36 #include "dialogs/GUIDialogYesNo.h"
37 #include "playlists/PlayListFactory.h"
38 #include "Application.h"
40 #include "PlayListPlayer.h"
41 #include "GUIPassword.h"
42 #include "filesystem/ZipManager.h"
43 #include "filesystem/StackDirectory.h"
44 #include "filesystem/MultiPathDirectory.h"
45 #include "video/dialogs/GUIDialogFileStacking.h"
46 #include "dialogs/GUIDialogMediaSource.h"
47 #include "windows/GUIWindowFileManager.h"
48 #include "filesystem/VideoDatabaseDirectory.h"
49 #include "PartyModeManager.h"
50 #include "guilib/GUIWindowManager.h"
51 #include "dialogs/GUIDialogOK.h"
52 #include "dialogs/GUIDialogSelect.h"
53 #include "dialogs/GUIDialogKeyboard.h"
54 #include "filesystem/Directory.h"
55 #include "playlists/PlayList.h"
56 #include "settings/Settings.h"
57 #include "settings/AdvancedSettings.h"
58 #include "settings/GUISettings.h"
59 #include "settings/GUIDialogContentSettings.h"
60 #include "guilib/LocalizeStrings.h"
61 #include "utils/StringUtils.h"
62 #include "utils/log.h"
63 #include "utils/FileUtils.h"
64 #include "utils/URIUtils.h"
66 #include "addons/Skin.h"
69 using namespace XFILE;
70 using namespace PLAYLIST;
71 using namespace VIDEODATABASEDIRECTORY;
72 using namespace VIDEO;
73 using namespace ADDON;
75 #define CONTROL_BTNVIEWASICONS 2
76 #define CONTROL_BTNSORTBY 3
77 #define CONTROL_BTNSORTASC 4
78 #define CONTROL_BTNTYPE 5
79 #define CONTROL_LABELFILES 12
81 #define CONTROL_PLAY_DVD 6
82 #define CONTROL_STACK 7
83 #define CONTROL_BTNSCAN 8
85 CGUIWindowVideoBase::CGUIWindowVideoBase(int id, const CStdString &xmlFile)
86 : CGUIMediaWindow(id, xmlFile)
88 m_thumbLoader.SetObserver(this);
89 m_thumbLoader.SetStreamDetailsObserver(this);
90 m_stackingAvailable = true;
93 CGUIWindowVideoBase::~CGUIWindowVideoBase()
97 bool CGUIWindowVideoBase::OnAction(const CAction &action)
99 if (action.GetID() == ACTION_SHOW_PLAYLIST)
101 OutputDebugString("activate guiwindowvideoplaylist!\n");
102 g_windowManager.ActivateWindow(WINDOW_VIDEO_PLAYLIST);
105 if (action.GetID() == ACTION_SCAN_ITEM)
106 return OnContextButton(m_viewControl.GetSelectedItem(),CONTEXT_BUTTON_SCAN);
108 return CGUIMediaWindow::OnAction(action);
111 bool CGUIWindowVideoBase::OnMessage(CGUIMessage& message)
113 switch ( message.GetMessage() )
115 case GUI_MSG_WINDOW_DEINIT:
116 if (m_thumbLoader.IsLoading())
117 m_thumbLoader.StopThread();
121 case GUI_MSG_WINDOW_INIT:
125 m_dlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
127 // save current window, unless the current window is the video playlist window
128 if (GetID() != WINDOW_VIDEO_PLAYLIST && g_settings.m_iVideoStartWindow != GetID())
130 g_settings.m_iVideoStartWindow = GetID();
134 return CGUIMediaWindow::OnMessage(message);
138 case GUI_MSG_CLICKED:
140 int iControl = message.GetSenderId();
141 if (iControl == CONTROL_STACK)
143 g_settings.m_videoStacking = !g_settings.m_videoStacking;
146 Update( m_vecItems->m_strPath );
148 else if (iControl == CONTROL_PLAY_DVD)
153 else if (iControl == CONTROL_BTNTYPE)
155 CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTNTYPE);
156 g_windowManager.SendMessage(msg);
158 int nSelected = msg.GetParam1();
159 int nNewWindow = WINDOW_VIDEO_FILES;
163 nNewWindow = WINDOW_VIDEO_FILES;
166 nNewWindow = WINDOW_VIDEO_NAV;
170 if (nNewWindow != GetID())
172 g_settings.m_iVideoStartWindow = nNewWindow;
174 g_windowManager.ChangeActiveWindow(nNewWindow);
175 CGUIMessage msg2(GUI_MSG_SETFOCUS, nNewWindow, CONTROL_BTNTYPE);
176 g_windowManager.SendMessage(msg2);
181 else if (m_viewControl.HasControl(iControl)) // list/thumb control
184 int iItem = m_viewControl.GetSelectedItem();
185 int iAction = message.GetParam1();
187 // iItem is checked for validity inside these routines
188 if (iAction == ACTION_QUEUE_ITEM || iAction == ACTION_MOUSE_MIDDLE_CLICK)
193 else if (iAction == ACTION_SHOW_INFO)
195 return OnInfo(iItem);
197 else if (iAction == ACTION_PLAYER_PLAY && !g_application.IsPlayingVideo())
199 return OnResumeItem(iItem);
201 else if (iAction == ACTION_DELETE_ITEM)
203 // is delete allowed?
204 if (g_settings.GetCurrentProfile().canWriteDatabases())
206 // must be at the title window
207 if (GetID() == WINDOW_VIDEO_NAV)
210 // or be at the files window and have file deletion enabled
211 else if (GetID() == WINDOW_VIDEO_FILES && g_guiSettings.GetBool("filelists.allowfiledeletion"))
214 // or be at the video playlists location
215 else if (m_vecItems->m_strPath.Equals("special://videoplaylists/"))
227 return CGUIMediaWindow::OnMessage(message);
230 void CGUIWindowVideoBase::UpdateButtons()
232 // Remove labels from the window selection
233 CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_BTNTYPE);
234 g_windowManager.SendMessage(msg);
236 // Add labels to the window selection
237 CStdString strItem = g_localizeStrings.Get(744); // Files
238 CGUIMessage msg2(GUI_MSG_LABEL_ADD, GetID(), CONTROL_BTNTYPE);
239 msg2.SetLabel(strItem);
240 g_windowManager.SendMessage(msg2);
242 strItem = g_localizeStrings.Get(14022); // Library
243 msg2.SetLabel(strItem);
244 g_windowManager.SendMessage(msg2);
246 // Select the current window as default item
247 int nWindow = g_settings.m_iVideoStartWindow-WINDOW_VIDEO_FILES;
248 CONTROL_SELECT_ITEM(CONTROL_BTNTYPE, nWindow);
250 CONTROL_ENABLE(CONTROL_BTNSCAN);
252 SET_CONTROL_LABEL(CONTROL_STACK, 14000); // Stack
253 SET_CONTROL_SELECTED(GetID(), CONTROL_STACK, g_settings.m_videoStacking);
254 CONTROL_ENABLE_ON_CONDITION(CONTROL_STACK, m_stackingAvailable);
256 CGUIMediaWindow::UpdateButtons();
259 void CGUIWindowVideoBase::OnInfo(CFileItem* pItem, const ADDON::ScraperPtr& scraper)
264 if (pItem->IsParentFolder() || pItem->m_bIsShareOrDrive || pItem->m_strPath.Equals("add"))
267 // ShowIMDB can kill the item as this window can be closed while we do it,
268 // so take a copy of the item now
269 CFileItem item(*pItem);
270 if (item.IsVideoDb() && item.HasVideoInfoTag())
272 if (item.GetVideoInfoTag()->m_strFileNameAndPath.IsEmpty())
273 item.m_strPath = item.GetVideoInfoTag()->m_strPath;
275 item.m_strPath = item.GetVideoInfoTag()->m_strFileNameAndPath;
279 if (item.m_bIsFolder && scraper && scraper->Content() != CONTENT_TVSHOWS)
282 CDirectory::GetDirectory(item.m_strPath, items,"",true,false,DIR_CACHE_ONCE,true,true);
285 // check for media files
286 bool bFoundFile(false);
287 for (int i = 0; i < items.Size(); ++i)
289 CFileItemPtr item2 = items[i];
291 if (item2->IsVideo() && !item2->IsPlayList() &&
292 !CUtil::ExcludeFileOrFolder(item2->m_strPath, g_advancedSettings.m_moviesExcludeFromScanRegExps))
294 item.m_strPath = item2->m_strPath;
295 item.m_bIsFolder = false;
301 // no video file in this folder
304 CGUIDialogOK::ShowAndGetInput(13346,20349,20022,20022);
310 // we need to also request any thumbs be applied to the folder item
311 if (pItem->m_bIsFolder)
312 item.SetProperty("set_folder_thumb", pItem->m_strPath);
314 bool modified = ShowIMDB(&item, scraper);
316 (g_windowManager.GetActiveWindow() == WINDOW_VIDEO_FILES ||
317 g_windowManager.GetActiveWindow() == WINDOW_VIDEO_NAV)) // since we can be called from the music library we need this check
319 int itemNumber = m_viewControl.GetSelectedItem();
320 Update(m_vecItems->m_strPath);
321 m_viewControl.SetSelectedItem(itemNumber);
325 // ShowIMDB is called as follows:
326 // 1. To lookup info on a file.
327 // 2. To lookup info on a folder (which may or may not contain a file)
328 // 3. To lookup info just for fun (no file or folder related)
330 // We just need the item object for this.
331 // A "blank" item object is sent for 3.
332 // If a folder is sent, currently it sets strFolder and bFolder
333 // this is only used for setting the folder thumb, however.
337 // 1. Check database to see if we have this information already
338 // 2. Else, check for a nfoFile to get the URL
339 // 3. Run a loop to check for refresh
340 // 4. If no URL is present do a search to get the URL
341 // 4. Once we have the URL, download the details
342 // 5. Once we have the details, add to the database if necessary (case 1,2)
343 // and show the information.
344 // 6. Check for a refresh, and if so, go to 3.
346 bool CGUIWindowVideoBase::ShowIMDB(CFileItem *item, const ScraperPtr &info2)
349 CLog::Log(LOGDEBUG,"CGUIWindowVideoBase::ShowIMDB");
350 CLog::Log(LOGDEBUG," strMovie = [%s]", strMovie.c_str());
351 CLog::Log(LOGDEBUG," strFile = [%s]", strFile.c_str());
352 CLog::Log(LOGDEBUG," strFolder = [%s]", strFolder.c_str());
353 CLog::Log(LOGDEBUG," bFolder = [%s]", ((int)bFolder ? "true" : "false"));
356 CGUIDialogProgress* pDlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
357 CGUIDialogSelect* pDlgSelect = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
358 CGUIDialogVideoInfo* pDlgInfo = (CGUIDialogVideoInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_INFO);
360 ScraperPtr info(info2); // use this as nfo might change it..
362 if (!pDlgProgress) return false;
363 if (!pDlgSelect) return false;
364 if (!pDlgInfo) return false;
366 // 1. Check for already downloaded information, and if we have it, display our dialog
367 // Return if no Refresh is needed.
370 CVideoInfoTag movieDetails;
371 movieDetails.Reset();
374 m_database.Open(); // since we can be called from the music library
376 if (info->Content() == CONTENT_MOVIES)
378 if (m_database.HasMovieInfo(item->m_strPath))
381 m_database.GetMovieInfo(item->m_strPath, movieDetails);
384 if (info->Content() == CONTENT_TVSHOWS)
386 if (item->m_bIsFolder)
388 if (m_database.HasTvShowInfo(item->m_strPath))
391 m_database.GetTvShowInfo(item->m_strPath, movieDetails);
397 if (item->HasVideoInfoTag())
398 EpisodeHint = item->GetVideoInfoTag()->m_iEpisode;
400 if ((idEpisode = m_database.GetEpisodeId(item->m_strPath,EpisodeHint)) > -1)
403 m_database.GetEpisodeInfo(item->m_strPath, movieDetails, idEpisode);
408 // As we cannot add an episode to a non-existing tvshow entry, we have to check the parent directory
409 // to see if it`s already in our video database. If it's not yet part of the database we will exit here.
412 // NOTE: This will fail for episodes on multipath shares, as the parent path isn't what is stored in the
413 // database. Possible solutions are to store the paths in the db separately and rely on the show
414 // stacking stuff, or to modify GetTvShowId to do support multipath:// shares
415 CStdString strParentDirectory;
416 URIUtils::GetParentPath(item->m_strPath, strParentDirectory);
417 if (m_database.GetTvShowId(strParentDirectory) < 0)
419 CLog::Log(LOGERROR,"%s: could not add episode [%s]. tvshow does not exist yet..", __FUNCTION__, item->m_strPath.c_str());
425 if (info->Content() == CONTENT_MUSICVIDEOS)
427 if (m_database.HasMusicVideoInfo(item->m_strPath))
430 m_database.GetMusicVideoInfo(item->m_strPath, movieDetails);
435 else if(item->HasVideoInfoTag())
438 movieDetails = *item->GetVideoInfoTag();
441 bool needsRefresh = false;
444 if (!info || info->Content() == CONTENT_NONE) // disable refresh button
445 movieDetails.m_strIMDBNumber = "xx"+movieDetails.m_strIMDBNumber;
446 *item->GetVideoInfoTag() = movieDetails;
447 pDlgInfo->SetMovie(item);
449 needsRefresh = pDlgInfo->NeedRefresh();
451 return pDlgInfo->HasUpdatedThumb();
454 // quietly return if Internet lookups are disabled
455 if (!g_settings.GetCurrentProfile().canWriteDatabases() && !g_passwordManager.bMasterUser)
461 CGUIDialogVideoScan* pDialog = (CGUIDialogVideoScan*)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_SCAN);
462 if (pDialog && pDialog->IsScanning())
464 CGUIDialogOK::ShowAndGetInput(13346,14057,-1,-1);
469 // 2. Look for a nfo File to get the search URL
470 SScanSettings settings;
471 info = m_database.GetScraperForPath(item->m_strPath,settings);
476 // Get the correct movie title
477 CStdString movieName = item->GetMovieName(settings.parent_name);
480 CVideoInfoScanner scanner;
481 bool hasDetails = false;
482 bool listNeedsUpdating = false;
483 bool ignoreNfo = false;
484 // 3. Run a loop so that if we Refresh we re-run this block
489 CNfoFile::NFOResult nfoResult = scanner.CheckForNFOFile(item,settings.parent_name_root,info,scrUrl);
490 if (nfoResult == CNfoFile::ERROR_NFO)
493 if (nfoResult != CNfoFile::NO_NFO)
499 if (nfoResult == CNfoFile::URL_NFO || nfoResult == CNfoFile::COMBINED_NFO || nfoResult == CNfoFile::FULL_NFO)
501 if (CGUIDialogYesNo::ShowAndGetInput(13346,20446,20447,20022))
512 // 4. if we don't have an url, or need to refresh the search
513 // then do the web search
515 if (info->Content() == CONTENT_TVSHOWS && !item->m_bIsFolder)
518 if (!hasDetails && (scrUrl.m_url.size() == 0 || needsRefresh))
520 // 4a. show dialog that we're busy querying www.imdb.com
521 CStdString strHeading;
522 strHeading.Format(g_localizeStrings.Get(197),info->Name().c_str());
523 pDlgProgress->SetHeading(strHeading);
524 pDlgProgress->SetLine(0, movieName);
525 pDlgProgress->SetLine(1, "");
526 pDlgProgress->SetLine(2, "");
527 pDlgProgress->StartModal();
528 pDlgProgress->Progress();
530 // 4b. do the websearch
532 CVideoInfoDownloader imdb(info);
533 int returncode = imdb.FindMovie(movieName, movielist, pDlgProgress);
536 pDlgProgress->Close();
537 if (movielist.size() > 0)
540 if (info->Content() == CONTENT_TVSHOWS)
542 pDlgSelect->SetHeading(iString);
544 for (unsigned int i = 0; i < movielist.size(); ++i)
545 pDlgSelect->Add(movielist[i].strTitle);
546 pDlgSelect->EnableButton(true, 413); // manual
547 pDlgSelect->DoModal();
549 // and wait till user selects one
550 int iSelectedMovie = pDlgSelect->GetSelectedLabel();
551 if (iSelectedMovie >= 0)
553 scrUrl = movielist[iSelectedMovie];
554 CLog::Log(LOGDEBUG, "%s: user selected movie '%s' with URL '%s'",
555 __FUNCTION__, scrUrl.strTitle.c_str(), scrUrl.m_url[0].m_url.c_str());
557 else if (!pDlgSelect->IsButtonPressed())
560 return listNeedsUpdating; // user backed out
564 else if (returncode == -1 || !CVideoInfoScanner::DownloadFailed(pDlgProgress))
566 pDlgProgress->Close();
570 // 4c. Check if url is still empty - occurs if user has selected to do a manual
571 // lookup, or if the IMDb lookup failed or was cancelled.
572 if (!hasDetails && scrUrl.m_url.size() == 0)
574 // Check for cancel of the progress dialog
575 pDlgProgress->Close();
576 if (pDlgProgress->IsCanceled())
579 return listNeedsUpdating;
582 // Prompt the user to input the movieName
584 if (info->Content() == CONTENT_TVSHOWS)
586 if (!CGUIDialogKeyboard::ShowAndGetInput(movieName, g_localizeStrings.Get(iString), false))
589 return listNeedsUpdating; // user backed out
596 // 5. Download the movie information
597 // show dialog that we're downloading the movie info
599 CStdString strPath=item->m_strPath;
600 if (item->IsVideoDb())
602 CFileItemPtr newItem(new CFileItem(*item->GetVideoInfoTag()));
604 strPath = item->GetVideoInfoTag()->m_strPath;
608 CFileItemPtr newItem(new CFileItem(*item));
612 if (item->m_bIsFolder)
613 URIUtils::GetParentPath(strPath,list.m_strPath);
615 URIUtils::GetDirectory(strPath,list.m_strPath);
618 if (info->Content() == CONTENT_TVSHOWS)
620 if (item->m_bIsFolder)
625 if (info->Content() == CONTENT_MUSICVIDEOS)
627 pDlgProgress->SetHeading(iString);
628 pDlgProgress->SetLine(0, movieName);
629 pDlgProgress->SetLine(1, scrUrl.strTitle);
630 pDlgProgress->SetLine(2, "");
631 pDlgProgress->StartModal();
632 pDlgProgress->Progress();
635 if (info->Content() == CONTENT_MOVIES)
636 m_database.DeleteMovie(item->m_strPath);
637 if (info->Content() == CONTENT_TVSHOWS && !item->m_bIsFolder)
638 m_database.DeleteEpisode(item->m_strPath,movieDetails.m_iDbId);
639 if (info->Content() == CONTENT_MUSICVIDEOS)
640 m_database.DeleteMusicVideo(item->m_strPath);
641 if (info->Content() == CONTENT_TVSHOWS && item->m_bIsFolder)
643 if (pDlgInfo->RefreshAll())
644 m_database.DeleteTvShow(item->m_strPath);
646 m_database.DeleteDetailsForTvShow(item->m_strPath);
649 if (scanner.RetrieveVideoInfo(list,settings.parent_name_root,info->Content(),!ignoreNfo,&scrUrl,pDlgInfo->RefreshAll(),pDlgProgress))
651 if (info->Content() == CONTENT_MOVIES)
652 m_database.GetMovieInfo(item->m_strPath,movieDetails);
653 if (info->Content() == CONTENT_MUSICVIDEOS)
654 m_database.GetMusicVideoInfo(item->m_strPath,movieDetails);
655 if (info->Content() == CONTENT_TVSHOWS)
657 // update tvshow info to get updated episode numbers
658 if (item->m_bIsFolder)
659 m_database.GetTvShowInfo(item->m_strPath,movieDetails);
661 m_database.GetEpisodeInfo(item->m_strPath,movieDetails);
664 // got all movie details :-)
665 OutputDebugString("got details\n");
666 pDlgProgress->Close();
668 // now show the imdb info
669 OutputDebugString("show info\n");
671 // remove directory caches and reload images
672 CUtil::DeleteVideoDatabaseDirectoryCache();
673 CGUIMessage reload(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
676 *item->GetVideoInfoTag() = movieDetails;
677 pDlgInfo->SetMovie(item);
679 item->SetThumbnailImage(pDlgInfo->GetThumbnail());
680 needsRefresh = pDlgInfo->NeedRefresh();
681 listNeedsUpdating = true;
685 pDlgProgress->Close();
686 if (pDlgProgress->IsCanceled())
689 return listNeedsUpdating; // user cancelled
691 CGUIDialogOK::ShowAndGetInput(195, movieName, 0, 0);
693 return listNeedsUpdating;
696 // 6. Check for a refresh
697 } while (needsRefresh);
699 return listNeedsUpdating;
702 void CGUIWindowVideoBase::OnQueueItem(int iItem)
704 if ( iItem < 0 || iItem >= m_vecItems->Size() ) return ;
706 // we take a copy so that we can alter the queue state
707 CFileItemPtr item(new CFileItem(*m_vecItems->Get(iItem)));
708 if (item->IsRAR() || item->IsZIP())
711 // Allow queuing of unqueueable items
712 // when we try to queue them directly
713 if (!item->CanQueue())
714 item->SetCanQueue(true);
716 CFileItemList queuedItems;
717 AddItemToPlayList(item, queuedItems);
718 // if party mode, add items but DONT start playing
719 if (g_partyModeManager.IsEnabled(PARTYMODECONTEXT_VIDEO))
721 g_partyModeManager.AddUserSongs(queuedItems, false);
725 g_playlistPlayer.Add(PLAYLIST_VIDEO, queuedItems);
726 // video does not auto play on queue like music
727 m_viewControl.SetSelectedItem(iItem + 1);
730 void CGUIWindowVideoBase::AddItemToPlayList(const CFileItemPtr &pItem, CFileItemList &queuedItems)
732 if (!pItem->CanQueue() || pItem->IsRAR() || pItem->IsZIP() || pItem->IsParentFolder()) // no zip/rar enques thank you!
735 if (pItem->m_bIsFolder)
737 if (pItem->IsParentFolder())
740 // Check if we add a locked share
741 if ( pItem->m_bIsShareOrDrive )
743 CFileItem item = *pItem;
744 if ( !g_passwordManager.IsItemUnlocked( &item, "video" ) )
750 GetDirectory(pItem->m_strPath, items);
751 FormatAndSort(items);
753 int watchedMode = g_settings.GetWatchMode(m_vecItems->GetContent());
754 bool unwatchedOnly = watchedMode == VIDEO_SHOW_UNWATCHED;
755 bool watchedOnly = watchedMode == VIDEO_SHOW_WATCHED;
756 for (int i = 0; i < items.Size(); ++i)
758 if (items[i]->m_bIsFolder)
760 CStdString strPath = items[i]->m_strPath;
761 URIUtils::RemoveSlashAtEnd(strPath);
763 if (strPath.size() > 6)
765 CStdString strSub = strPath.substr(strPath.size()-6);
766 if (strPath.Mid(strPath.size()-6).Equals("sample")) // skip sample folders
770 else if (items[i]->HasVideoInfoTag() &&
771 ((unwatchedOnly && items[i]->GetVideoInfoTag()->m_playCount > 0) ||
772 (watchedOnly && items[i]->GetVideoInfoTag()->m_playCount <= 0)))
775 AddItemToPlayList(items[i], queuedItems);
781 if (pItem->IsPlayList())
783 auto_ptr<CPlayList> pPlayList (CPlayListFactory::Create(*pItem));
787 if (!pPlayList->Load(pItem->m_strPath))
789 CGUIDialogOK::ShowAndGetInput(6, 0, 477, 0);
790 return; //hmmm unable to load playlist?
793 CPlayList playlist = *pPlayList;
794 for (int i = 0; i < (int)playlist.size(); ++i)
796 AddItemToPlayList(playlist[i], queuedItems);
801 else if(pItem->IsInternetStream())
802 { // just queue the internet stream, it will be expanded on play
803 queuedItems.Add(pItem);
805 else if (pItem->IsPlugin() && pItem->GetProperty("isplayable") == "true")
806 { // a playable python files
807 queuedItems.Add(pItem);
809 else if (pItem->IsVideoDb())
810 { // this case is needed unless we allow IsVideo() to return true for videodb items,
811 // but then we have issues with playlists of videodb items
812 CFileItemPtr item(new CFileItem(*pItem->GetVideoInfoTag()));
813 queuedItems.Add(item);
815 else if (!pItem->IsNFO() && pItem->IsVideo())
817 queuedItems.Add(pItem);
822 int CGUIWindowVideoBase::GetResumeItemOffset(const CFileItem *item)
824 // do not resume livetv
825 if (item->IsLiveTV())
830 long startoffset = 0;
832 if (item->IsStack() && (!g_guiSettings.GetBool("myvideos.treatstackasfile") ||
833 CFileItem(CStackDirectory::GetFirstStackedFile(item->m_strPath),false).IsDVDImage()) )
836 CStdStringArray movies;
837 GetStackedFiles(item->m_strPath, movies);
839 /* check if any of the stacked files have a resume bookmark */
840 for (unsigned i = 0; i<movies.size();i++)
843 if (db.GetResumeBookMark(movies[i], bookmark))
845 startoffset = (long)(bookmark.timeInSeconds*75);
846 startoffset += 0x10000000 * (i+1); /* store file number in here */
851 else if (!item->IsNFO() && !item->IsPlayList())
854 CStdString strPath = item->m_strPath;
855 if (item->IsVideoDb() && item->HasVideoInfoTag())
856 strPath = item->GetVideoInfoTag()->m_strFileNameAndPath;
858 if (db.GetResumeBookMark(strPath, bookmark))
859 startoffset = (long)(bookmark.timeInSeconds*75);
866 bool CGUIWindowVideoBase::OnClick(int iItem)
868 return CGUIMediaWindow::OnClick(iItem);
871 bool CGUIWindowVideoBase::OnSelect(int iItem)
873 if (iItem < 0 || iItem >= m_vecItems->Size())
876 CFileItemPtr item = m_vecItems->Get(iItem);
878 if (!item->m_bIsFolder && item->m_strPath != "add")
879 return OnFileAction(iItem, g_guiSettings.GetInt("myvideos.selectaction"));
881 return CGUIMediaWindow::OnSelect(iItem);
884 bool CGUIWindowVideoBase::OnFileAction(int iItem, int action)
886 CFileItemPtr item = m_vecItems->Get(iItem);
890 case SELECT_ACTION_CHOOSE:
892 CContextButtons choices;
895 if (!item->IsLiveTV())
897 CStdString resumeString = GetResumeString(*item);
898 if (!resumeString.IsEmpty())
901 choices.Add(SELECT_ACTION_RESUME, resumeString);
902 choices.Add(SELECT_ACTION_PLAY, 12021); // Start from beginning
906 choices.Add(SELECT_ACTION_PLAY, 208); // Play
908 choices.Add(SELECT_ACTION_INFO, 22081); // Info
909 choices.Add(SELECT_ACTION_MORE, 22082); // More
910 int value = CGUIDialogContextMenu::ShowAndGetChoice(choices);
914 return OnFileAction(iItem, value);
917 case SELECT_ACTION_PLAY_OR_RESUME:
918 return OnResumeItem(iItem);
919 case SELECT_ACTION_INFO:
923 case SELECT_ACTION_MORE:
926 case SELECT_ACTION_RESUME:
927 item->m_lStartOffset = STARTOFFSET_RESUME;
929 case SELECT_ACTION_PLAY:
933 return OnClick(iItem);
936 bool CGUIWindowVideoBase::OnInfo(int iItem)
938 if (iItem < 0 || iItem >= m_vecItems->Size())
941 CFileItemPtr item = m_vecItems->Get(iItem);
943 if (item->m_strPath.Equals("add") || item->IsParentFolder())
946 ADDON::ScraperPtr scraper;
947 if (!m_vecItems->IsPlugin() && !m_vecItems->IsRSS() && !m_vecItems->IsLiveTV())
950 if (item->IsVideoDb() &&
951 item->HasVideoInfoTag() &&
952 !item->GetVideoInfoTag()->m_strPath.IsEmpty())
954 strDir = item->GetVideoInfoTag()->m_strPath;
957 URIUtils::GetDirectory(item->m_strPath,strDir);
959 SScanSettings settings;
960 bool foundDirectly = false;
961 scraper = m_database.GetScraperForPath(strDir, settings, foundDirectly);
964 !(m_database.HasMovieInfo(item->m_strPath) ||
965 m_database.HasTvShowInfo(strDir) ||
966 m_database.HasEpisodeInfo(item->m_strPath)))
971 if (scraper && scraper->Content() == CONTENT_TVSHOWS && foundDirectly && !settings.parent_name_root) // dont lookup on root tvshow folder
975 OnInfo(item.get(), scraper);
980 void CGUIWindowVideoBase::OnRestartItem(int iItem)
982 CGUIMediaWindow::OnClick(iItem);
985 CStdString CGUIWindowVideoBase::GetResumeString(CFileItem item)
987 CStdString resumeString;
992 CStdString itemPath(item.m_strPath);
993 if (item.IsVideoDb())
994 itemPath = item.GetVideoInfoTag()->m_strFileNameAndPath;
995 if (db.GetResumeBookMark(itemPath, bookmark) )
996 resumeString.Format(g_localizeStrings.Get(12022).c_str(), StringUtils::SecondsToTimeString(lrint(bookmark.timeInSeconds)).c_str());
1002 bool CGUIWindowVideoBase::ShowResumeMenu(CFileItem &item)
1004 if (!item.m_bIsFolder && !item.IsLiveTV())
1006 CStdString resumeString = GetResumeString(item);
1007 if (!resumeString.IsEmpty())
1008 { // prompt user whether they wish to resume
1009 CContextButtons choices;
1010 choices.Add(1, resumeString);
1011 choices.Add(2, 12021); // start from the beginning
1012 int retVal = CGUIDialogContextMenu::ShowAndGetChoice(choices);
1014 return false; // don't do anything
1016 item.m_lStartOffset = STARTOFFSET_RESUME;
1022 bool CGUIWindowVideoBase::OnResumeItem(int iItem)
1024 if (iItem < 0 || iItem >= m_vecItems->Size()) return true;
1025 CFileItemPtr item = m_vecItems->Get(iItem);
1027 if (!item->m_bIsFolder)
1029 CStdString resumeString = GetResumeString(*item);
1030 if (!resumeString.IsEmpty())
1032 CContextButtons choices;
1033 choices.Add(SELECT_ACTION_RESUME, resumeString);
1034 choices.Add(SELECT_ACTION_PLAY, 12021); // Start from beginning
1035 int value = CGUIDialogContextMenu::ShowAndGetChoice(choices);
1038 return OnFileAction(iItem, value);
1042 return OnFileAction(iItem, SELECT_ACTION_PLAY);
1045 void CGUIWindowVideoBase::OnStreamDetails(const CStreamDetails &details, const CStdString &strFileName, long lFileId)
1051 db.SetStreamDetailsForFile(details, strFileName);
1053 db.SetStreamDetailsForFileId(details, lFileId);
1059 void CGUIWindowVideoBase::GetContextButtons(int itemNumber, CContextButtons &buttons)
1062 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
1063 item = m_vecItems->Get(itemNumber);
1065 // contextual buttons
1066 if (item && !item->GetPropertyBOOL("pluginreplacecontextitems"))
1068 if (!item->IsParentFolder())
1070 CStdString path(item->m_strPath);
1071 if (item->IsVideoDb() && item->HasVideoInfoTag())
1072 path = item->GetVideoInfoTag()->m_strFileNameAndPath;
1074 if (!item->IsPlugin() && !item->IsAddonsPath() && !item->IsLiveTV())
1076 if (URIUtils::IsStack(path))
1079 if (m_database.GetStackTimes(path,times))
1080 buttons.Add(CONTEXT_BUTTON_PLAY_PART, 20324);
1083 if (!m_vecItems->m_strPath.IsEmpty() && !item->m_strPath.Left(19).Equals("newsmartplaylist://")
1084 && !m_vecItems->m_strPath.Left(10).Equals("sources://"))
1086 buttons.Add(CONTEXT_BUTTON_QUEUE_ITEM, 13347); // Add to Playlist
1089 // allow a folder to be ad-hoc queued and played by the default player
1090 if (item->m_bIsFolder || (item->IsPlayList() &&
1091 !g_advancedSettings.m_playlistAsFolders))
1093 buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 208);
1096 if (!item->m_bIsFolder && !(item->IsPlayList() && !g_advancedSettings.m_playlistAsFolders))
1098 VECPLAYERCORES vecCores;
1099 if (item->IsVideoDb())
1102 item2.m_strPath = item->GetVideoInfoTag()->m_strFileNameAndPath;
1103 CPlayerCoreFactory::GetPlayers(item2, vecCores);
1106 CPlayerCoreFactory::GetPlayers(*item, vecCores);
1107 if (vecCores.size() > 1)
1108 buttons.Add(CONTEXT_BUTTON_PLAY_WITH, 15213);
1110 if (item->IsSmartPlayList())
1112 buttons.Add(CONTEXT_BUTTON_PLAY_PARTYMODE, 15216); // Play in Partymode
1115 // if autoresume is enabled then add restart video button
1116 // check to see if the Resume Video button is applicable
1117 if (GetResumeItemOffset(item.get()) > 0)
1119 buttons.Add(CONTEXT_BUTTON_RESUME_ITEM, GetResumeString(*(item.get()))); // Resume Video
1121 if (item->HasVideoInfoTag() && !item->m_bIsFolder && item->GetVideoInfoTag()->m_iEpisode > -1)
1123 buttons.Add(CONTEXT_BUTTON_PLAY_AND_QUEUE, 13412);
1125 if (item->IsSmartPlayList() || m_vecItems->IsSmartPlayList())
1126 buttons.Add(CONTEXT_BUTTON_EDIT_SMART_PLAYLIST, 586);
1129 CGUIMediaWindow::GetContextButtons(itemNumber, buttons);
1132 void CGUIWindowVideoBase::GetNonContextButtons(int itemNumber, CContextButtons &buttons)
1134 if (g_playlistPlayer.GetPlaylist(PLAYLIST_VIDEO).size() > 0)
1135 buttons.Add(CONTEXT_BUTTON_NOW_PLAYING, 13350);
1138 bool CGUIWindowVideoBase::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
1141 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
1142 item = m_vecItems->Get(itemNumber);
1145 case CONTEXT_BUTTON_SET_CONTENT:
1147 SScanSettings settings;
1148 ADDON::ScraperPtr info = m_database.GetScraperForPath(item->HasVideoInfoTag() ? item->GetVideoInfoTag()->m_strPath : item->m_strPath, settings);
1149 OnAssignContent(item->m_strPath,0, info, settings);
1152 case CONTEXT_BUTTON_PLAY_PART:
1154 CFileItemList items;
1155 CStdString path(item->m_strPath);
1156 if (item->IsVideoDb())
1157 path = item->GetVideoInfoTag()->m_strFileNameAndPath;
1159 CDirectory::GetDirectory(path,items);
1160 CGUIDialogFileStacking* dlg = (CGUIDialogFileStacking*)g_windowManager.GetWindow(WINDOW_DIALOG_FILESTACKING);
1161 if (!dlg) return true;
1162 dlg->SetNumberOfFiles(items.Size());
1164 int btn2 = dlg->GetSelectedFile();
1170 if (m_database.GetStackTimes(path,times))
1171 item->m_lStartOffset = times[btn2-2]*75; // wtf?
1174 item->m_lStartOffset = 0;
1176 // call CGUIMediaWindow::OnClick() as otherwise autoresume will kick in
1177 CGUIMediaWindow::OnClick(itemNumber);
1181 case CONTEXT_BUTTON_QUEUE_ITEM:
1182 OnQueueItem(itemNumber);
1185 case CONTEXT_BUTTON_PLAY_ITEM:
1186 PlayItem(itemNumber);
1189 case CONTEXT_BUTTON_PLAY_WITH:
1191 VECPLAYERCORES vecCores;
1192 if (item->IsVideoDb())
1194 CFileItem item2(*item->GetVideoInfoTag());
1195 CPlayerCoreFactory::GetPlayers(item2, vecCores);
1198 CPlayerCoreFactory::GetPlayers(*item, vecCores);
1199 g_application.m_eForcedNextPlayer = CPlayerCoreFactory::SelectPlayerDialog(vecCores);
1200 if (g_application.m_eForcedNextPlayer != EPC_NONE)
1201 OnClick(itemNumber);
1205 case CONTEXT_BUTTON_PLAY_PARTYMODE:
1206 g_partyModeManager.Enable(PARTYMODECONTEXT_VIDEO, m_vecItems->Get(itemNumber)->m_strPath);
1209 case CONTEXT_BUTTON_RESTART_ITEM:
1210 OnRestartItem(itemNumber);
1213 case CONTEXT_BUTTON_RESUME_ITEM:
1214 return OnFileAction(itemNumber, SELECT_ACTION_RESUME);
1216 case CONTEXT_BUTTON_NOW_PLAYING:
1217 g_windowManager.ActivateWindow(WINDOW_VIDEO_PLAYLIST);
1220 case CONTEXT_BUTTON_INFO:
1222 ADDON::ScraperPtr info;
1223 VIDEO::SScanSettings settings;
1224 GetScraperForItem(item.get(), info, settings);
1226 OnInfo(item.get(),info);
1229 case CONTEXT_BUTTON_STOP_SCANNING:
1231 CGUIDialogVideoScan *pScanDlg = (CGUIDialogVideoScan *)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_SCAN);
1232 if (pScanDlg && pScanDlg->IsScanning())
1233 pScanDlg->StopScanning();
1236 case CONTEXT_BUTTON_SCAN:
1237 case CONTEXT_BUTTON_UPDATE_TVSHOW:
1241 ADDON::ScraperPtr info;
1242 SScanSettings settings;
1243 GetScraperForItem(item.get(), info, settings);
1244 CStdString strPath = item->m_strPath;
1245 if (item->IsVideoDb() && (!item->m_bIsFolder || item->GetVideoInfoTag()->m_strPath.IsEmpty()))
1248 if (item->IsVideoDb())
1249 strPath = item->GetVideoInfoTag()->m_strPath;
1251 if (!info || info->Content() == CONTENT_NONE)
1254 if (item->m_bIsFolder)
1256 m_database.SetPathHash(strPath,""); // to force scan
1260 OnInfo(item.get(),info);
1264 case CONTEXT_BUTTON_DELETE:
1265 OnDeleteItem(itemNumber);
1267 case CONTEXT_BUTTON_EDIT_SMART_PLAYLIST:
1269 CStdString playlist = m_vecItems->Get(itemNumber)->IsSmartPlayList() ? m_vecItems->Get(itemNumber)->m_strPath : m_vecItems->m_strPath; // save path as activatewindow will destroy our items
1270 if (CGUIDialogSmartPlaylistEditor::EditPlaylist(playlist, "video"))
1272 m_vecItems->RemoveDiscCache(GetID());
1273 Update(m_vecItems->m_strPath);
1277 case CONTEXT_BUTTON_RENAME:
1278 OnRenameItem(itemNumber);
1280 case CONTEXT_BUTTON_MARK_WATCHED:
1282 int newSelection = m_viewControl.GetSelectedItem() + 1;
1283 MarkWatched(item,true);
1284 m_viewControl.SetSelectedItem(newSelection);
1286 CUtil::DeleteVideoDatabaseDirectoryCache();
1287 Update(m_vecItems->m_strPath);
1290 case CONTEXT_BUTTON_MARK_UNWATCHED:
1291 MarkWatched(item,false);
1292 CUtil::DeleteVideoDatabaseDirectoryCache();
1293 Update(m_vecItems->m_strPath);
1295 case CONTEXT_BUTTON_PLAY_AND_QUEUE:
1296 return OnPlayAndQueueMedia(item);
1300 return CGUIMediaWindow::OnContextButton(itemNumber, button);
1303 void CGUIWindowVideoBase::GetStackedFiles(const CStdString &strFilePath1, vector<CStdString> &movies)
1305 CStdString strFilePath = strFilePath1; // we're gonna be altering it
1309 CURL url(strFilePath);
1310 if (url.GetProtocol() == "stack")
1312 CStackDirectory dir;
1313 CFileItemList items;
1314 dir.GetDirectory(strFilePath, items);
1315 for (int i = 0; i < items.Size(); ++i)
1316 movies.push_back(items[i]->m_strPath);
1319 movies.push_back(strFilePath);
1322 bool CGUIWindowVideoBase::OnPlayMedia(int iItem)
1324 if ( iItem < 0 || iItem >= (int)m_vecItems->Size() )
1327 CFileItemPtr pItem = m_vecItems->Get(iItem);
1330 if (g_partyModeManager.IsEnabled(PARTYMODECONTEXT_VIDEO))
1332 CPlayList playlistTemp;
1333 playlistTemp.Add(pItem);
1334 g_partyModeManager.AddUserSongs(playlistTemp, true);
1338 // Reset Playlistplayer, playback started now does
1339 // not use the playlistplayer.
1340 g_playlistPlayer.Reset();
1341 g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_NONE);
1343 CFileItem item(*pItem);
1344 if (pItem->IsVideoDb())
1346 item.m_strPath = pItem->GetVideoInfoTag()->m_strFileNameAndPath;
1347 item.SetProperty("original_listitem_url", pItem->m_strPath);
1355 bool CGUIWindowVideoBase::OnPlayAndQueueMedia(const CFileItemPtr &item)
1357 // Get the current playlist and make sure it is not shuffled
1358 int iPlaylist = m_guiState->GetPlaylist();
1359 if (iPlaylist != PLAYLIST_NONE && g_playlistPlayer.IsShuffled(iPlaylist))
1360 g_playlistPlayer.SetShuffle(iPlaylist, false);
1362 // Call the base method to actually queue the items
1363 // and start playing the given item
1364 return CGUIMediaWindow::OnPlayAndQueueMedia(item);
1367 void CGUIWindowVideoBase::PlayMovie(const CFileItem *item)
1369 CFileItemList movieList;
1370 int selectedFile = 1;
1371 long startoffset = item->m_lStartOffset;
1373 if (item->IsStack() && (!g_guiSettings.GetBool("myvideos.treatstackasfile") ||
1374 CFileItem(CStackDirectory::GetFirstStackedFile(item->m_strPath),false).IsDVDImage()) )
1376 CStdStringArray movies;
1377 GetStackedFiles(item->m_strPath, movies);
1379 if (item->m_lStartOffset == STARTOFFSET_RESUME)
1381 startoffset = GetResumeItemOffset(item);
1383 if (startoffset & 0xF0000000) /* file is specified as a flag */
1385 selectedFile = (startoffset>>28);
1386 startoffset = startoffset & ~0xF0000000;
1390 /* attempt to start on a specific time in a stack */
1391 /* if we are lucky, we might have stored timings for */
1392 /* this stack at some point */
1396 /* figure out what file this time offset is */
1398 m_database.GetStackTimes(item->m_strPath, times);
1400 for (unsigned i = 0; i < times.size(); i++)
1402 totaltime += times[i]*75;
1403 if (startoffset < totaltime )
1406 startoffset -= totaltime - times[i]*75; /* rebase agains selected file */
1414 { // show file stacking dialog
1415 CGUIDialogFileStacking* dlg = (CGUIDialogFileStacking*)g_windowManager.GetWindow(WINDOW_DIALOG_FILESTACKING);
1418 dlg->SetNumberOfFiles(movies.size());
1420 selectedFile = dlg->GetSelectedFile();
1421 if (selectedFile < 1)
1425 // add to our movie list
1426 for (unsigned int i = 0; i < movies.size(); i++)
1428 CFileItemPtr movieItem(new CFileItem(movies[i], false));
1429 movieList.Add(movieItem);
1434 CFileItemPtr movieItem(new CFileItem(*item));
1435 movieList.Add(movieItem);
1438 g_playlistPlayer.Reset();
1439 g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_VIDEO);
1440 CPlayList& playlist = g_playlistPlayer.GetPlaylist(PLAYLIST_VIDEO);
1442 for (int i = selectedFile - 1; i < (int)movieList.Size(); ++i)
1444 if (i == selectedFile - 1)
1445 movieList[i]->m_lStartOffset = startoffset;
1446 playlist.Add(movieList[i]);
1449 if(m_thumbLoader.IsLoading())
1450 m_thumbLoader.StopAsync();
1453 g_playlistPlayer.Play(0);
1455 if(!g_application.IsPlayingVideo())
1456 m_thumbLoader.Load(*m_vecItems);
1459 void CGUIWindowVideoBase::OnDeleteItem(int iItem)
1461 if ( iItem < 0 || iItem >= m_vecItems->Size())
1464 OnDeleteItem(m_vecItems->Get(iItem));
1466 Update(m_vecItems->m_strPath);
1467 m_viewControl.SetSelectedItem(iItem);
1470 void CGUIWindowVideoBase::OnDeleteItem(CFileItemPtr item)
1472 // HACK: stacked files need to be treated as folders in order to be deleted
1473 if (item->IsStack())
1474 item->m_bIsFolder = true;
1475 if (g_settings.GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE &&
1476 g_settings.GetCurrentProfile().filesLocked())
1478 if (!g_passwordManager.IsMasterLockUnlocked(true))
1482 if (g_guiSettings.GetBool("filelists.allowfiledeletion") &&
1483 CUtil::SupportsFileOperations(item->m_strPath))
1484 CFileUtils::DeleteItem(item);
1487 void CGUIWindowVideoBase::MarkWatched(const CFileItemPtr &item, bool bMark)
1489 if (!g_settings.GetCurrentProfile().canWriteDatabases())
1491 // dont allow update while scanning
1492 CGUIDialogVideoScan* pDialogScan = (CGUIDialogVideoScan*)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_SCAN);
1493 if (pDialogScan && pDialogScan->IsScanning())
1495 CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1499 CVideoDatabase database;
1500 if (database.Open())
1502 CFileItemList items;
1503 if (item->m_bIsFolder)
1505 CStdString strPath = item->m_strPath;
1506 CDirectory::GetDirectory(strPath, items);
1511 for (int i=0;i<items.Size();++i)
1513 CFileItemPtr pItem=items[i];
1515 if (pItem->HasVideoInfoTag() &&
1516 (( bMark && pItem->GetVideoInfoTag()->m_playCount) ||
1517 (!bMark && !(pItem->GetVideoInfoTag()->m_playCount))))
1520 // Clear resume bookmark
1522 database.ClearBookMarksOfFile(pItem->m_strPath, CBookmark::RESUME);
1524 database.SetPlayCount(*pItem, bMark ? 1 : 0);
1531 //Add change a title's name
1532 void CGUIWindowVideoBase::UpdateVideoTitle(const CFileItem* pItem)
1534 // dont allow update while scanning
1535 CGUIDialogVideoScan* pDialogScan = (CGUIDialogVideoScan*)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_SCAN);
1536 if (pDialogScan && pDialogScan->IsScanning())
1538 CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1542 CVideoInfoTag detail;
1543 CVideoDatabase database;
1545 CVideoDatabaseDirectory dir;
1546 CQueryParams params;
1547 dir.GetQueryParams(pItem->m_strPath,params);
1548 int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
1550 VIDEODB_CONTENT_TYPE iType=VIDEODB_CONTENT_MOVIES;
1551 if (pItem->HasVideoInfoTag() && (!pItem->GetVideoInfoTag()->m_strShowTitle.IsEmpty() ||
1552 pItem->GetVideoInfoTag()->m_iEpisode > 0))
1554 iType = VIDEODB_CONTENT_TVSHOWS;
1556 if (pItem->HasVideoInfoTag() && pItem->GetVideoInfoTag()->m_iSeason > -1 && !pItem->m_bIsFolder)
1557 iType = VIDEODB_CONTENT_EPISODES;
1558 if (pItem->HasVideoInfoTag() && !pItem->GetVideoInfoTag()->m_strArtist.IsEmpty())
1559 iType = VIDEODB_CONTENT_MUSICVIDEOS;
1560 if (params.GetSetId() != -1 && params.GetMovieId() == -1)
1561 iType = VIDEODB_CONTENT_MOVIE_SETS;
1562 if (iType == VIDEODB_CONTENT_MOVIES)
1563 database.GetMovieInfo("", detail, pItem->GetVideoInfoTag()->m_iDbId);
1564 if (iType == VIDEODB_CONTENT_MOVIE_SETS)
1566 database.GetSetById(params.GetSetId(),detail.m_strTitle);
1567 iDbId = params.GetSetId();
1569 if (iType == VIDEODB_CONTENT_EPISODES)
1570 database.GetEpisodeInfo(pItem->m_strPath,detail,pItem->GetVideoInfoTag()->m_iDbId);
1571 if (iType == VIDEODB_CONTENT_TVSHOWS)
1572 database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath,detail,pItem->GetVideoInfoTag()->m_iDbId);
1573 if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
1574 database.GetMusicVideoInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath,detail,pItem->GetVideoInfoTag()->m_iDbId);
1576 CStdString strInput;
1577 strInput = detail.m_strTitle;
1580 if (!CGUIDialogKeyboard::ShowAndGetInput(strInput, g_localizeStrings.Get(16105), false))
1583 database.UpdateMovieTitle(iDbId, strInput, iType);
1586 void CGUIWindowVideoBase::LoadPlayList(const CStdString& strPlayList, int iPlayList /* = PLAYLIST_VIDEO */)
1588 // if partymode is active, we disable it
1589 if (g_partyModeManager.IsEnabled())
1590 g_partyModeManager.Disable();
1592 // load a playlist like .m3u, .pls
1593 // first get correct factory to load playlist
1594 auto_ptr<CPlayList> pPlayList (CPlayListFactory::Create(strPlayList));
1595 if (pPlayList.get())
1598 if (!pPlayList->Load(strPlayList))
1600 CGUIDialogOK::ShowAndGetInput(6, 0, 477, 0);
1601 return; //hmmm unable to load playlist?
1605 if (g_application.ProcessAndStartPlaylist(strPlayList, *pPlayList, iPlayList))
1607 if (m_guiState.get())
1608 m_guiState->SetPlaylistDirectory("playlistvideo://");
1612 void CGUIWindowVideoBase::PlayItem(int iItem)
1614 // restrictions should be placed in the appropiate window code
1615 // only call the base code if the item passes since this clears
1616 // the currently playing temp playlist
1618 const CFileItemPtr pItem = m_vecItems->Get(iItem);
1619 // if its a folder, build a temp playlist
1620 if (pItem->m_bIsFolder && !pItem->IsPlugin())
1622 // take a copy so we can alter the queue state
1623 CFileItemPtr item(new CFileItem(*m_vecItems->Get(iItem)));
1625 // Allow queuing of unqueueable items
1626 // when we try to queue them directly
1627 if (!item->CanQueue())
1628 item->SetCanQueue(true);
1631 if (item->IsParentFolder())
1634 // recursively add items to list
1635 CFileItemList queuedItems;
1636 AddItemToPlayList(item, queuedItems);
1638 g_playlistPlayer.ClearPlaylist(PLAYLIST_VIDEO);
1639 g_playlistPlayer.Reset();
1640 g_playlistPlayer.Add(PLAYLIST_VIDEO, queuedItems);
1641 g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_VIDEO);
1642 g_playlistPlayer.Play();
1644 else if (pItem->IsPlayList())
1646 // load the playlist the old way
1647 LoadPlayList(pItem->m_strPath, PLAYLIST_VIDEO);
1651 // single item, play it
1656 bool CGUIWindowVideoBase::Update(const CStdString &strDirectory)
1658 if (m_thumbLoader.IsLoading())
1659 m_thumbLoader.StopThread();
1661 if (!CGUIMediaWindow::Update(strDirectory))
1664 m_thumbLoader.Load(*m_vecItems);
1669 bool CGUIWindowVideoBase::GetDirectory(const CStdString &strDirectory, CFileItemList &items)
1671 bool bResult = CGUIMediaWindow::GetDirectory(strDirectory,items);
1673 // add in the "New Playlist" item if we're in the playlists folder
1674 if ((items.m_strPath == "special://videoplaylists/") && !items.Contains("newplaylist://"))
1676 CFileItemPtr newPlaylist(new CFileItem(g_settings.GetUserDataItem("PartyMode-Video.xsp"),false));
1677 newPlaylist->SetLabel(g_localizeStrings.Get(16035));
1678 newPlaylist->SetLabelPreformated(true);
1679 newPlaylist->m_bIsFolder = true;
1680 items.Add(newPlaylist);
1682 /* newPlaylist.reset(new CFileItem("newplaylist://", false));
1683 newPlaylist->SetLabel(g_localizeStrings.Get(525));
1684 newPlaylist->SetLabelPreformated(true);
1685 items.Add(newPlaylist);
1687 newPlaylist.reset(new CFileItem("newsmartplaylist://video", false));
1688 newPlaylist->SetLabel(g_localizeStrings.Get(21437)); // "new smart playlist..."
1689 newPlaylist->SetLabelPreformated(true);
1690 items.Add(newPlaylist);
1693 m_stackingAvailable = !(items.IsTuxBox() || items.IsPlugin() ||
1694 items.IsAddonsPath() || items.IsRSS() ||
1695 items.IsInternetStream() || items.IsVideoDb());
1696 // we may also be in a tvshow files listing
1697 // (ideally this should be removed, and our stack regexps tidied up if necessary
1698 // No "normal" episodes should stack, and multi-parts should be supported)
1699 ADDON::ScraperPtr info = m_database.GetScraperForPath(strDirectory);
1700 if (info && info->Content() == CONTENT_TVSHOWS)
1701 m_stackingAvailable = false;
1703 if (m_stackingAvailable && !items.IsStack() && g_settings.m_videoStacking)
1709 void CGUIWindowVideoBase::OnPrepareFileItems(CFileItemList &items)
1711 if (!items.m_strPath.Equals("plugin://video/"))
1712 items.SetCachedVideoThumbs();
1714 if (items.GetContent() != "episodes")
1715 { // we don't set cached fanart for episodes, as this requires a db fetch per episode
1716 for (int i = 0; i < items.Size(); ++i)
1718 CFileItemPtr item = items[i];
1719 if (!item->HasProperty("fanart_image"))
1721 CStdString art = item->GetCachedFanart();
1722 if (CFile::Exists(art))
1723 item->SetProperty("fanart_image", art);
1729 void CGUIWindowVideoBase::AddToDatabase(int iItem)
1731 if (iItem < 0 || iItem >= m_vecItems->Size())
1734 CFileItemPtr pItem = m_vecItems->Get(iItem);
1735 if (pItem->IsParentFolder() || pItem->m_bIsFolder)
1738 CVideoInfoTag movie;
1742 // enter a new title
1743 CStdString strTitle = pItem->GetLabel();
1744 if (!CGUIDialogKeyboard::ShowAndGetInput(strTitle, g_localizeStrings.Get(528), false)) // Enter Title
1748 CGUIDialogSelect* pSelect = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1752 pSelect->SetHeading(530); // Select Genre
1754 CFileItemList items;
1755 if (!CDirectory::GetDirectory("videodb://1/1/", items))
1757 pSelect->SetItems(&items);
1758 pSelect->EnableButton(true, 531); // New Genre
1760 CStdString strGenre;
1761 int iSelected = pSelect->GetSelectedLabel();
1763 strGenre = items[iSelected]->GetLabel();
1764 else if (!pSelect->IsButtonPressed())
1767 // enter new genre string
1768 if (strGenre.IsEmpty())
1770 strGenre = g_localizeStrings.Get(532); // Manual Addition
1771 if (!CGUIDialogKeyboard::ShowAndGetInput(strGenre, g_localizeStrings.Get(533), false)) // Enter Genre
1772 return; // user backed out
1773 if (strGenre.IsEmpty())
1774 return; // no genre string
1778 movie.m_strTitle = strTitle;
1779 movie.m_strGenre = strGenre;
1781 // everything is ok, so add to database
1783 int idMovie = m_database.AddMovie(pItem->m_strPath);
1784 movie.m_strIMDBNumber.Format("xx%08i", idMovie);
1785 m_database.SetDetailsForMovie(pItem->m_strPath, movie);
1789 CGUIDialogOK::ShowAndGetInput(20177, movie.m_strTitle, movie.m_strGenre, movie.m_strIMDBNumber);
1791 // library view cache needs to be cleared
1792 CUtil::DeleteVideoDatabaseDirectoryCache();
1795 /// \brief Search the current directory for a string got from the virtual keyboard
1796 void CGUIWindowVideoBase::OnSearch()
1798 CStdString strSearch;
1799 if (!CGUIDialogKeyboard::ShowAndGetInput(strSearch, g_localizeStrings.Get(16017), false))
1802 strSearch.ToLower();
1805 m_dlgProgress->SetHeading(194);
1806 m_dlgProgress->SetLine(0, strSearch);
1807 m_dlgProgress->SetLine(1, "");
1808 m_dlgProgress->SetLine(2, "");
1809 m_dlgProgress->StartModal();
1810 m_dlgProgress->Progress();
1812 CFileItemList items;
1813 DoSearch(strSearch, items);
1816 m_dlgProgress->Close();
1820 CGUIDialogSelect* pDlgSelect = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1821 pDlgSelect->Reset();
1822 pDlgSelect->SetHeading(283);
1824 for (int i = 0; i < (int)items.Size(); i++)
1826 CFileItemPtr pItem = items[i];
1827 pDlgSelect->Add(pItem->GetLabel());
1830 pDlgSelect->DoModal();
1832 int iItem = pDlgSelect->GetSelectedLabel();
1836 CFileItemPtr pSelItem = items[iItem];
1838 OnSearchItemFound(pSelItem.get());
1842 CGUIDialogOK::ShowAndGetInput(194, 284, 0, 0);
1846 /// \brief React on the selected search item
1847 /// \param pItem Search result item
1848 void CGUIWindowVideoBase::OnSearchItemFound(const CFileItem* pSelItem)
1850 if (pSelItem->m_bIsFolder)
1852 CStdString strPath = pSelItem->m_strPath;
1853 CStdString strParentPath;
1854 URIUtils::GetParentPath(strPath, strParentPath);
1856 Update(strParentPath);
1858 if (pSelItem->IsVideoDb() && g_settings.m_bMyVideoNavFlatten)
1859 SetHistoryForPath("");
1861 SetHistoryForPath(strParentPath);
1863 strPath = pSelItem->m_strPath;
1865 if (pSelItem->IsSmb() && !URIUtils::HasSlashAtEnd(strPath))
1868 for (int i = 0; i < m_vecItems->Size(); i++)
1870 CFileItemPtr pItem = m_vecItems->Get(i);
1871 if (pItem->m_strPath == strPath)
1873 m_viewControl.SetSelectedItem(i);
1881 URIUtils::GetDirectory(pSelItem->m_strPath, strPath);
1885 if (pSelItem->IsVideoDb() && g_settings.m_bMyVideoNavFlatten)
1886 SetHistoryForPath("");
1888 SetHistoryForPath(strPath);
1890 for (int i = 0; i < (int)m_vecItems->Size(); i++)
1892 CFileItemPtr pItem = m_vecItems->Get(i);
1893 if (pItem->m_strPath == pSelItem->m_strPath)
1895 m_viewControl.SetSelectedItem(i);
1900 m_viewControl.SetFocused();
1903 int CGUIWindowVideoBase::GetScraperForItem(CFileItem *item, ADDON::ScraperPtr &info, SScanSettings& settings)
1908 if (m_vecItems->IsPlugin() || m_vecItems->IsRSS())
1913 else if(m_vecItems->IsLiveTV())
1919 bool foundDirectly = false;
1920 info = m_database.GetScraperForPath(item->HasVideoInfoTag() ? item->GetVideoInfoTag()->m_strPath : item->m_strPath, settings, foundDirectly);
1921 return foundDirectly ? 1 : 0;
1924 void CGUIWindowVideoBase::OnScan(const CStdString& strPath, bool scanAll)
1926 CGUIDialogVideoScan* pDialog = (CGUIDialogVideoScan*)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_SCAN);
1928 pDialog->StartScanning(strPath, scanAll);
1931 CStdString CGUIWindowVideoBase::GetStartFolder(const CStdString &dir)
1933 if (dir.Equals("$PLAYLISTS") || dir.Equals("Playlists"))
1934 return "special://videoplaylists/";
1935 else if (dir.Equals("Plugins") || dir.Equals("Addons"))
1936 return "addons://sources/video/";
1937 return CGUIMediaWindow::GetStartFolder(dir);
1940 void CGUIWindowVideoBase::AppendAndClearSearchItems(CFileItemList &searchItems, const CStdString &prependLabel, CFileItemList &results)
1942 if (!searchItems.Size())
1945 searchItems.Sort(g_guiSettings.GetBool("filelists.ignorethewhensorting") ? SORT_METHOD_LABEL_IGNORE_THE : SORT_METHOD_LABEL, SORT_ORDER_ASC);
1946 for (int i = 0; i < searchItems.Size(); i++)
1947 searchItems[i]->SetLabel(prependLabel + searchItems[i]->GetLabel());
1948 results.Append(searchItems);
1950 searchItems.Clear();
1953 bool CGUIWindowVideoBase::OnUnAssignContent(const CStdString &path, int label1, int label2, int label3)
1958 if (CGUIDialogYesNo::ShowAndGetInput(label1,label2,label3,20022,bCanceled))
1960 db.RemoveContentForPath(path);
1962 CUtil::DeleteVideoDatabaseDirectoryCache();
1969 ADDON::ScraperPtr info;
1970 SScanSettings settings;
1971 settings.exclude = true;
1972 db.SetScraperForPath(path,info,settings);
1980 void CGUIWindowVideoBase::OnAssignContent(const CStdString &path, int iFound, ADDON::ScraperPtr& info, SScanSettings& settings)
1987 info = db.GetScraperForPath(path, settings);
1990 ADDON::ScraperPtr info2(info);
1992 if (CGUIDialogContentSettings::Show(info, settings, bScan))
1994 if(settings.exclude || (!info && info2))
1996 OnUnAssignContent(path,20375,20340,20341);
1998 else if (info != info2)
2000 if (OnUnAssignContent(path,20442,20443,20444))
2004 db.SetScraperForPath(path,info,settings);
2008 CGUIDialogVideoScan* pDialog = (CGUIDialogVideoScan*)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_SCAN);
2010 pDialog->StartScanning(path, true);