2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "GUIUserMessages.h"
22 #include "GUIWindowVideoNav.h"
23 #include "music/windows/GUIWindowMusicNav.h"
24 #include "utils/FileUtils.h"
26 #include "utils/RegExp.h"
27 #include "PlayListPlayer.h"
28 #include "GUIPassword.h"
29 #include "dialogs/GUIDialogFileBrowser.h"
30 #include "filesystem/VideoDatabaseDirectory.h"
31 #include "playlists/PlayListFactory.h"
32 #include "dialogs/GUIDialogOK.h"
33 #include "addons/AddonManager.h"
34 #include "PartyModeManager.h"
35 #include "music/MusicDatabase.h"
36 #include "guilib/GUIWindowManager.h"
37 #include "dialogs/GUIDialogYesNo.h"
38 #include "dialogs/GUIDialogSelect.h"
39 #include "filesystem/Directory.h"
40 #include "filesystem/File.h"
42 #include "Application.h"
43 #include "ApplicationMessenger.h"
44 #include "profiles/ProfilesManager.h"
45 #include "settings/AdvancedSettings.h"
46 #include "settings/MediaSettings.h"
47 #include "settings/MediaSourceSettings.h"
48 #include "settings/Settings.h"
49 #include "guilib/Key.h"
50 #include "guilib/LocalizeStrings.h"
51 #include "storage/MediaManager.h"
52 #include "utils/LegacyPathTranslation.h"
53 #include "utils/log.h"
54 #include "utils/URIUtils.h"
55 #include "utils/StringUtils.h"
56 #include "TextureCache.h"
57 #include "guilib/GUIKeyboardFactory.h"
58 #include "video/VideoInfoScanner.h"
59 #include "video/dialogs/GUIDialogVideoInfo.h"
60 #include "pvr/recordings/PVRRecording.h"
62 using namespace XFILE;
63 using namespace VIDEODATABASEDIRECTORY;
66 #define CONTROL_BTNVIEWASICONS 2
67 #define CONTROL_BTNSORTBY 3
68 #define CONTROL_BTNSORTASC 4
69 #define CONTROL_BTNTYPE 5
70 #define CONTROL_BTNSEARCH 8
71 #define CONTROL_LABELFILES 12
73 #define CONTROL_BTN_FILTER 19
74 #define CONTROL_BTNSHOWMODE 10
75 #define CONTROL_BTNSHOWALL 14
76 #define CONTROL_UNLOCK 11
78 #define CONTROL_FILTER 15
79 #define CONTROL_BTNPARTYMODE 16
80 #define CONTROL_LABELEMPTY 18
82 #define CONTROL_UPDATE_LIBRARY 20
84 CGUIWindowVideoNav::CGUIWindowVideoNav(void)
85 : CGUIWindowVideoBase(WINDOW_VIDEO_NAV, "MyVideoNav.xml")
87 m_thumbLoader.SetObserver(this);
90 CGUIWindowVideoNav::~CGUIWindowVideoNav(void)
94 bool CGUIWindowVideoNav::OnAction(const CAction &action)
96 if (action.GetID() == ACTION_TOGGLE_WATCHED)
98 CFileItemPtr pItem = m_vecItems->Get(m_viewControl.GetSelectedItem());
99 if (pItem->IsParentFolder())
101 if (pItem && pItem->GetVideoInfoTag()->m_playCount == 0)
102 return OnContextButton(m_viewControl.GetSelectedItem(),CONTEXT_BUTTON_MARK_WATCHED);
103 if (pItem && pItem->GetVideoInfoTag()->m_playCount > 0)
104 return OnContextButton(m_viewControl.GetSelectedItem(),CONTEXT_BUTTON_MARK_UNWATCHED);
106 return CGUIWindowVideoBase::OnAction(action);
109 bool CGUIWindowVideoNav::OnMessage(CGUIMessage& message)
111 switch (message.GetMessage())
113 case GUI_MSG_WINDOW_RESET:
114 m_vecItems->SetPath("");
116 case GUI_MSG_WINDOW_DEINIT:
117 if (m_thumbLoader.IsLoading())
118 m_thumbLoader.StopThread();
120 case GUI_MSG_WINDOW_INIT:
122 /* We don't want to show Autosourced items (ie removable pendrives, memorycards) in Library mode */
123 m_rootDir.AllowNonLocalSources(false);
125 SetProperty("flattened", CSettings::Get().GetBool("myvideos.flatten"));
126 if (message.GetNumStringParams() && message.GetStringParam(0).Equals("Files") &&
127 CMediaSourceSettings::Get().GetSources("video")->empty())
129 message.SetStringParam("");
132 if (!CGUIWindowVideoBase::OnMessage(message))
139 case GUI_MSG_CLICKED:
141 int iControl = message.GetSenderId();
142 if (iControl == CONTROL_BTNPARTYMODE)
144 if (g_partyModeManager.IsEnabled())
145 g_partyModeManager.Disable();
148 if (!g_partyModeManager.Enable(PARTYMODECONTEXT_VIDEO))
150 SET_CONTROL_SELECTED(GetID(),CONTROL_BTNPARTYMODE,false);
154 // Playlist directory is the root of the playlist window
155 if (m_guiState.get()) m_guiState->SetPlaylistDirectory("playlistvideo://");
162 if (iControl == CONTROL_BTNSEARCH)
166 else if (iControl == CONTROL_BTNSHOWMODE)
168 CMediaSettings::Get().CycleWatchedMode(m_vecItems->GetContent());
169 CSettings::Get().Save();
170 OnFilterItems(GetProperty("filter").asString());
173 else if (iControl == CONTROL_BTNSHOWALL)
175 if (CMediaSettings::Get().GetWatchedMode(m_vecItems->GetContent()) == WatchedModeAll)
176 CMediaSettings::Get().SetWatchedMode(m_vecItems->GetContent(), WatchedModeUnwatched);
178 CMediaSettings::Get().SetWatchedMode(m_vecItems->GetContent(), WatchedModeAll);
179 CSettings::Get().Save();
180 OnFilterItems(GetProperty("filter").asString());
183 else if (iControl == CONTROL_UPDATE_LIBRARY)
185 if (!g_application.IsVideoScanning())
188 g_application.StopVideoScan();
193 // update the display
194 case GUI_MSG_SCAN_FINISHED:
195 case GUI_MSG_REFRESH_THUMBS:
199 return CGUIWindowVideoBase::OnMessage(message);
202 CStdString CGUIWindowVideoNav::GetQuickpathName(const CStdString& strPath) const
204 CStdString path = CLegacyPathTranslation::TranslateVideoDbPath(strPath);
205 if (path.Equals("videodb://movies/genres/"))
206 return "MovieGenres";
207 else if (path.Equals("videodb://movies/titles/"))
208 return "MovieTitles";
209 else if (path.Equals("videodb://movies/years/"))
211 else if (path.Equals("videodb://movies/actors/"))
212 return "MovieActors";
213 else if (path.Equals("videodb://movies/directors/"))
214 return "MovieDirectors";
215 else if (path.Equals("videodb://movies/studios/"))
216 return "MovieStudios";
217 else if (path.Equals("videodb://movies/sets/"))
219 else if (path.Equals("videodb://movies/countries/"))
220 return "MovieCountries";
221 else if (path.Equals("videodb://movies/tags/"))
223 else if (path.Equals("videodb://movies/"))
225 else if (path.Equals("videodb://tvshows/genres/"))
226 return "TvShowGenres";
227 else if (path.Equals("videodb://tvshows/titles/"))
228 return "TvShowTitles";
229 else if (path.Equals("videodb://tvshows/years/"))
230 return "TvShowYears";
231 else if (path.Equals("videodb://tvshows/actors/"))
232 return "TvShowActors";
233 else if (path.Equals("videodb://tvshows/studios/"))
234 return "TvShowStudios";
235 else if (path.Equals("videodb://tvshows/tags/"))
237 else if (path.Equals("videodb://tvshows/"))
239 else if (path.Equals("videodb://musicvideos/genres/"))
240 return "MusicVideoGenres";
241 else if (path.Equals("videodb://musicvideos/titles/"))
242 return "MusicVideoTitles";
243 else if (path.Equals("videodb://musicvideos/years/"))
244 return "MusicVideoYears";
245 else if (path.Equals("videodb://musicvideos/artists/"))
246 return "MusicVideoArtists";
247 else if (path.Equals("videodb://musicvideos/albums/"))
248 return "MusicVideoDirectors";
249 else if (path.Equals("videodb://musicvideos/tags/"))
250 return "MusicVideoTags";
251 else if (path.Equals("videodb://musicvideos/"))
252 return "MusicVideos";
253 else if (path.Equals("videodb://recentlyaddedmovies/"))
254 return "RecentlyAddedMovies";
255 else if (path.Equals("videodb://recentlyaddedepisodes/"))
256 return "RecentlyAddedEpisodes";
257 else if (path.Equals("videodb://recentlyaddedmusicvideos/"))
258 return "RecentlyAddedMusicVideos";
259 else if (path.Equals("special://videoplaylists/"))
261 else if (path.Equals("sources://video/"))
265 CLog::Log(LOGERROR, " CGUIWindowVideoNav::GetQuickpathName: Unknown parameter (%s)", strPath.c_str());
270 bool CGUIWindowVideoNav::GetDirectory(const CStdString &strDirectory, CFileItemList &items)
272 if (m_thumbLoader.IsLoading())
273 m_thumbLoader.StopThread();
275 items.ClearProperties();
277 bool bResult = CGUIWindowVideoBase::GetDirectory(strDirectory, items);
280 if (items.IsVideoDb())
282 XFILE::CVideoDatabaseDirectory dir;
284 dir.GetQueryParams(items.GetPath(),params);
285 VIDEODATABASEDIRECTORY::NODE_TYPE node = dir.GetDirectoryChildType(items.GetPath());
287 items.SetArt("thumb", "");
288 if (node == VIDEODATABASEDIRECTORY::NODE_TYPE_EPISODES ||
289 node == NODE_TYPE_SEASONS ||
290 node == NODE_TYPE_RECENTLY_ADDED_EPISODES)
292 CLog::Log(LOGDEBUG, "WindowVideoNav::GetDirectory");
293 // grab the show thumb
294 CVideoInfoTag details;
295 m_database.GetTvShowInfo("", details, params.GetTvShowId());
296 map<string, string> art;
297 if (m_database.GetArtForItem(details.m_iDbId, details.m_type, art))
299 items.AppendArt(art, "tvshow");
300 items.SetArtFallback("fanart", "tvshow.fanart");
301 if (node == NODE_TYPE_SEASONS)
302 { // set an art fallback for "thumb"
303 if (items.HasArt("tvshow.poster"))
304 items.SetArtFallback("thumb", "tvshow.poster");
305 else if (items.HasArt("tvshow.banner"))
306 items.SetArtFallback("thumb", "tvshow.banner");
311 items.SetProperty("fanart_color1", details.m_fanart.GetColor(0));
312 items.SetProperty("fanart_color2", details.m_fanart.GetColor(1));
313 items.SetProperty("fanart_color3", details.m_fanart.GetColor(2));
315 // save the show description (showplot)
316 items.SetProperty("showplot", details.m_strPlot);
318 // the container folder thumb is the parent (i.e. season or show)
319 if (node == NODE_TYPE_EPISODES || node == NODE_TYPE_RECENTLY_ADDED_EPISODES)
321 items.SetContent("episodes");
322 // grab the season thumb as the folder thumb
323 int seasonID = m_database.GetSeasonId(details.m_iDbId, params.GetSeason());
324 CGUIListItem::ArtMap seasonArt;
325 if (m_database.GetArtForItem(seasonID, "season", seasonArt))
327 items.AppendArt(art, "season");
328 // set an art fallback for "thumb"
329 if (items.HasArt("season.poster"))
330 items.SetArtFallback("thumb", "season.poster");
331 else if (items.HasArt("season.banner"))
332 items.SetArtFallback("thumb", "season.banner");
336 items.SetContent("seasons");
338 else if (node == NODE_TYPE_TITLE_MOVIES ||
339 node == NODE_TYPE_RECENTLY_ADDED_MOVIES)
340 items.SetContent("movies");
341 else if (node == NODE_TYPE_TITLE_TVSHOWS)
342 items.SetContent("tvshows");
343 else if (node == NODE_TYPE_TITLE_MUSICVIDEOS ||
344 node == NODE_TYPE_RECENTLY_ADDED_MUSICVIDEOS)
345 items.SetContent("musicvideos");
346 else if (node == NODE_TYPE_GENRE)
347 items.SetContent("genres");
348 else if (node == NODE_TYPE_COUNTRY)
349 items.SetContent("countries");
350 else if (node == NODE_TYPE_ACTOR)
352 if (params.GetContentType() == VIDEODB_CONTENT_MUSICVIDEOS)
353 items.SetContent("artists");
355 items.SetContent("actors");
357 else if (node == NODE_TYPE_DIRECTOR)
358 items.SetContent("directors");
359 else if (node == NODE_TYPE_STUDIO)
360 items.SetContent("studios");
361 else if (node == NODE_TYPE_YEAR)
362 items.SetContent("years");
363 else if (node == NODE_TYPE_MUSICVIDEOS_ALBUM)
364 items.SetContent("albums");
365 else if (node == NODE_TYPE_SETS)
366 items.SetContent("sets");
367 else if (node == NODE_TYPE_TAGS)
368 items.SetContent("tags");
370 items.SetContent("");
372 else if (!items.IsVirtualDirectoryRoot())
373 { // load info from the database
375 if (items.GetLabel().IsEmpty() && m_rootDir.IsSource(items.GetPath(), CMediaSourceSettings::Get().GetSources("video"), &label))
376 items.SetLabel(label);
377 if (!items.IsSourcesPath())
378 LoadVideoInfo(items);
381 CVideoDbUrl videoUrl;
382 if (videoUrl.FromString(items.GetPath()) && items.GetContent() == "tags" &&
383 !items.Contains("newtag://" + videoUrl.GetType()))
385 CFileItemPtr newTag(new CFileItem("newtag://" + videoUrl.GetType(), false));
386 newTag->SetLabel(g_localizeStrings.Get(20462));
387 newTag->SetLabelPreformated(true);
388 newTag->SetSpecialSort(SortSpecialOnTop);
395 void CGUIWindowVideoNav::LoadVideoInfo(CFileItemList &items)
397 LoadVideoInfo(items, m_database);
400 void CGUIWindowVideoNav::LoadVideoInfo(CFileItemList &items, CVideoDatabase &database, bool allowReplaceLabels)
402 // TODO: this could possibly be threaded as per the music info loading,
403 // we could also cache the info
404 if (!items.GetContent().IsEmpty() && !items.IsPlugin())
405 return; // don't load for listings that have content set and weren't created from plugins
407 CStdString content = items.GetContent();
408 // determine content only if it isn't set
409 if (content.IsEmpty())
411 content = database.GetContentForPath(items.GetPath());
412 items.SetContent(content.IsEmpty() ? "files" : content);
416 If we have a matching item in the library, so we can assign the metadata to it. In addition, we can choose
417 * whether the item is stacked down (eg in the case of folders representing a single item)
418 * whether or not we assign the library's labels to the item, or leave the item as is.
420 As certain users (read: certain developers) don't want either of these to occur, we compromise by stacking
421 items down only if stacking is available and enabled.
423 Similarly, we assign the "clean" library labels to the item only if the "Replace filenames with library titles"
426 const bool stackItems = items.GetProperty("isstacked").asBoolean() || (StackingAvailable(items) && CSettings::Get().GetBool("myvideos.stackvideos"));
427 const bool replaceLabels = allowReplaceLabels && CSettings::Get().GetBool("myvideos.replacelabels");
429 CFileItemList dbItems;
430 /* NOTE: In the future when GetItemsForPath returns all items regardless of whether they're "in the library"
431 we won't need the fetchedPlayCounts code, and can "simply" do this directly on absense of content. */
432 bool fetchedPlayCounts = false;
433 if (!content.IsEmpty())
435 database.GetItemsForPath(content, items.GetPath(), dbItems);
436 dbItems.SetFastLookup(true);
439 for (int i = 0; i < items.Size(); i++)
441 CFileItemPtr pItem = items[i];
443 if (!content.IsEmpty()) /* optical media will be stacked down, so it's path won't match the base path */
444 match = dbItems.Get(pItem->IsOpticalMediaFile() ? pItem->GetLocalMetadataPath() : pItem->GetPath());
447 pItem->UpdateInfo(*match, replaceLabels);
451 if (match->m_bIsFolder)
452 pItem->SetPath(match->GetVideoInfoTag()->m_strPath);
454 pItem->SetPath(match->GetVideoInfoTag()->m_strFileNameAndPath);
455 // if we switch from a file to a folder item it means we really shouldn't be sorting files and
456 // folders separately
457 if (pItem->m_bIsFolder != match->m_bIsFolder)
459 items.SetSortIgnoreFolders(true);
460 pItem->m_bIsFolder = match->m_bIsFolder;
466 /* NOTE: Currently we GetPlayCounts on our items regardless of whether content is set
467 as if content is set, GetItemsForPaths doesn't return anything not in the content tables.
468 This code can be removed once the content tables are always filled */
469 if (!pItem->m_bIsFolder && !fetchedPlayCounts)
471 database.GetPlayCounts(items.GetPath(), items);
472 fetchedPlayCounts = true;
475 // preferably use some information from PVR info tag if available
476 if (pItem->HasPVRRecordingInfoTag())
477 pItem->GetPVRRecordingInfoTag()->CopyClientInfo(pItem->GetVideoInfoTag());
479 // set the watched overlay
480 if (pItem->IsVideo())
481 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, pItem->HasVideoInfoTag() && pItem->GetVideoInfoTag()->m_playCount > 0);
486 void CGUIWindowVideoNav::UpdateButtons()
488 CGUIWindowVideoBase::UpdateButtons();
490 // Update object count
491 int iItems = m_vecItems->Size();
494 // check for parent dir and "all" items
495 // should always be the first two items
496 for (int i = 0; i <= (iItems>=2 ? 1 : 0); i++)
498 CFileItemPtr pItem = m_vecItems->Get(i);
499 if (pItem->IsParentFolder()) iItems--;
500 if (StringUtils::StartsWith(pItem->GetPath(), "/-1/")) iItems--;
503 if (m_vecItems->Size() > 2 &&
504 StringUtils::StartsWith(m_vecItems->Get(m_vecItems->Size()-1)->GetPath(), "/-1/"))
508 items.Format("%i %s", iItems, g_localizeStrings.Get(127).c_str());
509 SET_CONTROL_LABEL(CONTROL_LABELFILES, items);
511 // set the filter label
515 if (m_vecItems->GetPath().Equals("special://videoplaylists/"))
516 strLabel = g_localizeStrings.Get(136);
518 else if (m_vecItems->IsPlayList())
520 // get playlist name from path
522 URIUtils::Split(m_vecItems->GetPath(), strDummy, strLabel);
524 else if (m_vecItems->GetPath().Equals("sources://video/"))
525 strLabel = g_localizeStrings.Get(744);
526 // everything else is from a videodb:// path
527 else if (m_vecItems->IsVideoDb())
529 CVideoDatabaseDirectory dir;
530 dir.GetLabel(m_vecItems->GetPath(), strLabel);
533 strLabel = URIUtils::GetFileName(m_vecItems->GetPath());
535 SET_CONTROL_LABEL(CONTROL_FILTER, strLabel);
537 int watchMode = CMediaSettings::Get().GetWatchedMode(m_vecItems->GetContent());
538 SET_CONTROL_LABEL(CONTROL_BTNSHOWMODE, g_localizeStrings.Get(16100 + watchMode));
540 SET_CONTROL_SELECTED(GetID(), CONTROL_BTNSHOWALL, watchMode != WatchedModeAll);
542 SET_CONTROL_SELECTED(GetID(),CONTROL_BTNPARTYMODE, g_partyModeManager.IsEnabled());
544 CONTROL_ENABLE_ON_CONDITION(CONTROL_UPDATE_LIBRARY, !m_vecItems->IsAddonsPath() && !m_vecItems->IsPlugin() && !m_vecItems->IsScript());
547 bool CGUIWindowVideoNav::GetFilteredItems(const CStdString &filter, CFileItemList &items)
549 bool listchanged = CGUIMediaWindow::GetFilteredItems(filter, items);
550 listchanged |= ApplyWatchedFilter(items);
555 /// \brief Search for names, genres, artists, directors, and plots with search string \e strSearch in the
556 /// \brief video databases and return the found \e items
557 /// \param strSearch The search string
558 /// \param items Items Found
559 void CGUIWindowVideoNav::DoSearch(const CStdString& strSearch, CFileItemList& items)
561 CFileItemList tempItems;
562 CStdString strGenre = g_localizeStrings.Get(515); // Genre
563 CStdString strActor = g_localizeStrings.Get(20337); // Actor
564 CStdString strDirector = g_localizeStrings.Get(20339); // Director
565 CStdString strMovie = g_localizeStrings.Get(20338); // Movie
568 m_database.GetMoviesByName(strSearch, tempItems);
569 AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20338) + "] ", items);
571 m_database.GetEpisodesByName(strSearch, tempItems);
572 AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20359) + "] ", items);
574 m_database.GetTvShowsByName(strSearch, tempItems);
575 AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20364) + "] ", items);
577 m_database.GetMusicVideosByName(strSearch, tempItems);
578 AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20391) + "] ", items);
580 m_database.GetMusicVideosByAlbum(strSearch, tempItems);
581 AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(558) + "] ", items);
583 // get matching genres
584 m_database.GetMovieGenresByName(strSearch, tempItems);
585 AppendAndClearSearchItems(tempItems, "[" + strGenre + " - " + g_localizeStrings.Get(20342) + "] ", items);
587 m_database.GetTvShowGenresByName(strSearch, tempItems);
588 AppendAndClearSearchItems(tempItems, "[" + strGenre + " - " + g_localizeStrings.Get(20343) + "] ", items);
590 m_database.GetMusicVideoGenresByName(strSearch, tempItems);
591 AppendAndClearSearchItems(tempItems, "[" + strGenre + " - " + g_localizeStrings.Get(20389) + "] ", items);
594 m_database.GetMovieActorsByName(strSearch, tempItems);
595 AppendAndClearSearchItems(tempItems, "[" + strActor + " - " + g_localizeStrings.Get(20342) + "] ", items);
597 m_database.GetTvShowsActorsByName(strSearch, tempItems);
598 AppendAndClearSearchItems(tempItems, "[" + strActor + " - " + g_localizeStrings.Get(20343) + "] ", items);
600 m_database.GetMusicVideoArtistsByName(strSearch, tempItems);
601 AppendAndClearSearchItems(tempItems, "[" + strActor + " - " + g_localizeStrings.Get(20389) + "] ", items);
604 m_database.GetMovieDirectorsByName(strSearch, tempItems);
605 AppendAndClearSearchItems(tempItems, "[" + strDirector + " - " + g_localizeStrings.Get(20342) + "] ", items);
607 m_database.GetTvShowsDirectorsByName(strSearch, tempItems);
608 AppendAndClearSearchItems(tempItems, "[" + strDirector + " - " + g_localizeStrings.Get(20343) + "] ", items);
610 m_database.GetMusicVideoDirectorsByName(strSearch, tempItems);
611 AppendAndClearSearchItems(tempItems, "[" + strDirector + " - " + g_localizeStrings.Get(20389) + "] ", items);
614 m_database.GetEpisodesByPlot(strSearch, tempItems);
615 AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20365) + "] ", items);
617 m_database.GetMoviesByPlot(strSearch, tempItems);
618 AppendAndClearSearchItems(tempItems, "[" + strMovie + " " + g_localizeStrings.Get(207) + "] ", items);
621 void CGUIWindowVideoNav::PlayItem(int iItem)
623 // unlike additemtoplaylist, we need to check the items here
624 // before calling it since the current playlist will be stopped
627 // root is not allowed
628 if (m_vecItems->IsVirtualDirectoryRoot())
631 CGUIWindowVideoBase::PlayItem(iItem);
634 void CGUIWindowVideoNav::OnInfo(CFileItem* pItem, ADDON::ScraperPtr& scraper)
636 m_database.Open(); // since we can be called from the music library without being inited
637 if (pItem->IsVideoDb())
638 scraper = m_database.GetScraperForPath(pItem->GetVideoInfoTag()->m_strPath);
641 CStdString strPath,strFile;
642 URIUtils::Split(pItem->GetPath(),strPath,strFile);
643 scraper = m_database.GetScraperForPath(strPath);
646 CGUIWindowVideoBase::OnInfo(pItem,scraper);
649 bool CGUIWindowVideoNav::CanDelete(const CStdString& strPath)
652 CVideoDatabaseDirectory::GetQueryParams(strPath,params);
654 if (params.GetMovieId() != -1 ||
655 params.GetEpisodeId() != -1 ||
656 params.GetMVideoId() != -1 ||
657 (params.GetTvShowId() != -1 && params.GetSeason() <= -1
658 && !CVideoDatabaseDirectory::IsAllItem(strPath)))
664 void CGUIWindowVideoNav::OnDeleteItem(CFileItemPtr pItem)
666 if (m_vecItems->IsParentFolder())
669 if (!m_vecItems->IsVideoDb() && !pItem->IsVideoDb())
671 if (!pItem->GetPath().Equals("newsmartplaylist://video") &&
672 !pItem->GetPath().Equals("special://videoplaylists/") &&
673 !pItem->GetPath().Equals("sources://video/") &&
674 !StringUtils::StartsWithNoCase(pItem->GetPath(), "newtag://"))
675 CGUIWindowVideoBase::OnDeleteItem(pItem);
677 else if (StringUtils::StartsWithNoCase(pItem->GetPath(), "videodb://movies/sets/") &&
678 pItem->GetPath().size() > 22 && pItem->m_bIsFolder)
680 CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
681 pDialog->SetHeading(432);
683 strLabel.Format(g_localizeStrings.Get(433),pItem->GetLabel());
684 pDialog->SetLine(1, strLabel);
685 pDialog->SetLine(2, "");;
687 if (pDialog->IsConfirmed())
690 CDirectory::GetDirectory(pItem->GetPath(),items,"",DIR_FLAG_NO_FILE_DIRS);
691 for (int i=0;i<items.Size();++i)
692 OnDeleteItem(items[i]);
694 CVideoDatabaseDirectory dir;
696 dir.GetQueryParams(pItem->GetPath(),params);
697 m_database.DeleteSet(params.GetSetId());
700 else if (m_vecItems->GetContent() == "tags" && pItem->m_bIsFolder)
702 CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
703 pDialog->SetHeading(432);
705 strLabel.Format(g_localizeStrings.Get(433), pItem->GetLabel());
706 pDialog->SetLine(1, strLabel);
707 pDialog->SetLine(2, "");
709 if (pDialog->IsConfirmed())
711 CVideoDatabaseDirectory dir;
713 dir.GetQueryParams(pItem->GetPath(), params);
714 m_database.DeleteTag(params.GetTagId(), (VIDEODB_CONTENT_TYPE)params.GetContentType());
717 else if (m_vecItems->GetPath().Equals(CUtil::VideoPlaylistsLocation()) ||
718 m_vecItems->GetPath().Equals("special://videoplaylists/"))
720 pItem->m_bIsFolder = false;
721 CFileUtils::DeleteItem(pItem);
725 if (!DeleteItem(pItem.get()))
728 CStdString strDeletePath;
729 if (pItem->m_bIsFolder)
730 strDeletePath=pItem->GetVideoInfoTag()->m_strPath;
732 strDeletePath=pItem->GetVideoInfoTag()->m_strFileNameAndPath;
734 if (URIUtils::GetFileName(strDeletePath).Equals("VIDEO_TS.IFO"))
736 strDeletePath = URIUtils::GetDirectory(strDeletePath);
737 if (StringUtils::EndsWithNoCase(strDeletePath, "video_ts/"))
739 URIUtils::RemoveSlashAtEnd(strDeletePath);
740 strDeletePath = URIUtils::GetDirectory(strDeletePath);
743 if (URIUtils::HasSlashAtEnd(strDeletePath))
744 pItem->m_bIsFolder=true;
746 if (CSettings::Get().GetBool("filelists.allowfiledeletion") &&
747 CUtil::SupportsWriteFileOperations(strDeletePath))
749 pItem->SetPath(strDeletePath);
750 CGUIWindowVideoBase::OnDeleteItem(pItem);
754 CUtil::DeleteVideoDatabaseDirectoryCache();
757 bool CGUIWindowVideoNav::DeleteItem(CFileItem* pItem, bool bUnavailable /* = false */)
759 if (!pItem->HasVideoInfoTag() || !CanDelete(pItem->GetPath()))
762 VIDEODB_CONTENT_TYPE iType=VIDEODB_CONTENT_MOVIES;
763 if (pItem->HasVideoInfoTag() && !pItem->GetVideoInfoTag()->m_strShowTitle.IsEmpty())
764 iType = VIDEODB_CONTENT_TVSHOWS;
765 if (pItem->HasVideoInfoTag() && pItem->GetVideoInfoTag()->m_iSeason > -1 && !pItem->m_bIsFolder)
766 iType = VIDEODB_CONTENT_EPISODES;
767 if (pItem->HasVideoInfoTag() && !pItem->GetVideoInfoTag()->m_artist.empty())
768 iType = VIDEODB_CONTENT_MUSICVIDEOS;
770 // dont allow update while scanning
771 if (g_application.IsVideoScanning())
773 CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
778 CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
781 if (iType == VIDEODB_CONTENT_MOVIES)
782 pDialog->SetHeading(432);
783 if (iType == VIDEODB_CONTENT_EPISODES)
784 pDialog->SetHeading(20362);
785 if (iType == VIDEODB_CONTENT_TVSHOWS)
786 pDialog->SetHeading(20363);
787 if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
788 pDialog->SetHeading(20392);
792 pDialog->SetLine(0, g_localizeStrings.Get(662));
793 pDialog->SetLine(1, g_localizeStrings.Get(663));
794 pDialog->SetLine(2, "");;
800 strLine.Format(g_localizeStrings.Get(433),pItem->GetLabel());
801 pDialog->SetLine(0, strLine);
802 pDialog->SetLine(1, "");
803 pDialog->SetLine(2, "");;
807 if (!pDialog->IsConfirmed())
811 CVideoDatabase database;
814 database.GetFilePathById(pItem->GetVideoInfoTag()->m_iDbId, path, iType);
817 if (iType == VIDEODB_CONTENT_MOVIES)
818 database.DeleteMovie(path);
819 if (iType == VIDEODB_CONTENT_EPISODES)
820 database.DeleteEpisode(path, pItem->GetVideoInfoTag()->m_iDbId);
821 if (iType == VIDEODB_CONTENT_TVSHOWS)
822 database.DeleteTvShow(path);
823 if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
824 database.DeleteMusicVideo(path);
826 if (iType == VIDEODB_CONTENT_TVSHOWS)
827 database.SetPathHash(path,"");
829 database.SetPathHash(URIUtils::GetDirectory(path), "");
834 void CGUIWindowVideoNav::GetContextButtons(int itemNumber, CContextButtons &buttons)
837 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
838 item = m_vecItems->Get(itemNumber);
840 CGUIWindowVideoBase::GetContextButtons(itemNumber, buttons);
842 if (item && item->GetProperty("pluginreplacecontextitems").asBoolean())
845 CVideoDatabaseDirectory dir;
846 NODE_TYPE node = dir.GetDirectoryChildType(m_vecItems->GetPath());
850 // nothing to do here
852 else if (m_vecItems->GetPath().Equals("sources://video/"))
854 // get the usual shares
855 CGUIDialogContextMenu::GetContextButtons("video", item, buttons);
856 // add scan button somewhere here
857 if (g_application.IsVideoScanning())
858 buttons.Add(CONTEXT_BUTTON_STOP_SCANNING, 13353); // Stop Scanning
859 if (!item->IsDVD() && item->GetPath() != "add" && !item->IsParentFolder() &&
860 (CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser))
862 CVideoDatabase database;
864 ADDON::ScraperPtr info = database.GetScraperForPath(item->GetPath());
866 if (!g_application.IsVideoScanning())
868 if (!item->IsLiveTV() && !item->IsPlugin() && !item->IsAddonsPath() && !URIUtils::IsUPnP(item->GetPath()))
870 if (info && info->Content() != CONTENT_NONE)
871 buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20442);
873 buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20333);
877 if (info && !g_application.IsVideoScanning())
878 buttons.Add(CONTEXT_BUTTON_SCAN, 13349);
883 // are we in the playlists location?
884 bool inPlaylists = m_vecItems->GetPath().Equals(CUtil::VideoPlaylistsLocation()) ||
885 m_vecItems->GetPath().Equals("special://videoplaylists/");
887 if (item->HasVideoInfoTag() && !item->GetVideoInfoTag()->m_artist.empty())
889 CMusicDatabase database;
891 if (database.GetArtistByName(StringUtils::Join(item->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator)) > -1)
892 buttons.Add(CONTEXT_BUTTON_GO_TO_ARTIST, 20396);
894 if (item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_strAlbum.size() > 0)
896 CMusicDatabase database;
898 if (database.GetAlbumByName(item->GetVideoInfoTag()->m_strAlbum) > -1)
899 buttons.Add(CONTEXT_BUTTON_GO_TO_ALBUM, 20397);
901 if (item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_strAlbum.size() > 0 &&
902 item->GetVideoInfoTag()->m_artist.size() > 0 &&
903 item->GetVideoInfoTag()->m_strTitle.size() > 0)
905 CMusicDatabase database;
907 if (database.GetSongByArtistAndAlbumAndTitle(StringUtils::Join(item->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator),
908 item->GetVideoInfoTag()->m_strAlbum,
909 item->GetVideoInfoTag()->m_strTitle) > -1)
911 buttons.Add(CONTEXT_BUTTON_PLAY_OTHER, 20398);
914 if (!item->IsParentFolder())
916 ADDON::ScraperPtr info;
917 VIDEO::SScanSettings settings;
918 GetScraperForItem(item.get(), info, settings);
920 if (info && info->Content() == CONTENT_TVSHOWS)
921 buttons.Add(CONTEXT_BUTTON_INFO, item->m_bIsFolder ? 20351 : 20352);
922 else if (info && info->Content() == CONTENT_MUSICVIDEOS)
923 buttons.Add(CONTEXT_BUTTON_INFO,20393);
924 else if (info && info->Content() == CONTENT_MOVIES)
925 buttons.Add(CONTEXT_BUTTON_INFO, 13346);
927 // can we update the database?
928 if (CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser)
930 if (node == NODE_TYPE_TITLE_TVSHOWS)
932 if (g_application.IsVideoScanning())
933 buttons.Add(CONTEXT_BUTTON_STOP_SCANNING, 13353);
935 buttons.Add(CONTEXT_BUTTON_SCAN, 13349);
937 if (!item->IsPlugin() && !item->IsScript() && !item->IsLiveTV() && !item->IsAddonsPath() &&
938 item->GetPath() != "sources://video/" && item->GetPath() != "special://videoplaylists/" &&
939 item->GetPath().Left(19) != "newsmartplaylist://" && item->GetPath().Left(14) != "newplaylist://" &&
940 item->GetPath().Left(9) != "newtag://")
942 if (item->m_bIsFolder)
944 // Have both options for folders since we don't know whether all childs are watched/unwatched
945 buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
946 buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103); //Mark as Watched
950 if (item->GetOverlayImage().Equals("OverlayWatched.png"))
951 buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
953 buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103); //Mark as Watched
956 if (!g_application.IsVideoScanning() && item->IsVideoDb() && item->HasVideoInfoTag() &&
957 (!item->m_bIsFolder || m_vecItems->GetContent().Equals("movies") || m_vecItems->GetContent().Equals("tvshows")))
959 buttons.Add(CONTEXT_BUTTON_EDIT, 16106);
962 if (node == NODE_TYPE_SEASONS && item->m_bIsFolder)
963 buttons.Add(CONTEXT_BUTTON_SET_SEASON_ART, 13511);
965 if (StringUtils::StartsWithNoCase(item->GetPath(), "videodb://movies/sets/") && item->GetPath().size() > 22 && item->m_bIsFolder) // sets
967 buttons.Add(CONTEXT_BUTTON_SET_MOVIESET_ART, 13511);
968 buttons.Add(CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS, 20465);
969 buttons.Add(CONTEXT_BUTTON_DELETE, 646);
972 if (m_vecItems->GetContent() == "tags" && item->m_bIsFolder) // tags
974 CVideoDbUrl videoUrl;
975 if (videoUrl.FromString(item->GetPath()))
977 std::string mediaType = videoUrl.GetItemType();
979 CStdString strLabelAdd; strLabelAdd.Format(g_localizeStrings.Get(20460), GetLocalizedType(videoUrl.GetItemType()).c_str());
980 CStdString strLabelRemove; strLabelRemove.Format(g_localizeStrings.Get(20461), GetLocalizedType(videoUrl.GetItemType()).c_str());
981 buttons.Add(CONTEXT_BUTTON_TAGS_ADD_ITEMS, strLabelAdd);
982 buttons.Add(CONTEXT_BUTTON_TAGS_REMOVE_ITEMS, strLabelRemove);
983 buttons.Add(CONTEXT_BUTTON_DELETE, 646);
987 if (node == NODE_TYPE_ACTOR && !dir.IsAllItem(item->GetPath()) && item->m_bIsFolder)
989 if (StringUtils::StartsWithNoCase(m_vecItems->GetPath(), "videodb://musicvideos")) // mvids
990 buttons.Add(CONTEXT_BUTTON_SET_ARTIST_THUMB, 13359);
992 buttons.Add(CONTEXT_BUTTON_SET_ACTOR_THUMB, 20403);
994 if (item->IsVideoDb() && item->HasVideoInfoTag() &&
995 (!item->m_bIsFolder || node == NODE_TYPE_TITLE_TVSHOWS))
996 buttons.Add(CONTEXT_BUTTON_DELETE, 646);
999 if (!m_vecItems->IsVideoDb() && !m_vecItems->IsVirtualDirectoryRoot())
1000 { // non-video db items, file operations are allowed
1001 if ((CSettings::Get().GetBool("filelists.allowfiledeletion") &&
1002 CUtil::SupportsWriteFileOperations(item->GetPath())) ||
1003 (inPlaylists && !URIUtils::GetFileName(item->GetPath()).Equals("PartyMode-Video.xsp")
1004 && (item->IsPlayList() || item->IsSmartPlayList())))
1006 buttons.Add(CONTEXT_BUTTON_DELETE, 117);
1007 buttons.Add(CONTEXT_BUTTON_RENAME, 118);
1009 // add "Set/Change content" to folders
1010 if (item->m_bIsFolder && !item->IsVideoDb() && !item->IsPlayList() && !item->IsSmartPlayList() && !item->IsLibraryFolder() && !item->IsLiveTV() && !item->IsPlugin() && !item->IsAddonsPath() && !URIUtils::IsUPnP(item->GetPath()))
1012 if (!g_application.IsVideoScanning())
1014 if (info && info->Content() != CONTENT_NONE)
1016 buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20442);
1017 buttons.Add(CONTEXT_BUTTON_SCAN, 13349);
1020 buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20333);
1024 if (item->IsPlugin() || item->IsScript() || m_vecItems->IsPlugin())
1025 buttons.Add(CONTEXT_BUTTON_PLUGIN_SETTINGS, 1045);
1030 // predicate used by sorting and set_difference
1031 bool compFileItemsByDbId(const CFileItemPtr& lhs, const CFileItemPtr& rhs)
1033 return lhs->HasVideoInfoTag() && rhs->HasVideoInfoTag() && lhs->GetVideoInfoTag()->m_iDbId < rhs->GetVideoInfoTag()->m_iDbId;
1036 bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
1039 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
1040 item = m_vecItems->Get(itemNumber);
1041 if (CGUIDialogContextMenu::OnContextButton("video", item, button))
1043 //TODO should we search DB for entries from plugins?
1044 if (button == CONTEXT_BUTTON_REMOVE_SOURCE && !item->IsPlugin()
1045 && !item->IsLiveTV() &&!item->IsRSS() && !URIUtils::IsUPnP(item->GetPath()))
1047 OnUnAssignContent(item->GetPath(),20375,20340,20341);
1054 case CONTEXT_BUTTON_EDIT:
1056 CONTEXT_BUTTON ret = (CONTEXT_BUTTON)CGUIDialogVideoInfo::ManageVideoItem(item);
1059 if (ret == CONTEXT_BUTTON_MARK_WATCHED)
1060 m_viewControl.SetSelectedItem(itemNumber + 1);
1067 case CONTEXT_BUTTON_SET_SEASON_ART:
1068 case CONTEXT_BUTTON_SET_ACTOR_THUMB:
1069 case CONTEXT_BUTTON_SET_ARTIST_THUMB:
1070 case CONTEXT_BUTTON_SET_MOVIESET_ART:
1072 // Grab the thumbnails from the web
1073 CFileItemList items;
1074 CFileItemPtr noneitem(new CFileItem("thumb://None", false));
1075 CStdString currentThumb;
1077 CStdString artistPath;
1078 string artType = "thumb";
1079 if (button == CONTEXT_BUTTON_SET_ARTIST_THUMB)
1081 CMusicDatabase database;
1083 idArtist = database.GetArtistByName(m_vecItems->Get(itemNumber)->GetLabel());
1084 database.GetArtistPath(idArtist, artistPath);
1085 currentThumb = database.GetArtForItem(idArtist, "artist", "thumb");
1086 if (currentThumb.empty())
1087 currentThumb = m_database.GetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, artType);
1089 else if (button == CONTEXT_BUTTON_SET_ACTOR_THUMB)
1090 currentThumb = m_database.GetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, artType);
1093 map<string, string> currentArt;
1094 artType = CGUIDialogVideoInfo::ChooseArtType(*m_vecItems->Get(itemNumber), currentArt);
1095 if (artType.empty())
1098 if (artType == "fanart")
1100 OnChooseFanart(*m_vecItems->Get(itemNumber));
1104 if (currentArt.find(artType) != currentArt.end())
1105 currentThumb = currentArt[artType];
1106 else if ((artType == "poster" || artType == "banner") && currentArt.find("thumb") != currentArt.end())
1107 currentThumb = currentArt["thumb"];
1109 if (!currentThumb.IsEmpty())
1111 CFileItemPtr item(new CFileItem("thumb://Current", false));
1112 item->SetArt("thumb", currentThumb);
1113 item->SetLabel(g_localizeStrings.Get(13512));
1116 noneitem->SetIconImage("DefaultFolder.png");
1117 noneitem->SetLabel(g_localizeStrings.Get(13515));
1119 vector<CStdString> thumbs;
1120 if (button != CONTEXT_BUTTON_SET_ARTIST_THUMB)
1123 if (button == CONTEXT_BUTTON_SET_SEASON_ART)
1124 m_database.GetTvShowInfo("",tag,m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iIdShow);
1126 tag = *m_vecItems->Get(itemNumber)->GetVideoInfoTag();
1127 if (button == CONTEXT_BUTTON_SET_SEASON_ART)
1128 tag.m_strPictureURL.GetThumbURLs(thumbs, artType, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iSeason);
1130 tag.m_strPictureURL.GetThumbURLs(thumbs, artType);
1132 for (unsigned int i = 0; i < thumbs.size(); i++)
1134 CStdString strItemPath;
1135 strItemPath.Format("thumb://Remote%i",i);
1136 CFileItemPtr item(new CFileItem(strItemPath, false));
1137 item->SetArt("thumb", thumbs[i]);
1138 item->SetIconImage("DefaultPicture.png");
1139 item->SetLabel(g_localizeStrings.Get(13513));
1142 // TODO: Do we need to clear the cached image?
1143 // CTextureCache::Get().ClearCachedImage(thumbs[i]);
1148 if (button == CONTEXT_BUTTON_SET_ARTIST_THUMB)
1150 CStdString strThumb = URIUtils::AddFileToFolder(artistPath, "folder.jpg");
1151 if (XFILE::CFile::Exists(strThumb))
1153 CFileItemPtr pItem(new CFileItem(strThumb,false));
1154 pItem->SetLabel(g_localizeStrings.Get(13514));
1155 pItem->SetArt("thumb", strThumb);
1160 noneitem->SetIconImage("DefaultArtist.png");
1163 if (button == CONTEXT_BUTTON_SET_ACTOR_THUMB)
1165 CStdString picturePath;
1166 CStdString strThumb = URIUtils::AddFileToFolder(picturePath, "folder.jpg");
1167 if (XFILE::CFile::Exists(strThumb))
1169 CFileItemPtr pItem(new CFileItem(strThumb,false));
1170 pItem->SetLabel(g_localizeStrings.Get(13514));
1171 pItem->SetArt("thumb", strThumb);
1176 noneitem->SetIconImage("DefaultActor.png");
1179 if (button == CONTEXT_BUTTON_SET_MOVIESET_ART)
1180 noneitem->SetIconImage("DefaultVideo.png");
1183 items.Add(noneitem);
1185 VECSOURCES sources=*CMediaSourceSettings::Get().GetSources("video");
1186 g_mediaManager.GetLocalDrives(sources);
1188 CGUIDialogVideoInfo::AddItemPathToFileBrowserSources(sources, *item);
1189 if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources,
1190 g_localizeStrings.Get(13511), result))
1192 return false; // user cancelled
1195 if (result == "thumb://Current")
1196 result = currentThumb; // user chose the one they have
1198 // delete the thumbnail if that's what the user wants, else overwrite with the
1200 if (result.Left(14) == "thumb://Remote")
1202 int number = atoi(result.Mid(14));
1203 result = thumbs[number];
1205 else if (result == "thumb://None")
1207 if (button == CONTEXT_BUTTON_SET_MOVIESET_ART ||
1208 button == CONTEXT_BUTTON_SET_ACTOR_THUMB ||
1209 button == CONTEXT_BUTTON_SET_SEASON_ART ||
1210 (button == CONTEXT_BUTTON_SET_ARTIST_THUMB && idArtist < 0))
1211 m_database.SetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, artType, result);
1216 db.SetArtForItem(idArtist, "artist", artType, result);
1219 CUtil::DeleteVideoDatabaseDirectoryCache();
1220 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
1221 g_windowManager.SendMessage(msg);
1226 case CONTEXT_BUTTON_TAGS_ADD_ITEMS:
1228 CVideoDbUrl videoUrl;
1229 if (!videoUrl.FromString(item->GetPath()))
1232 std::string mediaType = videoUrl.GetItemType();
1233 mediaType = mediaType.substr(0, mediaType.length() - 1);
1235 CFileItemList items;
1236 CStdString localizedType = GetLocalizedType(mediaType);
1237 CStdString strLabel; strLabel.Format(g_localizeStrings.Get(20464), localizedType.c_str());
1238 if (!GetItemsForTag(strLabel, mediaType, items, item->GetVideoInfoTag()->m_iDbId))
1241 CVideoDatabase videodb;
1242 if (!videodb.Open())
1245 for (int index = 0; index < items.Size(); index++)
1247 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1250 videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_iDbId, mediaType);
1253 // we need to clear any cached version of this tag's listing
1254 items.SetPath(item->GetPath());
1255 items.RemoveDiscCache(GetID());
1258 case CONTEXT_BUTTON_TAGS_REMOVE_ITEMS:
1260 CVideoDbUrl videoUrl;
1261 if (!videoUrl.FromString(item->GetPath()))
1264 std::string mediaType = videoUrl.GetItemType();
1265 mediaType = mediaType.substr(0, mediaType.length() - 1);
1267 CFileItemList items;
1268 CStdString localizedType = GetLocalizedType(mediaType);
1269 CStdString strLabel; strLabel.Format(g_localizeStrings.Get(20464), localizedType.c_str());
1270 if (!GetItemsForTag(strLabel, mediaType, items, item->GetVideoInfoTag()->m_iDbId, false))
1273 CVideoDatabase videodb;
1274 if (!videodb.Open())
1277 for (int index = 0; index < items.Size(); index++)
1279 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1282 videodb.RemoveTagFromItem(items[index]->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_iDbId, mediaType);
1285 // we need to clear any cached version of this tag's listing
1286 items.SetPath(item->GetPath());
1287 items.RemoveDiscCache(GetID());
1290 case CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS:
1292 CFileItemList originalItems;
1293 CFileItemList selectedItems;
1295 if (!CGUIDialogVideoInfo::GetMoviesForSet(item.get(), originalItems, selectedItems) || selectedItems.Size() == 0) // need at least one item selected
1297 VECFILEITEMS original = originalItems.GetList();
1298 std::sort(original.begin(), original.end(), compFileItemsByDbId);
1299 VECFILEITEMS selected = selectedItems.GetList();
1300 std::sort(selected.begin(), selected.end(), compFileItemsByDbId);
1302 bool refreshNeeded = false;
1303 // update the "added" items
1304 VECFILEITEMS addedItems;
1305 set_difference(selected.begin(),selected.end(), original.begin(),original.end(), std::back_inserter(addedItems), compFileItemsByDbId);
1306 for (VECFILEITEMS::iterator it = addedItems.begin(); it != addedItems.end(); ++it)
1308 if (CGUIDialogVideoInfo::SetMovieSet(it->get(), item.get()))
1309 refreshNeeded = true;
1311 // update the "deleted" items
1312 CFileItemPtr clearItem(new CFileItem());
1313 clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
1314 VECFILEITEMS deletedItems;
1315 set_difference(original.begin(),original.end(), selected.begin(),selected.end(), std::back_inserter(deletedItems), compFileItemsByDbId);
1316 for (VECFILEITEMS::iterator it = deletedItems.begin(); it != deletedItems.end(); ++it)
1318 if (CGUIDialogVideoInfo::SetMovieSet(it->get(), clearItem.get()))
1319 refreshNeeded = true;
1322 // we need to clear any cached version of this tag's listing
1327 case CONTEXT_BUTTON_GO_TO_ARTIST:
1330 CMusicDatabase database;
1332 strPath.Format("musicdb://artists/%ld/",database.GetArtistByName(StringUtils::Join(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator)));
1333 g_windowManager.ActivateWindow(WINDOW_MUSIC_NAV,strPath);
1336 case CONTEXT_BUTTON_GO_TO_ALBUM:
1339 CMusicDatabase database;
1341 strPath.Format("musicdb://albums/%ld/",database.GetAlbumByName(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strAlbum));
1342 g_windowManager.ActivateWindow(WINDOW_MUSIC_NAV,strPath);
1345 case CONTEXT_BUTTON_PLAY_OTHER:
1347 CMusicDatabase database;
1350 if (database.GetSong(database.GetSongByArtistAndAlbumAndTitle(StringUtils::Join(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator),m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strAlbum,
1351 m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strTitle),
1354 CApplicationMessenger::Get().PlayFile(song);
1363 return CGUIWindowVideoBase::OnContextButton(itemNumber, button);
1366 void CGUIWindowVideoNav::OnChooseFanart(const CFileItem &videoItem)
1368 if (!videoItem.HasVideoInfoTag())
1371 CFileItem item(videoItem);
1373 CFileItemList items;
1375 CVideoThumbLoader loader;
1376 loader.LoadItem(&item);
1378 if (item.HasArt("fanart"))
1380 CFileItemPtr itemCurrent(new CFileItem("fanart://Current",false));
1381 itemCurrent->SetArt("thumb", item.GetArt("fanart"));
1382 itemCurrent->SetLabel(g_localizeStrings.Get(20440));
1383 items.Add(itemCurrent);
1386 // add the none option
1388 CFileItemPtr itemNone(new CFileItem("fanart://None", false));
1389 itemNone->SetIconImage("DefaultVideo.png");
1390 itemNone->SetLabel(g_localizeStrings.Get(20439));
1391 items.Add(itemNone);
1395 VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
1396 g_mediaManager.GetLocalDrives(sources);
1397 CGUIDialogVideoInfo::AddItemPathToFileBrowserSources(sources, item);
1399 if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) || result.Equals("fanart://Current"))
1402 if (result.Equals("fanart://None") || !CFile::Exists(result))
1404 if (!result.IsEmpty() && flip)
1405 result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
1411 db.SetArtForItem(item.GetVideoInfoTag()->m_iDbId, item.GetVideoInfoTag()->m_type, "fanart", result);
1415 // clear view cache and reload images
1416 CUtil::DeleteVideoDatabaseDirectoryCache();
1421 bool CGUIWindowVideoNav::OnClick(int iItem)
1423 CFileItemPtr item = m_vecItems->Get(iItem);
1424 if (!item->m_bIsFolder && item->IsVideoDb() && !item->Exists())
1426 CLog::Log(LOGDEBUG, "%s called on '%s' but file doesn't exist", __FUNCTION__, item->GetPath().c_str());
1427 if (!DeleteItem(item.get(), true))
1432 m_viewControl.SetSelectedItem(iItem);
1435 else if (StringUtils::StartsWithNoCase(item->GetPath(), "newtag://"))
1437 // dont allow update while scanning
1438 if (g_application.IsVideoScanning())
1440 CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1446 if (!CGUIKeyboardFactory::ShowAndGetInput(strTag, g_localizeStrings.Get(20462), false))
1449 CVideoDatabase videodb;
1450 if (!videodb.Open())
1453 // get the media type and convert from plural to singular (by removing the trailing "s")
1454 CStdString mediaType = item->GetPath().Mid(9);
1455 mediaType = mediaType.Left(mediaType.size() - 1);
1456 CStdString localizedType = GetLocalizedType(mediaType);
1457 if (localizedType.empty())
1460 if (!videodb.GetSingleValue("tag", "tag.idTag", videodb.PrepareSQL("tag.strTag = '%s' AND tag.idTag IN (SELECT taglinks.idTag FROM taglinks WHERE taglinks.media_type = '%s')", strTag.c_str(), mediaType.c_str())).empty())
1462 CStdString strError; strError.Format(g_localizeStrings.Get(20463), strTag.c_str());
1463 CGUIDialogOK::ShowAndGetInput(20462, "", strError, "");
1467 int idTag = videodb.AddTag(strTag);
1468 CFileItemList items;
1469 CStdString strLabel; strLabel.Format(g_localizeStrings.Get(20464), localizedType.c_str());
1470 if (GetItemsForTag(strLabel, mediaType, items, idTag))
1472 for (int index = 0; index < items.Size(); index++)
1474 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1477 videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, idTag, mediaType);
1485 return CGUIWindowVideoBase::OnClick(iItem);
1488 CStdString CGUIWindowVideoNav::GetStartFolder(const CStdString &dir)
1490 if (dir.Equals("MovieGenres"))
1491 return "videodb://movies/genres/";
1492 else if (dir.Equals("MovieTitles"))
1493 return "videodb://movies/titles/";
1494 else if (dir.Equals("MovieYears"))
1495 return "videodb://movies/years/";
1496 else if (dir.Equals("MovieActors"))
1497 return "videodb://movies/actors/";
1498 else if (dir.Equals("MovieDirectors"))
1499 return "videodb://movies/directors/";
1500 else if (dir.Equals("MovieStudios"))
1501 return "videodb://movies/studios/";
1502 else if (dir.Equals("MovieSets"))
1503 return "videodb://movies/sets/";
1504 else if (dir.Equals("MovieCountries"))
1505 return "videodb://movies/countries/";
1506 else if (dir.Equals("MovieTags"))
1507 return "videodb://movies/tags/";
1508 else if (dir.Equals("Movies"))
1509 return "videodb://movies/";
1510 else if (dir.Equals("TvShowGenres"))
1511 return "videodb://tvshows/genres/";
1512 else if (dir.Equals("TvShowTitles"))
1513 return "videodb://tvshows/titles/";
1514 else if (dir.Equals("TvShowYears"))
1515 return "videodb://tvshows/years/";
1516 else if (dir.Equals("TvShowActors"))
1517 return "videodb://tvshows/actors/";
1518 else if (dir.Equals("TvShowStudios"))
1519 return "videodb://tvshows/studios/";
1520 else if (dir.Equals("TvShowTags"))
1521 return "videodb://tvshows/tags/";
1522 else if (dir.Equals("TvShows"))
1523 return "videodb://tvshows/";
1524 else if (dir.Equals("MusicVideoGenres"))
1525 return "videodb://musicvideos/genres/";
1526 else if (dir.Equals("MusicVideoTitles"))
1527 return "videodb://musicvideos/titles/";
1528 else if (dir.Equals("MusicVideoYears"))
1529 return "videodb://musicvideos/years/";
1530 else if (dir.Equals("MusicVideoArtists"))
1531 return "videodb://musicvideos/artists/";
1532 else if (dir.Equals("MusicVideoAlbums"))
1533 return "videodb://musicvideos/albums/";
1534 else if (dir.Equals("MusicVideoDirectors"))
1535 return "videodb://musicvideos/directors/";
1536 else if (dir.Equals("MusicVideoStudios"))
1537 return "videodb://musicvideos/studios/";
1538 else if (dir.Equals("MusicVideoTags"))
1539 return "videodb://musicvideos/tags/";
1540 else if (dir.Equals("MusicVideos"))
1541 return "videodb://musicvideos/";
1542 else if (dir.Equals("RecentlyAddedMovies"))
1543 return "videodb://recentlyaddedmovies/";
1544 else if (dir.Equals("RecentlyAddedEpisodes"))
1545 return "videodb://recentlyaddedepisodes/";
1546 else if (dir.Equals("RecentlyAddedMusicVideos"))
1547 return "videodb://recentlyaddedmusicvideos/";
1548 else if (dir.Equals("Files"))
1549 return "sources://video/";
1550 return CGUIWindowVideoBase::GetStartFolder(dir);
1553 bool CGUIWindowVideoNav::ApplyWatchedFilter(CFileItemList &items)
1555 bool listchanged = false;
1556 CVideoDatabaseDirectory dir;
1557 NODE_TYPE node = dir.GetDirectoryChildType(items.GetPath());
1559 // now filter watched items as necessary
1560 bool filterWatched=false;
1561 if (node == NODE_TYPE_EPISODES
1562 || node == NODE_TYPE_SEASONS
1563 || node == NODE_TYPE_SETS
1564 || node == NODE_TYPE_TAGS
1565 || node == NODE_TYPE_TITLE_MOVIES
1566 || node == NODE_TYPE_TITLE_TVSHOWS
1567 || node == NODE_TYPE_TITLE_MUSICVIDEOS
1568 || node == NODE_TYPE_RECENTLY_ADDED_EPISODES
1569 || node == NODE_TYPE_RECENTLY_ADDED_MOVIES
1570 || node == NODE_TYPE_RECENTLY_ADDED_MUSICVIDEOS)
1571 filterWatched = true;
1572 if (!items.IsVideoDb())
1573 filterWatched = true;
1574 if (items.GetContent() == "tvshows" &&
1575 (items.IsSmartPlayList() || items.IsLibraryFolder()))
1576 node = NODE_TYPE_TITLE_TVSHOWS; // so that the check below works
1578 int watchMode = CMediaSettings::Get().GetWatchedMode(m_vecItems->GetContent());
1580 for (int i = 0; i < items.Size(); i++)
1582 CFileItemPtr item = items.Get(i);
1584 if(item->HasVideoInfoTag() && (node == NODE_TYPE_TITLE_TVSHOWS || node == NODE_TYPE_SEASONS))
1586 if (watchMode == WatchedModeUnwatched)
1587 item->GetVideoInfoTag()->m_iEpisode = (int)item->GetProperty("unwatchedepisodes").asInteger();
1588 if (watchMode == WatchedModeWatched)
1589 item->GetVideoInfoTag()->m_iEpisode = (int)item->GetProperty("watchedepisodes").asInteger();
1590 if (watchMode == WatchedModeAll)
1591 item->GetVideoInfoTag()->m_iEpisode = (int)item->GetProperty("totalepisodes").asInteger();
1592 item->SetProperty("numepisodes", item->GetVideoInfoTag()->m_iEpisode);
1598 if((watchMode==WatchedModeWatched && item->GetVideoInfoTag()->m_playCount== 0)
1599 || (watchMode==WatchedModeUnwatched && item->GetVideoInfoTag()->m_playCount > 0))
1608 if(node == NODE_TYPE_TITLE_TVSHOWS || node == NODE_TYPE_SEASONS)
1610 // the watched filter may change the "numepisodes" property which is reflected in the TV_SHOWS and SEASONS nodes
1611 // therefore, the items labels have to be refreshed, and possibly the list needs resorting as well.
1612 items.ClearSortState(); // this is needed to force resorting even if sort method did not change
1613 FormatAndSort(items);
1619 bool CGUIWindowVideoNav::GetItemsForTag(const CStdString &strHeading, const std::string &type, CFileItemList &items, int idTag /* = -1 */, bool showAll /* = true */)
1621 CVideoDatabase videodb;
1622 if (!videodb.Open())
1625 MediaType mediaType = MediaTypeNone;
1626 std::string baseDir = "videodb://";
1627 std::string idColumn;
1628 if (type.compare("movie") == 0)
1630 mediaType = MediaTypeMovie;
1631 baseDir += "movies";
1632 idColumn = "idMovie";
1634 else if (type.compare("tvshow") == 0)
1636 mediaType = MediaTypeTvShow;
1637 baseDir += "tvshows";
1638 idColumn = "idShow";
1640 else if (type.compare("musicvideo") == 0)
1642 mediaType = MediaTypeMusicVideo;
1643 baseDir += "musicvideos";
1644 idColumn = "idMVideo";
1647 baseDir += "/titles/";
1648 CVideoDbUrl videoUrl;
1649 if (!videoUrl.FromString(baseDir))
1652 CVideoDatabase::Filter filter;
1656 videoUrl.AddOption("tagid", idTag);
1658 filter.where = videodb.PrepareSQL("%sview.%s NOT IN (SELECT taglinks.idMedia FROM taglinks WHERE taglinks.idTag = %d AND taglinks.media_type = '%s')", type.c_str(), idColumn.c_str(), idTag, type.c_str());
1661 CFileItemList listItems;
1662 if (!videodb.GetSortedVideos(mediaType, videoUrl.ToString(), SortDescription(), listItems, filter) || listItems.Size() <= 0)
1665 CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1669 listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1672 dialog->SetMultiSelection(true);
1673 dialog->SetHeading(strHeading);
1674 dialog->SetItems(&listItems);
1675 dialog->EnableButton(true, 186);
1678 items.Copy(dialog->GetSelectedItems());
1679 return items.Size() > 0;
1682 CStdString CGUIWindowVideoNav::GetLocalizedType(const std::string &strType)
1684 if (strType == "movie" || strType == "movies")
1685 return g_localizeStrings.Get(20342);
1686 else if (strType == "tvshow" || strType == "tvshows")
1687 return g_localizeStrings.Get(20343);
1688 else if (strType == "episode" || strType == "episodes")
1689 return g_localizeStrings.Get(20359);
1690 else if (strType == "musicvideo" || strType == "musicvideos")
1691 return g_localizeStrings.Get(20391);