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::OnPrepareFileItems(CFileItemList &items)
836 CGUIWindowVideoBase::OnPrepareFileItems(items);
839 void CGUIWindowVideoNav::GetContextButtons(int itemNumber, CContextButtons &buttons)
842 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
843 item = m_vecItems->Get(itemNumber);
845 CGUIWindowVideoBase::GetContextButtons(itemNumber, buttons);
847 if (item && item->GetProperty("pluginreplacecontextitems").asBoolean())
850 CVideoDatabaseDirectory dir;
851 NODE_TYPE node = dir.GetDirectoryChildType(m_vecItems->GetPath());
855 // nothing to do here
857 else if (m_vecItems->GetPath().Equals("sources://video/"))
859 // get the usual shares
860 CGUIDialogContextMenu::GetContextButtons("video", item, buttons);
861 // add scan button somewhere here
862 if (g_application.IsVideoScanning())
863 buttons.Add(CONTEXT_BUTTON_STOP_SCANNING, 13353); // Stop Scanning
864 if (!item->IsDVD() && item->GetPath() != "add" && !item->IsParentFolder() &&
865 (CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser))
867 CVideoDatabase database;
869 ADDON::ScraperPtr info = database.GetScraperForPath(item->GetPath());
871 if (!g_application.IsVideoScanning())
873 if (!item->IsLiveTV() && !item->IsPlugin() && !item->IsAddonsPath() && !URIUtils::IsUPnP(item->GetPath()))
875 if (info && info->Content() != CONTENT_NONE)
876 buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20442);
878 buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20333);
882 if (info && !g_application.IsVideoScanning())
883 buttons.Add(CONTEXT_BUTTON_SCAN, 13349);
888 // are we in the playlists location?
889 bool inPlaylists = m_vecItems->GetPath().Equals(CUtil::VideoPlaylistsLocation()) ||
890 m_vecItems->GetPath().Equals("special://videoplaylists/");
892 if (item->HasVideoInfoTag() && !item->GetVideoInfoTag()->m_artist.empty())
894 CMusicDatabase database;
896 if (database.GetArtistByName(StringUtils::Join(item->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator)) > -1)
897 buttons.Add(CONTEXT_BUTTON_GO_TO_ARTIST, 20396);
899 if (item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_strAlbum.size() > 0)
901 CMusicDatabase database;
903 if (database.GetAlbumByName(item->GetVideoInfoTag()->m_strAlbum) > -1)
904 buttons.Add(CONTEXT_BUTTON_GO_TO_ALBUM, 20397);
906 if (item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_strAlbum.size() > 0 &&
907 item->GetVideoInfoTag()->m_artist.size() > 0 &&
908 item->GetVideoInfoTag()->m_strTitle.size() > 0)
910 CMusicDatabase database;
912 if (database.GetSongByArtistAndAlbumAndTitle(StringUtils::Join(item->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator),
913 item->GetVideoInfoTag()->m_strAlbum,
914 item->GetVideoInfoTag()->m_strTitle) > -1)
916 buttons.Add(CONTEXT_BUTTON_PLAY_OTHER, 20398);
919 if (!item->IsParentFolder())
921 ADDON::ScraperPtr info;
922 VIDEO::SScanSettings settings;
923 GetScraperForItem(item.get(), info, settings);
925 if (info && info->Content() == CONTENT_TVSHOWS)
926 buttons.Add(CONTEXT_BUTTON_INFO, item->m_bIsFolder ? 20351 : 20352);
927 else if (info && info->Content() == CONTENT_MUSICVIDEOS)
928 buttons.Add(CONTEXT_BUTTON_INFO,20393);
929 else if (info && info->Content() == CONTENT_MOVIES)
930 buttons.Add(CONTEXT_BUTTON_INFO, 13346);
932 // can we update the database?
933 if (CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser)
935 if (node == NODE_TYPE_TITLE_TVSHOWS)
937 if (g_application.IsVideoScanning())
938 buttons.Add(CONTEXT_BUTTON_STOP_SCANNING, 13353);
940 buttons.Add(CONTEXT_BUTTON_SCAN, 13349);
942 if (!item->IsPlugin() && !item->IsScript() && !item->IsLiveTV() && !item->IsAddonsPath() &&
943 item->GetPath() != "sources://video/" && item->GetPath() != "special://videoplaylists/" &&
944 item->GetPath().Left(19) != "newsmartplaylist://" && item->GetPath().Left(14) != "newplaylist://" &&
945 item->GetPath().Left(9) != "newtag://")
947 if (item->m_bIsFolder)
949 // Have both options for folders since we don't know whether all childs are watched/unwatched
950 buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
951 buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103); //Mark as Watched
955 if (item->GetOverlayImage().Equals("OverlayWatched.png"))
956 buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
958 buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103); //Mark as Watched
961 if (!g_application.IsVideoScanning() && item->IsVideoDb() && item->HasVideoInfoTag() &&
962 (!item->m_bIsFolder || m_vecItems->GetContent().Equals("movies") || m_vecItems->GetContent().Equals("tvshows")))
964 buttons.Add(CONTEXT_BUTTON_EDIT, 16106);
967 if (node == NODE_TYPE_SEASONS && item->m_bIsFolder)
968 buttons.Add(CONTEXT_BUTTON_SET_SEASON_ART, 13511);
970 if (StringUtils::StartsWithNoCase(item->GetPath(), "videodb://movies/sets/") && item->GetPath().size() > 22 && item->m_bIsFolder) // sets
972 buttons.Add(CONTEXT_BUTTON_SET_MOVIESET_ART, 13511);
973 buttons.Add(CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS, 20465);
974 buttons.Add(CONTEXT_BUTTON_DELETE, 646);
977 if (m_vecItems->GetContent() == "tags" && item->m_bIsFolder) // tags
979 CVideoDbUrl videoUrl;
980 if (videoUrl.FromString(item->GetPath()))
982 std::string mediaType = videoUrl.GetItemType();
984 CStdString strLabelAdd; strLabelAdd.Format(g_localizeStrings.Get(20460), GetLocalizedType(videoUrl.GetItemType()).c_str());
985 CStdString strLabelRemove; strLabelRemove.Format(g_localizeStrings.Get(20461), GetLocalizedType(videoUrl.GetItemType()).c_str());
986 buttons.Add(CONTEXT_BUTTON_TAGS_ADD_ITEMS, strLabelAdd);
987 buttons.Add(CONTEXT_BUTTON_TAGS_REMOVE_ITEMS, strLabelRemove);
988 buttons.Add(CONTEXT_BUTTON_DELETE, 646);
992 if (node == NODE_TYPE_ACTOR && !dir.IsAllItem(item->GetPath()) && item->m_bIsFolder)
994 if (StringUtils::StartsWithNoCase(m_vecItems->GetPath(), "videodb://musicvideos")) // mvids
995 buttons.Add(CONTEXT_BUTTON_SET_ARTIST_THUMB, 13359);
997 buttons.Add(CONTEXT_BUTTON_SET_ACTOR_THUMB, 20403);
999 if (item->IsVideoDb() && item->HasVideoInfoTag() &&
1000 (!item->m_bIsFolder || node == NODE_TYPE_TITLE_TVSHOWS))
1001 buttons.Add(CONTEXT_BUTTON_DELETE, 646);
1004 if (!m_vecItems->IsVideoDb() && !m_vecItems->IsVirtualDirectoryRoot())
1005 { // non-video db items, file operations are allowed
1006 if ((CSettings::Get().GetBool("filelists.allowfiledeletion") &&
1007 CUtil::SupportsWriteFileOperations(item->GetPath())) ||
1008 (inPlaylists && !URIUtils::GetFileName(item->GetPath()).Equals("PartyMode-Video.xsp")
1009 && (item->IsPlayList() || item->IsSmartPlayList())))
1011 buttons.Add(CONTEXT_BUTTON_DELETE, 117);
1012 buttons.Add(CONTEXT_BUTTON_RENAME, 118);
1014 // add "Set/Change content" to folders
1015 if (item->m_bIsFolder && !item->IsVideoDb() && !item->IsPlayList() && !item->IsSmartPlayList() && !item->IsLibraryFolder() && !item->IsLiveTV() && !item->IsPlugin() && !item->IsAddonsPath() && !URIUtils::IsUPnP(item->GetPath()))
1017 if (!g_application.IsVideoScanning())
1019 if (info && info->Content() != CONTENT_NONE)
1021 buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20442);
1022 buttons.Add(CONTEXT_BUTTON_SCAN, 13349);
1025 buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20333);
1029 if (item->IsPlugin() || item->IsScript() || m_vecItems->IsPlugin())
1030 buttons.Add(CONTEXT_BUTTON_PLUGIN_SETTINGS, 1045);
1035 // predicate used by sorting and set_difference
1036 bool compFileItemsByDbId(const CFileItemPtr& lhs, const CFileItemPtr& rhs)
1038 return lhs->HasVideoInfoTag() && rhs->HasVideoInfoTag() && lhs->GetVideoInfoTag()->m_iDbId < rhs->GetVideoInfoTag()->m_iDbId;
1041 bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
1044 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
1045 item = m_vecItems->Get(itemNumber);
1046 if (CGUIDialogContextMenu::OnContextButton("video", item, button))
1048 //TODO should we search DB for entries from plugins?
1049 if (button == CONTEXT_BUTTON_REMOVE_SOURCE && !item->IsPlugin()
1050 && !item->IsLiveTV() &&!item->IsRSS() && !URIUtils::IsUPnP(item->GetPath()))
1052 OnUnAssignContent(item->GetPath(),20375,20340,20341);
1059 case CONTEXT_BUTTON_EDIT:
1061 CONTEXT_BUTTON ret = (CONTEXT_BUTTON)CGUIDialogVideoInfo::ManageVideoItem(item);
1064 if (ret == CONTEXT_BUTTON_MARK_WATCHED)
1065 m_viewControl.SetSelectedItem(itemNumber + 1);
1072 case CONTEXT_BUTTON_SET_SEASON_ART:
1073 case CONTEXT_BUTTON_SET_ACTOR_THUMB:
1074 case CONTEXT_BUTTON_SET_ARTIST_THUMB:
1075 case CONTEXT_BUTTON_SET_MOVIESET_ART:
1077 // Grab the thumbnails from the web
1078 CFileItemList items;
1079 CFileItemPtr noneitem(new CFileItem("thumb://None", false));
1080 CStdString currentThumb;
1082 CStdString artistPath;
1083 string artType = "thumb";
1084 if (button == CONTEXT_BUTTON_SET_ARTIST_THUMB)
1086 CMusicDatabase database;
1088 idArtist = database.GetArtistByName(m_vecItems->Get(itemNumber)->GetLabel());
1089 database.GetArtistPath(idArtist, artistPath);
1090 currentThumb = database.GetArtForItem(idArtist, "artist", "thumb");
1091 if (currentThumb.empty())
1092 currentThumb = m_database.GetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, artType);
1094 else if (button == CONTEXT_BUTTON_SET_ACTOR_THUMB)
1095 currentThumb = m_database.GetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, artType);
1098 map<string, string> currentArt;
1099 artType = CGUIDialogVideoInfo::ChooseArtType(*m_vecItems->Get(itemNumber), currentArt);
1100 if (artType.empty())
1103 if (artType == "fanart")
1105 OnChooseFanart(*m_vecItems->Get(itemNumber));
1109 if (currentArt.find(artType) != currentArt.end())
1110 currentThumb = currentArt[artType];
1111 else if ((artType == "poster" || artType == "banner") && currentArt.find("thumb") != currentArt.end())
1112 currentThumb = currentArt["thumb"];
1114 if (!currentThumb.IsEmpty())
1116 CFileItemPtr item(new CFileItem("thumb://Current", false));
1117 item->SetArt("thumb", currentThumb);
1118 item->SetLabel(g_localizeStrings.Get(13512));
1121 noneitem->SetIconImage("DefaultFolder.png");
1122 noneitem->SetLabel(g_localizeStrings.Get(13515));
1124 vector<CStdString> thumbs;
1125 if (button != CONTEXT_BUTTON_SET_ARTIST_THUMB)
1128 if (button == CONTEXT_BUTTON_SET_SEASON_ART)
1129 m_database.GetTvShowInfo("",tag,m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iIdShow);
1131 tag = *m_vecItems->Get(itemNumber)->GetVideoInfoTag();
1132 if (button == CONTEXT_BUTTON_SET_SEASON_ART)
1133 tag.m_strPictureURL.GetThumbURLs(thumbs, artType, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iSeason);
1135 tag.m_strPictureURL.GetThumbURLs(thumbs, artType);
1137 for (unsigned int i = 0; i < thumbs.size(); i++)
1139 CStdString strItemPath;
1140 strItemPath.Format("thumb://Remote%i",i);
1141 CFileItemPtr item(new CFileItem(strItemPath, false));
1142 item->SetArt("thumb", thumbs[i]);
1143 item->SetIconImage("DefaultPicture.png");
1144 item->SetLabel(g_localizeStrings.Get(13513));
1147 // TODO: Do we need to clear the cached image?
1148 // CTextureCache::Get().ClearCachedImage(thumbs[i]);
1153 if (button == CONTEXT_BUTTON_SET_ARTIST_THUMB)
1155 CStdString strThumb = URIUtils::AddFileToFolder(artistPath, "folder.jpg");
1156 if (XFILE::CFile::Exists(strThumb))
1158 CFileItemPtr pItem(new CFileItem(strThumb,false));
1159 pItem->SetLabel(g_localizeStrings.Get(13514));
1160 pItem->SetArt("thumb", strThumb);
1165 noneitem->SetIconImage("DefaultArtist.png");
1168 if (button == CONTEXT_BUTTON_SET_ACTOR_THUMB)
1170 CStdString picturePath;
1171 CStdString strThumb = URIUtils::AddFileToFolder(picturePath, "folder.jpg");
1172 if (XFILE::CFile::Exists(strThumb))
1174 CFileItemPtr pItem(new CFileItem(strThumb,false));
1175 pItem->SetLabel(g_localizeStrings.Get(13514));
1176 pItem->SetArt("thumb", strThumb);
1181 noneitem->SetIconImage("DefaultActor.png");
1184 if (button == CONTEXT_BUTTON_SET_MOVIESET_ART)
1185 noneitem->SetIconImage("DefaultVideo.png");
1188 items.Add(noneitem);
1190 VECSOURCES sources=*CMediaSourceSettings::Get().GetSources("video");
1191 g_mediaManager.GetLocalDrives(sources);
1193 CGUIDialogVideoInfo::AddItemPathToFileBrowserSources(sources, *item);
1194 if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources,
1195 g_localizeStrings.Get(13511), result))
1197 return false; // user cancelled
1200 if (result == "thumb://Current")
1201 result = currentThumb; // user chose the one they have
1203 // delete the thumbnail if that's what the user wants, else overwrite with the
1205 if (result.Left(14) == "thumb://Remote")
1207 int number = atoi(result.Mid(14));
1208 result = thumbs[number];
1210 else if (result == "thumb://None")
1212 if (button == CONTEXT_BUTTON_SET_MOVIESET_ART ||
1213 button == CONTEXT_BUTTON_SET_ACTOR_THUMB ||
1214 button == CONTEXT_BUTTON_SET_SEASON_ART ||
1215 (button == CONTEXT_BUTTON_SET_ARTIST_THUMB && idArtist < 0))
1216 m_database.SetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, artType, result);
1221 db.SetArtForItem(idArtist, "artist", artType, result);
1224 CUtil::DeleteVideoDatabaseDirectoryCache();
1225 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
1226 g_windowManager.SendMessage(msg);
1231 case CONTEXT_BUTTON_TAGS_ADD_ITEMS:
1233 CVideoDbUrl videoUrl;
1234 if (!videoUrl.FromString(item->GetPath()))
1237 std::string mediaType = videoUrl.GetItemType();
1238 mediaType = mediaType.substr(0, mediaType.length() - 1);
1240 CFileItemList items;
1241 CStdString localizedType = GetLocalizedType(mediaType);
1242 CStdString strLabel; strLabel.Format(g_localizeStrings.Get(20464), localizedType.c_str());
1243 if (!GetItemsForTag(strLabel, mediaType, items, item->GetVideoInfoTag()->m_iDbId))
1246 CVideoDatabase videodb;
1247 if (!videodb.Open())
1250 for (int index = 0; index < items.Size(); index++)
1252 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1255 videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_iDbId, mediaType);
1258 // we need to clear any cached version of this tag's listing
1259 items.SetPath(item->GetPath());
1260 items.RemoveDiscCache(GetID());
1263 case CONTEXT_BUTTON_TAGS_REMOVE_ITEMS:
1265 CVideoDbUrl videoUrl;
1266 if (!videoUrl.FromString(item->GetPath()))
1269 std::string mediaType = videoUrl.GetItemType();
1270 mediaType = mediaType.substr(0, mediaType.length() - 1);
1272 CFileItemList items;
1273 CStdString localizedType = GetLocalizedType(mediaType);
1274 CStdString strLabel; strLabel.Format(g_localizeStrings.Get(20464), localizedType.c_str());
1275 if (!GetItemsForTag(strLabel, mediaType, items, item->GetVideoInfoTag()->m_iDbId, false))
1278 CVideoDatabase videodb;
1279 if (!videodb.Open())
1282 for (int index = 0; index < items.Size(); index++)
1284 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1287 videodb.RemoveTagFromItem(items[index]->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_iDbId, mediaType);
1290 // we need to clear any cached version of this tag's listing
1291 items.SetPath(item->GetPath());
1292 items.RemoveDiscCache(GetID());
1295 case CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS:
1297 CFileItemList originalItems;
1298 CFileItemList selectedItems;
1300 if (!CGUIDialogVideoInfo::GetMoviesForSet(item.get(), originalItems, selectedItems) || selectedItems.Size() == 0) // need at least one item selected
1302 VECFILEITEMS original = originalItems.GetList();
1303 std::sort(original.begin(), original.end(), compFileItemsByDbId);
1304 VECFILEITEMS selected = selectedItems.GetList();
1305 std::sort(selected.begin(), selected.end(), compFileItemsByDbId);
1307 bool refreshNeeded = false;
1308 // update the "added" items
1309 VECFILEITEMS addedItems;
1310 set_difference(selected.begin(),selected.end(), original.begin(),original.end(), std::back_inserter(addedItems), compFileItemsByDbId);
1311 for (VECFILEITEMS::iterator it = addedItems.begin(); it != addedItems.end(); ++it)
1313 if (CGUIDialogVideoInfo::SetMovieSet(it->get(), item.get()))
1314 refreshNeeded = true;
1316 // update the "deleted" items
1317 CFileItemPtr clearItem(new CFileItem());
1318 clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
1319 VECFILEITEMS deletedItems;
1320 set_difference(original.begin(),original.end(), selected.begin(),selected.end(), std::back_inserter(deletedItems), compFileItemsByDbId);
1321 for (VECFILEITEMS::iterator it = deletedItems.begin(); it != deletedItems.end(); ++it)
1323 if (CGUIDialogVideoInfo::SetMovieSet(it->get(), clearItem.get()))
1324 refreshNeeded = true;
1327 // we need to clear any cached version of this tag's listing
1332 case CONTEXT_BUTTON_GO_TO_ARTIST:
1335 CMusicDatabase database;
1337 strPath.Format("musicdb://artists/%ld/",database.GetArtistByName(StringUtils::Join(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator)));
1338 g_windowManager.ActivateWindow(WINDOW_MUSIC_NAV,strPath);
1341 case CONTEXT_BUTTON_GO_TO_ALBUM:
1344 CMusicDatabase database;
1346 strPath.Format("musicdb://albums/%ld/",database.GetAlbumByName(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strAlbum));
1347 g_windowManager.ActivateWindow(WINDOW_MUSIC_NAV,strPath);
1350 case CONTEXT_BUTTON_PLAY_OTHER:
1352 CMusicDatabase database;
1355 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,
1356 m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strTitle),
1359 CApplicationMessenger::Get().PlayFile(song);
1368 return CGUIWindowVideoBase::OnContextButton(itemNumber, button);
1371 void CGUIWindowVideoNav::OnChooseFanart(const CFileItem &videoItem)
1373 if (!videoItem.HasVideoInfoTag())
1376 CFileItem item(videoItem);
1378 CFileItemList items;
1380 CVideoThumbLoader loader;
1381 loader.LoadItem(&item);
1383 if (item.HasArt("fanart"))
1385 CFileItemPtr itemCurrent(new CFileItem("fanart://Current",false));
1386 itemCurrent->SetArt("thumb", item.GetArt("fanart"));
1387 itemCurrent->SetLabel(g_localizeStrings.Get(20440));
1388 items.Add(itemCurrent);
1391 // add the none option
1393 CFileItemPtr itemNone(new CFileItem("fanart://None", false));
1394 itemNone->SetIconImage("DefaultVideo.png");
1395 itemNone->SetLabel(g_localizeStrings.Get(20439));
1396 items.Add(itemNone);
1400 VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
1401 g_mediaManager.GetLocalDrives(sources);
1402 CGUIDialogVideoInfo::AddItemPathToFileBrowserSources(sources, item);
1404 if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) || result.Equals("fanart://Current"))
1407 if (result.Equals("fanart://None") || !CFile::Exists(result))
1409 if (!result.IsEmpty() && flip)
1410 result = CTextureCache::GetWrappedImageURL(result, "", "flipped");
1416 db.SetArtForItem(item.GetVideoInfoTag()->m_iDbId, item.GetVideoInfoTag()->m_type, "fanart", result);
1420 // clear view cache and reload images
1421 CUtil::DeleteVideoDatabaseDirectoryCache();
1426 bool CGUIWindowVideoNav::OnClick(int iItem)
1428 CFileItemPtr item = m_vecItems->Get(iItem);
1429 if (!item->m_bIsFolder && item->IsVideoDb() && !item->Exists())
1431 CLog::Log(LOGDEBUG, "%s called on '%s' but file doesn't exist", __FUNCTION__, item->GetPath().c_str());
1432 if (!DeleteItem(item.get(), true))
1437 m_viewControl.SetSelectedItem(iItem);
1440 else if (StringUtils::StartsWithNoCase(item->GetPath(), "newtag://"))
1442 // dont allow update while scanning
1443 if (g_application.IsVideoScanning())
1445 CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1451 if (!CGUIKeyboardFactory::ShowAndGetInput(strTag, g_localizeStrings.Get(20462), false))
1454 CVideoDatabase videodb;
1455 if (!videodb.Open())
1458 // get the media type and convert from plural to singular (by removing the trailing "s")
1459 CStdString mediaType = item->GetPath().Mid(9);
1460 mediaType = mediaType.Left(mediaType.size() - 1);
1461 CStdString localizedType = GetLocalizedType(mediaType);
1462 if (localizedType.empty())
1465 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())
1467 CStdString strError; strError.Format(g_localizeStrings.Get(20463), strTag.c_str());
1468 CGUIDialogOK::ShowAndGetInput(20462, "", strError, "");
1472 int idTag = videodb.AddTag(strTag);
1473 CFileItemList items;
1474 CStdString strLabel; strLabel.Format(g_localizeStrings.Get(20464), localizedType.c_str());
1475 if (GetItemsForTag(strLabel, mediaType, items, idTag))
1477 for (int index = 0; index < items.Size(); index++)
1479 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1482 videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, idTag, mediaType);
1490 return CGUIWindowVideoBase::OnClick(iItem);
1493 CStdString CGUIWindowVideoNav::GetStartFolder(const CStdString &dir)
1495 if (dir.Equals("MovieGenres"))
1496 return "videodb://movies/genres/";
1497 else if (dir.Equals("MovieTitles"))
1498 return "videodb://movies/titles/";
1499 else if (dir.Equals("MovieYears"))
1500 return "videodb://movies/years/";
1501 else if (dir.Equals("MovieActors"))
1502 return "videodb://movies/actors/";
1503 else if (dir.Equals("MovieDirectors"))
1504 return "videodb://movies/directors/";
1505 else if (dir.Equals("MovieStudios"))
1506 return "videodb://movies/studios/";
1507 else if (dir.Equals("MovieSets"))
1508 return "videodb://movies/sets/";
1509 else if (dir.Equals("MovieCountries"))
1510 return "videodb://movies/countries/";
1511 else if (dir.Equals("MovieTags"))
1512 return "videodb://movies/tags/";
1513 else if (dir.Equals("Movies"))
1514 return "videodb://movies/";
1515 else if (dir.Equals("TvShowGenres"))
1516 return "videodb://tvshows/genres/";
1517 else if (dir.Equals("TvShowTitles"))
1518 return "videodb://tvshows/titles/";
1519 else if (dir.Equals("TvShowYears"))
1520 return "videodb://tvshows/years/";
1521 else if (dir.Equals("TvShowActors"))
1522 return "videodb://tvshows/actors/";
1523 else if (dir.Equals("TvShowStudios"))
1524 return "videodb://tvshows/studios/";
1525 else if (dir.Equals("TvShowTags"))
1526 return "videodb://tvshows/tags/";
1527 else if (dir.Equals("TvShows"))
1528 return "videodb://tvshows/";
1529 else if (dir.Equals("MusicVideoGenres"))
1530 return "videodb://musicvideos/genres/";
1531 else if (dir.Equals("MusicVideoTitles"))
1532 return "videodb://musicvideos/titles/";
1533 else if (dir.Equals("MusicVideoYears"))
1534 return "videodb://musicvideos/years/";
1535 else if (dir.Equals("MusicVideoArtists"))
1536 return "videodb://musicvideos/artists/";
1537 else if (dir.Equals("MusicVideoAlbums"))
1538 return "videodb://musicvideos/albums/";
1539 else if (dir.Equals("MusicVideoDirectors"))
1540 return "videodb://musicvideos/directors/";
1541 else if (dir.Equals("MusicVideoStudios"))
1542 return "videodb://musicvideos/studios/";
1543 else if (dir.Equals("MusicVideoTags"))
1544 return "videodb://musicvideos/tags/";
1545 else if (dir.Equals("MusicVideos"))
1546 return "videodb://musicvideos/";
1547 else if (dir.Equals("RecentlyAddedMovies"))
1548 return "videodb://recentlyaddedmovies/";
1549 else if (dir.Equals("RecentlyAddedEpisodes"))
1550 return "videodb://recentlyaddedepisodes/";
1551 else if (dir.Equals("RecentlyAddedMusicVideos"))
1552 return "videodb://recentlyaddedmusicvideos/";
1553 else if (dir.Equals("Files"))
1554 return "sources://video/";
1555 return CGUIWindowVideoBase::GetStartFolder(dir);
1558 bool CGUIWindowVideoNav::ApplyWatchedFilter(CFileItemList &items)
1560 bool listchanged = false;
1561 CVideoDatabaseDirectory dir;
1562 NODE_TYPE node = dir.GetDirectoryChildType(items.GetPath());
1564 // now filter watched items as necessary
1565 bool filterWatched=false;
1566 if (node == NODE_TYPE_EPISODES
1567 || node == NODE_TYPE_SEASONS
1568 || node == NODE_TYPE_SETS
1569 || node == NODE_TYPE_TAGS
1570 || node == NODE_TYPE_TITLE_MOVIES
1571 || node == NODE_TYPE_TITLE_TVSHOWS
1572 || node == NODE_TYPE_TITLE_MUSICVIDEOS
1573 || node == NODE_TYPE_RECENTLY_ADDED_EPISODES
1574 || node == NODE_TYPE_RECENTLY_ADDED_MOVIES
1575 || node == NODE_TYPE_RECENTLY_ADDED_MUSICVIDEOS)
1576 filterWatched = true;
1577 if (!items.IsVideoDb())
1578 filterWatched = true;
1579 if (items.GetContent() == "tvshows" &&
1580 (items.IsSmartPlayList() || items.IsLibraryFolder()))
1581 node = NODE_TYPE_TITLE_TVSHOWS; // so that the check below works
1583 int watchMode = CMediaSettings::Get().GetWatchedMode(m_vecItems->GetContent());
1585 for (int i = 0; i < items.Size(); i++)
1587 CFileItemPtr item = items.Get(i);
1589 if(item->HasVideoInfoTag() && (node == NODE_TYPE_TITLE_TVSHOWS || node == NODE_TYPE_SEASONS))
1591 if (watchMode == WatchedModeUnwatched)
1592 item->GetVideoInfoTag()->m_iEpisode = (int)item->GetProperty("unwatchedepisodes").asInteger();
1593 if (watchMode == WatchedModeWatched)
1594 item->GetVideoInfoTag()->m_iEpisode = (int)item->GetProperty("watchedepisodes").asInteger();
1595 if (watchMode == WatchedModeAll)
1596 item->GetVideoInfoTag()->m_iEpisode = (int)item->GetProperty("totalepisodes").asInteger();
1597 item->SetProperty("numepisodes", item->GetVideoInfoTag()->m_iEpisode);
1603 if((watchMode==WatchedModeWatched && item->GetVideoInfoTag()->m_playCount== 0)
1604 || (watchMode==WatchedModeUnwatched && item->GetVideoInfoTag()->m_playCount > 0))
1613 if(node == NODE_TYPE_TITLE_TVSHOWS || node == NODE_TYPE_SEASONS)
1615 // the watched filter may change the "numepisodes" property which is reflected in the TV_SHOWS and SEASONS nodes
1616 // therefore, the items labels have to be refreshed, and possibly the list needs resorting as well.
1617 items.ClearSortState(); // this is needed to force resorting even if sort method did not change
1618 FormatAndSort(items);
1624 bool CGUIWindowVideoNav::GetItemsForTag(const CStdString &strHeading, const std::string &type, CFileItemList &items, int idTag /* = -1 */, bool showAll /* = true */)
1626 CVideoDatabase videodb;
1627 if (!videodb.Open())
1630 MediaType mediaType = MediaTypeNone;
1631 std::string baseDir = "videodb://";
1632 std::string idColumn;
1633 if (type.compare("movie") == 0)
1635 mediaType = MediaTypeMovie;
1636 baseDir += "movies";
1637 idColumn = "idMovie";
1639 else if (type.compare("tvshow") == 0)
1641 mediaType = MediaTypeTvShow;
1642 baseDir += "tvshows";
1643 idColumn = "idShow";
1645 else if (type.compare("musicvideo") == 0)
1647 mediaType = MediaTypeMusicVideo;
1648 baseDir += "musicvideos";
1649 idColumn = "idMVideo";
1652 baseDir += "/titles/";
1653 CVideoDbUrl videoUrl;
1654 if (!videoUrl.FromString(baseDir))
1657 CVideoDatabase::Filter filter;
1661 videoUrl.AddOption("tagid", idTag);
1663 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());
1666 CFileItemList listItems;
1667 if (!videodb.GetSortedVideos(mediaType, videoUrl.ToString(), SortDescription(), listItems, filter) || listItems.Size() <= 0)
1670 CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1674 listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1677 dialog->SetMultiSelection(true);
1678 dialog->SetHeading(strHeading);
1679 dialog->SetItems(&listItems);
1680 dialog->EnableButton(true, 186);
1683 items.Copy(dialog->GetSelectedItems());
1684 return items.Size() > 0;
1687 CStdString CGUIWindowVideoNav::GetLocalizedType(const std::string &strType)
1689 if (strType == "movie" || strType == "movies")
1690 return g_localizeStrings.Get(20342);
1691 else if (strType == "tvshow" || strType == "tvshows")
1692 return g_localizeStrings.Get(20343);
1693 else if (strType == "episode" || strType == "episodes")
1694 return g_localizeStrings.Get(20359);
1695 else if (strType == "musicvideo" || strType == "musicvideos")
1696 return g_localizeStrings.Get(20391);