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/"))
507 CStdString items = StringUtils::Format("%i %s", iItems, g_localizeStrings.Get(127).c_str());
508 SET_CONTROL_LABEL(CONTROL_LABELFILES, items);
510 // set the filter label
514 if (m_vecItems->GetPath().Equals("special://videoplaylists/"))
515 strLabel = g_localizeStrings.Get(136);
517 else if (m_vecItems->IsPlayList())
519 // get playlist name from path
521 URIUtils::Split(m_vecItems->GetPath(), strDummy, strLabel);
523 else if (m_vecItems->GetPath().Equals("sources://video/"))
524 strLabel = g_localizeStrings.Get(744);
525 // everything else is from a videodb:// path
526 else if (m_vecItems->IsVideoDb())
528 CVideoDatabaseDirectory dir;
529 dir.GetLabel(m_vecItems->GetPath(), strLabel);
532 strLabel = URIUtils::GetFileName(m_vecItems->GetPath());
534 SET_CONTROL_LABEL(CONTROL_FILTER, strLabel);
536 int watchMode = CMediaSettings::Get().GetWatchedMode(m_vecItems->GetContent());
537 SET_CONTROL_LABEL(CONTROL_BTNSHOWMODE, g_localizeStrings.Get(16100 + watchMode));
539 SET_CONTROL_SELECTED(GetID(), CONTROL_BTNSHOWALL, watchMode != WatchedModeAll);
541 SET_CONTROL_SELECTED(GetID(),CONTROL_BTNPARTYMODE, g_partyModeManager.IsEnabled());
543 CONTROL_ENABLE_ON_CONDITION(CONTROL_UPDATE_LIBRARY, !m_vecItems->IsAddonsPath() && !m_vecItems->IsPlugin() && !m_vecItems->IsScript());
546 bool CGUIWindowVideoNav::GetFilteredItems(const CStdString &filter, CFileItemList &items)
548 bool listchanged = CGUIMediaWindow::GetFilteredItems(filter, items);
549 listchanged |= ApplyWatchedFilter(items);
554 /// \brief Search for names, genres, artists, directors, and plots with search string \e strSearch in the
555 /// \brief video databases and return the found \e items
556 /// \param strSearch The search string
557 /// \param items Items Found
558 void CGUIWindowVideoNav::DoSearch(const CStdString& strSearch, CFileItemList& items)
560 CFileItemList tempItems;
561 CStdString strGenre = g_localizeStrings.Get(515); // Genre
562 CStdString strActor = g_localizeStrings.Get(20337); // Actor
563 CStdString strDirector = g_localizeStrings.Get(20339); // Director
564 CStdString strMovie = g_localizeStrings.Get(20338); // Movie
567 m_database.GetMoviesByName(strSearch, tempItems);
568 AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20338) + "] ", items);
570 m_database.GetEpisodesByName(strSearch, tempItems);
571 AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20359) + "] ", items);
573 m_database.GetTvShowsByName(strSearch, tempItems);
574 AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20364) + "] ", items);
576 m_database.GetMusicVideosByName(strSearch, tempItems);
577 AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20391) + "] ", items);
579 m_database.GetMusicVideosByAlbum(strSearch, tempItems);
580 AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(558) + "] ", items);
582 // get matching genres
583 m_database.GetMovieGenresByName(strSearch, tempItems);
584 AppendAndClearSearchItems(tempItems, "[" + strGenre + " - " + g_localizeStrings.Get(20342) + "] ", items);
586 m_database.GetTvShowGenresByName(strSearch, tempItems);
587 AppendAndClearSearchItems(tempItems, "[" + strGenre + " - " + g_localizeStrings.Get(20343) + "] ", items);
589 m_database.GetMusicVideoGenresByName(strSearch, tempItems);
590 AppendAndClearSearchItems(tempItems, "[" + strGenre + " - " + g_localizeStrings.Get(20389) + "] ", items);
593 m_database.GetMovieActorsByName(strSearch, tempItems);
594 AppendAndClearSearchItems(tempItems, "[" + strActor + " - " + g_localizeStrings.Get(20342) + "] ", items);
596 m_database.GetTvShowsActorsByName(strSearch, tempItems);
597 AppendAndClearSearchItems(tempItems, "[" + strActor + " - " + g_localizeStrings.Get(20343) + "] ", items);
599 m_database.GetMusicVideoArtistsByName(strSearch, tempItems);
600 AppendAndClearSearchItems(tempItems, "[" + strActor + " - " + g_localizeStrings.Get(20389) + "] ", items);
603 m_database.GetMovieDirectorsByName(strSearch, tempItems);
604 AppendAndClearSearchItems(tempItems, "[" + strDirector + " - " + g_localizeStrings.Get(20342) + "] ", items);
606 m_database.GetTvShowsDirectorsByName(strSearch, tempItems);
607 AppendAndClearSearchItems(tempItems, "[" + strDirector + " - " + g_localizeStrings.Get(20343) + "] ", items);
609 m_database.GetMusicVideoDirectorsByName(strSearch, tempItems);
610 AppendAndClearSearchItems(tempItems, "[" + strDirector + " - " + g_localizeStrings.Get(20389) + "] ", items);
613 m_database.GetEpisodesByPlot(strSearch, tempItems);
614 AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20365) + "] ", items);
616 m_database.GetMoviesByPlot(strSearch, tempItems);
617 AppendAndClearSearchItems(tempItems, "[" + strMovie + " " + g_localizeStrings.Get(207) + "] ", items);
620 void CGUIWindowVideoNav::PlayItem(int iItem)
622 // unlike additemtoplaylist, we need to check the items here
623 // before calling it since the current playlist will be stopped
626 // root is not allowed
627 if (m_vecItems->IsVirtualDirectoryRoot())
630 CGUIWindowVideoBase::PlayItem(iItem);
633 void CGUIWindowVideoNav::OnInfo(CFileItem* pItem, ADDON::ScraperPtr& scraper)
635 m_database.Open(); // since we can be called from the music library without being inited
636 if (pItem->IsVideoDb())
637 scraper = m_database.GetScraperForPath(pItem->GetVideoInfoTag()->m_strPath);
640 CStdString strPath,strFile;
641 URIUtils::Split(pItem->GetPath(),strPath,strFile);
642 scraper = m_database.GetScraperForPath(strPath);
645 CGUIWindowVideoBase::OnInfo(pItem,scraper);
648 bool CGUIWindowVideoNav::CanDelete(const CStdString& strPath)
651 CVideoDatabaseDirectory::GetQueryParams(strPath,params);
653 if (params.GetMovieId() != -1 ||
654 params.GetEpisodeId() != -1 ||
655 params.GetMVideoId() != -1 ||
656 (params.GetTvShowId() != -1 && params.GetSeason() <= -1
657 && !CVideoDatabaseDirectory::IsAllItem(strPath)))
663 void CGUIWindowVideoNav::OnDeleteItem(CFileItemPtr pItem)
665 if (m_vecItems->IsParentFolder())
668 if (!m_vecItems->IsVideoDb() && !pItem->IsVideoDb())
670 if (!pItem->GetPath().Equals("newsmartplaylist://video") &&
671 !pItem->GetPath().Equals("special://videoplaylists/") &&
672 !pItem->GetPath().Equals("sources://video/") &&
673 !StringUtils::StartsWithNoCase(pItem->GetPath(), "newtag://"))
674 CGUIWindowVideoBase::OnDeleteItem(pItem);
676 else if (StringUtils::StartsWithNoCase(pItem->GetPath(), "videodb://movies/sets/") &&
677 pItem->GetPath().size() > 22 && pItem->m_bIsFolder)
679 CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
680 pDialog->SetHeading(432);
681 CStdString strLabel = StringUtils::Format(g_localizeStrings.Get(433),pItem->GetLabel().c_str());
682 pDialog->SetLine(1, strLabel);
683 pDialog->SetLine(2, "");;
685 if (pDialog->IsConfirmed())
688 CDirectory::GetDirectory(pItem->GetPath(),items,"",DIR_FLAG_NO_FILE_DIRS);
689 for (int i=0;i<items.Size();++i)
690 OnDeleteItem(items[i]);
692 CVideoDatabaseDirectory dir;
694 dir.GetQueryParams(pItem->GetPath(),params);
695 m_database.DeleteSet(params.GetSetId());
698 else if (m_vecItems->GetContent() == "tags" && pItem->m_bIsFolder)
700 CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
701 pDialog->SetHeading(432);
702 CStdString strLabel = StringUtils::Format(g_localizeStrings.Get(433), pItem->GetLabel().c_str());
703 pDialog->SetLine(1, strLabel);
704 pDialog->SetLine(2, "");
706 if (pDialog->IsConfirmed())
708 CVideoDatabaseDirectory dir;
710 dir.GetQueryParams(pItem->GetPath(), params);
711 m_database.DeleteTag(params.GetTagId(), (VIDEODB_CONTENT_TYPE)params.GetContentType());
714 else if (m_vecItems->GetPath().Equals(CUtil::VideoPlaylistsLocation()) ||
715 m_vecItems->GetPath().Equals("special://videoplaylists/"))
717 pItem->m_bIsFolder = false;
718 CFileUtils::DeleteItem(pItem);
722 if (!DeleteItem(pItem.get()))
725 CStdString strDeletePath;
726 if (pItem->m_bIsFolder)
727 strDeletePath=pItem->GetVideoInfoTag()->m_strPath;
729 strDeletePath=pItem->GetVideoInfoTag()->m_strFileNameAndPath;
731 if (URIUtils::GetFileName(strDeletePath).Equals("VIDEO_TS.IFO"))
733 strDeletePath = URIUtils::GetDirectory(strDeletePath);
734 if (StringUtils::EndsWithNoCase(strDeletePath, "video_ts/"))
736 URIUtils::RemoveSlashAtEnd(strDeletePath);
737 strDeletePath = URIUtils::GetDirectory(strDeletePath);
740 if (URIUtils::HasSlashAtEnd(strDeletePath))
741 pItem->m_bIsFolder=true;
743 if (CSettings::Get().GetBool("filelists.allowfiledeletion") &&
744 CUtil::SupportsWriteFileOperations(strDeletePath))
746 pItem->SetPath(strDeletePath);
747 CGUIWindowVideoBase::OnDeleteItem(pItem);
751 CUtil::DeleteVideoDatabaseDirectoryCache();
754 bool CGUIWindowVideoNav::DeleteItem(CFileItem* pItem, bool bUnavailable /* = false */)
756 if (!pItem->HasVideoInfoTag() || !CanDelete(pItem->GetPath()))
759 VIDEODB_CONTENT_TYPE iType=VIDEODB_CONTENT_MOVIES;
760 if (pItem->HasVideoInfoTag() && !pItem->GetVideoInfoTag()->m_strShowTitle.IsEmpty())
761 iType = VIDEODB_CONTENT_TVSHOWS;
762 if (pItem->HasVideoInfoTag() && pItem->GetVideoInfoTag()->m_iSeason > -1 && !pItem->m_bIsFolder)
763 iType = VIDEODB_CONTENT_EPISODES;
764 if (pItem->HasVideoInfoTag() && !pItem->GetVideoInfoTag()->m_artist.empty())
765 iType = VIDEODB_CONTENT_MUSICVIDEOS;
767 // dont allow update while scanning
768 if (g_application.IsVideoScanning())
770 CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
775 CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
778 if (iType == VIDEODB_CONTENT_MOVIES)
779 pDialog->SetHeading(432);
780 if (iType == VIDEODB_CONTENT_EPISODES)
781 pDialog->SetHeading(20362);
782 if (iType == VIDEODB_CONTENT_TVSHOWS)
783 pDialog->SetHeading(20363);
784 if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
785 pDialog->SetHeading(20392);
789 pDialog->SetLine(0, g_localizeStrings.Get(662));
790 pDialog->SetLine(1, g_localizeStrings.Get(663));
791 pDialog->SetLine(2, "");;
796 CStdString strLine = StringUtils::Format(g_localizeStrings.Get(433), pItem->GetLabel().c_str());
797 pDialog->SetLine(0, strLine);
798 pDialog->SetLine(1, "");
799 pDialog->SetLine(2, "");;
803 if (!pDialog->IsConfirmed())
807 CVideoDatabase database;
810 database.GetFilePathById(pItem->GetVideoInfoTag()->m_iDbId, path, iType);
813 if (iType == VIDEODB_CONTENT_MOVIES)
814 database.DeleteMovie(path);
815 if (iType == VIDEODB_CONTENT_EPISODES)
816 database.DeleteEpisode(path, pItem->GetVideoInfoTag()->m_iDbId);
817 if (iType == VIDEODB_CONTENT_TVSHOWS)
818 database.DeleteTvShow(path);
819 if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
820 database.DeleteMusicVideo(path);
822 if (iType == VIDEODB_CONTENT_TVSHOWS)
823 database.SetPathHash(path,"");
825 database.SetPathHash(URIUtils::GetDirectory(path), "");
830 void CGUIWindowVideoNav::GetContextButtons(int itemNumber, CContextButtons &buttons)
833 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
834 item = m_vecItems->Get(itemNumber);
836 CGUIWindowVideoBase::GetContextButtons(itemNumber, buttons);
838 if (item && item->GetProperty("pluginreplacecontextitems").asBoolean())
841 CVideoDatabaseDirectory dir;
842 NODE_TYPE node = dir.GetDirectoryChildType(m_vecItems->GetPath());
846 // nothing to do here
848 else if (m_vecItems->GetPath().Equals("sources://video/"))
850 // get the usual shares
851 CGUIDialogContextMenu::GetContextButtons("video", item, buttons);
852 // add scan button somewhere here
853 if (g_application.IsVideoScanning())
854 buttons.Add(CONTEXT_BUTTON_STOP_SCANNING, 13353); // Stop Scanning
855 if (!item->IsDVD() && item->GetPath() != "add" && !item->IsParentFolder() &&
856 (CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser))
858 CVideoDatabase database;
860 ADDON::ScraperPtr info = database.GetScraperForPath(item->GetPath());
862 if (!g_application.IsVideoScanning())
864 if (!item->IsLiveTV() && !item->IsPlugin() && !item->IsAddonsPath() && !URIUtils::IsUPnP(item->GetPath()))
866 if (info && info->Content() != CONTENT_NONE)
867 buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20442);
869 buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20333);
873 if (info && !g_application.IsVideoScanning())
874 buttons.Add(CONTEXT_BUTTON_SCAN, 13349);
879 // are we in the playlists location?
880 bool inPlaylists = m_vecItems->GetPath().Equals(CUtil::VideoPlaylistsLocation()) ||
881 m_vecItems->GetPath().Equals("special://videoplaylists/");
883 if (item->HasVideoInfoTag() && !item->GetVideoInfoTag()->m_artist.empty())
885 CMusicDatabase database;
887 if (database.GetArtistByName(StringUtils::Join(item->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator)) > -1)
888 buttons.Add(CONTEXT_BUTTON_GO_TO_ARTIST, 20396);
890 if (item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_strAlbum.size() > 0)
892 CMusicDatabase database;
894 if (database.GetAlbumByName(item->GetVideoInfoTag()->m_strAlbum) > -1)
895 buttons.Add(CONTEXT_BUTTON_GO_TO_ALBUM, 20397);
897 if (item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_strAlbum.size() > 0 &&
898 item->GetVideoInfoTag()->m_artist.size() > 0 &&
899 item->GetVideoInfoTag()->m_strTitle.size() > 0)
901 CMusicDatabase database;
903 if (database.GetSongByArtistAndAlbumAndTitle(StringUtils::Join(item->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator),
904 item->GetVideoInfoTag()->m_strAlbum,
905 item->GetVideoInfoTag()->m_strTitle) > -1)
907 buttons.Add(CONTEXT_BUTTON_PLAY_OTHER, 20398);
910 if (!item->IsParentFolder())
912 ADDON::ScraperPtr info;
913 VIDEO::SScanSettings settings;
914 GetScraperForItem(item.get(), info, settings);
916 if (info && info->Content() == CONTENT_TVSHOWS)
917 buttons.Add(CONTEXT_BUTTON_INFO, item->m_bIsFolder ? 20351 : 20352);
918 else if (info && info->Content() == CONTENT_MUSICVIDEOS)
919 buttons.Add(CONTEXT_BUTTON_INFO,20393);
920 else if (info && info->Content() == CONTENT_MOVIES)
921 buttons.Add(CONTEXT_BUTTON_INFO, 13346);
923 // can we update the database?
924 if (CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser)
926 if (node == NODE_TYPE_TITLE_TVSHOWS)
928 if (g_application.IsVideoScanning())
929 buttons.Add(CONTEXT_BUTTON_STOP_SCANNING, 13353);
931 buttons.Add(CONTEXT_BUTTON_SCAN, 13349);
933 if (!item->IsPlugin() && !item->IsScript() && !item->IsLiveTV() && !item->IsAddonsPath() &&
934 item->GetPath() != "sources://video/" && item->GetPath() != "special://videoplaylists/" &&
935 item->GetPath().Left(19) != "newsmartplaylist://" && item->GetPath().Left(14) != "newplaylist://" &&
936 item->GetPath().Left(9) != "newtag://")
938 if (item->m_bIsFolder)
940 // Have both options for folders since we don't know whether all childs are watched/unwatched
941 buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
942 buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103); //Mark as Watched
946 if (item->GetOverlayImage().Equals("OverlayWatched.png"))
947 buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
949 buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103); //Mark as Watched
952 if (!g_application.IsVideoScanning() && item->IsVideoDb() && item->HasVideoInfoTag() &&
953 (!item->m_bIsFolder || m_vecItems->GetContent().Equals("movies") || m_vecItems->GetContent().Equals("tvshows")))
955 buttons.Add(CONTEXT_BUTTON_EDIT, 16106);
958 if (node == NODE_TYPE_SEASONS && item->m_bIsFolder)
959 buttons.Add(CONTEXT_BUTTON_SET_SEASON_ART, 13511);
961 if (StringUtils::StartsWithNoCase(item->GetPath(), "videodb://movies/sets/") && item->GetPath().size() > 22 && item->m_bIsFolder) // sets
963 buttons.Add(CONTEXT_BUTTON_SET_MOVIESET_ART, 13511);
964 buttons.Add(CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS, 20465);
965 buttons.Add(CONTEXT_BUTTON_DELETE, 646);
968 if (m_vecItems->GetContent() == "tags" && item->m_bIsFolder) // tags
970 CVideoDbUrl videoUrl;
971 if (videoUrl.FromString(item->GetPath()))
973 std::string mediaType = videoUrl.GetItemType();
975 CStdString strLabelAdd = StringUtils::Format(g_localizeStrings.Get(20460), GetLocalizedType(videoUrl.GetItemType()).c_str());
976 CStdString strLabelRemove = StringUtils::Format(g_localizeStrings.Get(20461), GetLocalizedType(videoUrl.GetItemType()).c_str());
977 buttons.Add(CONTEXT_BUTTON_TAGS_ADD_ITEMS, strLabelAdd);
978 buttons.Add(CONTEXT_BUTTON_TAGS_REMOVE_ITEMS, strLabelRemove);
979 buttons.Add(CONTEXT_BUTTON_DELETE, 646);
983 if (node == NODE_TYPE_ACTOR && !dir.IsAllItem(item->GetPath()) && item->m_bIsFolder)
985 if (StringUtils::StartsWithNoCase(m_vecItems->GetPath(), "videodb://musicvideos")) // mvids
986 buttons.Add(CONTEXT_BUTTON_SET_ARTIST_THUMB, 13359);
988 buttons.Add(CONTEXT_BUTTON_SET_ACTOR_THUMB, 20403);
990 if (item->IsVideoDb() && item->HasVideoInfoTag() &&
991 (!item->m_bIsFolder || node == NODE_TYPE_TITLE_TVSHOWS))
992 buttons.Add(CONTEXT_BUTTON_DELETE, 646);
995 if (!m_vecItems->IsVideoDb() && !m_vecItems->IsVirtualDirectoryRoot())
996 { // non-video db items, file operations are allowed
997 if ((CSettings::Get().GetBool("filelists.allowfiledeletion") &&
998 CUtil::SupportsWriteFileOperations(item->GetPath())) ||
999 (inPlaylists && !URIUtils::GetFileName(item->GetPath()).Equals("PartyMode-Video.xsp")
1000 && (item->IsPlayList() || item->IsSmartPlayList())))
1002 buttons.Add(CONTEXT_BUTTON_DELETE, 117);
1003 buttons.Add(CONTEXT_BUTTON_RENAME, 118);
1005 // add "Set/Change content" to folders
1006 if (item->m_bIsFolder && !item->IsVideoDb() && !item->IsPlayList() && !item->IsSmartPlayList() && !item->IsLibraryFolder() && !item->IsLiveTV() && !item->IsPlugin() && !item->IsAddonsPath() && !URIUtils::IsUPnP(item->GetPath()))
1008 if (!g_application.IsVideoScanning())
1010 if (info && info->Content() != CONTENT_NONE)
1012 buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20442);
1013 buttons.Add(CONTEXT_BUTTON_SCAN, 13349);
1016 buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20333);
1020 if (item->IsPlugin() || item->IsScript() || m_vecItems->IsPlugin())
1021 buttons.Add(CONTEXT_BUTTON_PLUGIN_SETTINGS, 1045);
1026 // predicate used by sorting and set_difference
1027 bool compFileItemsByDbId(const CFileItemPtr& lhs, const CFileItemPtr& rhs)
1029 return lhs->HasVideoInfoTag() && rhs->HasVideoInfoTag() && lhs->GetVideoInfoTag()->m_iDbId < rhs->GetVideoInfoTag()->m_iDbId;
1032 bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
1035 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
1036 item = m_vecItems->Get(itemNumber);
1037 if (CGUIDialogContextMenu::OnContextButton("video", item, button))
1039 //TODO should we search DB for entries from plugins?
1040 if (button == CONTEXT_BUTTON_REMOVE_SOURCE && !item->IsPlugin()
1041 && !item->IsLiveTV() &&!item->IsRSS() && !URIUtils::IsUPnP(item->GetPath()))
1043 OnUnAssignContent(item->GetPath(),20375,20340,20341);
1050 case CONTEXT_BUTTON_EDIT:
1052 CONTEXT_BUTTON ret = (CONTEXT_BUTTON)CGUIDialogVideoInfo::ManageVideoItem(item);
1055 if (ret == CONTEXT_BUTTON_MARK_WATCHED)
1056 m_viewControl.SetSelectedItem(itemNumber + 1);
1063 case CONTEXT_BUTTON_SET_SEASON_ART:
1064 case CONTEXT_BUTTON_SET_ACTOR_THUMB:
1065 case CONTEXT_BUTTON_SET_ARTIST_THUMB:
1066 case CONTEXT_BUTTON_SET_MOVIESET_ART:
1068 // Grab the thumbnails from the web
1069 CFileItemList items;
1070 CFileItemPtr noneitem(new CFileItem("thumb://None", false));
1071 CStdString currentThumb;
1073 CStdString artistPath;
1074 string artType = "thumb";
1075 if (button == CONTEXT_BUTTON_SET_ARTIST_THUMB)
1077 CMusicDatabase database;
1079 idArtist = database.GetArtistByName(m_vecItems->Get(itemNumber)->GetLabel());
1080 database.GetArtistPath(idArtist, artistPath);
1081 currentThumb = database.GetArtForItem(idArtist, "artist", "thumb");
1082 if (currentThumb.empty())
1083 currentThumb = m_database.GetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, artType);
1085 else if (button == CONTEXT_BUTTON_SET_ACTOR_THUMB)
1086 currentThumb = m_database.GetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, artType);
1089 map<string, string> currentArt;
1090 artType = CGUIDialogVideoInfo::ChooseArtType(*m_vecItems->Get(itemNumber), currentArt);
1091 if (artType.empty())
1094 if (artType == "fanart")
1096 OnChooseFanart(*m_vecItems->Get(itemNumber));
1100 if (currentArt.find(artType) != currentArt.end())
1101 currentThumb = currentArt[artType];
1102 else if ((artType == "poster" || artType == "banner") && currentArt.find("thumb") != currentArt.end())
1103 currentThumb = currentArt["thumb"];
1105 if (!currentThumb.IsEmpty())
1107 CFileItemPtr item(new CFileItem("thumb://Current", false));
1108 item->SetArt("thumb", currentThumb);
1109 item->SetLabel(g_localizeStrings.Get(13512));
1112 noneitem->SetIconImage("DefaultFolder.png");
1113 noneitem->SetLabel(g_localizeStrings.Get(13515));
1115 vector<CStdString> thumbs;
1116 if (button != CONTEXT_BUTTON_SET_ARTIST_THUMB)
1119 if (button == CONTEXT_BUTTON_SET_SEASON_ART)
1120 m_database.GetTvShowInfo("",tag,m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iIdShow);
1122 tag = *m_vecItems->Get(itemNumber)->GetVideoInfoTag();
1123 if (button == CONTEXT_BUTTON_SET_SEASON_ART)
1124 tag.m_strPictureURL.GetThumbURLs(thumbs, artType, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iSeason);
1126 tag.m_strPictureURL.GetThumbURLs(thumbs, artType);
1128 for (unsigned int i = 0; i < thumbs.size(); i++)
1130 CStdString strItemPath = StringUtils::Format("thumb://Remote%i",i);
1131 CFileItemPtr item(new CFileItem(strItemPath, false));
1132 item->SetArt("thumb", thumbs[i]);
1133 item->SetIconImage("DefaultPicture.png");
1134 item->SetLabel(g_localizeStrings.Get(13513));
1137 // TODO: Do we need to clear the cached image?
1138 // CTextureCache::Get().ClearCachedImage(thumbs[i]);
1143 if (button == CONTEXT_BUTTON_SET_ARTIST_THUMB)
1145 CStdString strThumb = URIUtils::AddFileToFolder(artistPath, "folder.jpg");
1146 if (XFILE::CFile::Exists(strThumb))
1148 CFileItemPtr pItem(new CFileItem(strThumb,false));
1149 pItem->SetLabel(g_localizeStrings.Get(13514));
1150 pItem->SetArt("thumb", strThumb);
1155 noneitem->SetIconImage("DefaultArtist.png");
1158 if (button == CONTEXT_BUTTON_SET_ACTOR_THUMB)
1160 CStdString picturePath;
1161 CStdString strThumb = URIUtils::AddFileToFolder(picturePath, "folder.jpg");
1162 if (XFILE::CFile::Exists(strThumb))
1164 CFileItemPtr pItem(new CFileItem(strThumb,false));
1165 pItem->SetLabel(g_localizeStrings.Get(13514));
1166 pItem->SetArt("thumb", strThumb);
1171 noneitem->SetIconImage("DefaultActor.png");
1174 if (button == CONTEXT_BUTTON_SET_MOVIESET_ART)
1175 noneitem->SetIconImage("DefaultVideo.png");
1178 items.Add(noneitem);
1180 VECSOURCES sources=*CMediaSourceSettings::Get().GetSources("video");
1181 g_mediaManager.GetLocalDrives(sources);
1183 CGUIDialogVideoInfo::AddItemPathToFileBrowserSources(sources, *item);
1184 if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources,
1185 g_localizeStrings.Get(13511), result))
1187 return false; // user cancelled
1190 if (result == "thumb://Current")
1191 result = currentThumb; // user chose the one they have
1193 // delete the thumbnail if that's what the user wants, else overwrite with the
1195 if (result.Left(14) == "thumb://Remote")
1197 int number = atoi(result.Mid(14));
1198 result = thumbs[number];
1200 else if (result == "thumb://None")
1202 if (button == CONTEXT_BUTTON_SET_MOVIESET_ART ||
1203 button == CONTEXT_BUTTON_SET_ACTOR_THUMB ||
1204 button == CONTEXT_BUTTON_SET_SEASON_ART ||
1205 (button == CONTEXT_BUTTON_SET_ARTIST_THUMB && idArtist < 0))
1206 m_database.SetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, artType, result);
1211 db.SetArtForItem(idArtist, "artist", artType, result);
1214 CUtil::DeleteVideoDatabaseDirectoryCache();
1215 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
1216 g_windowManager.SendMessage(msg);
1221 case CONTEXT_BUTTON_TAGS_ADD_ITEMS:
1223 CVideoDbUrl videoUrl;
1224 if (!videoUrl.FromString(item->GetPath()))
1227 std::string mediaType = videoUrl.GetItemType();
1228 mediaType = mediaType.substr(0, mediaType.length() - 1);
1230 CFileItemList items;
1231 CStdString localizedType = GetLocalizedType(mediaType);
1232 CStdString strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType.c_str());
1233 if (!GetItemsForTag(strLabel, mediaType, items, item->GetVideoInfoTag()->m_iDbId))
1236 CVideoDatabase videodb;
1237 if (!videodb.Open())
1240 for (int index = 0; index < items.Size(); index++)
1242 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1245 videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_iDbId, mediaType);
1248 // we need to clear any cached version of this tag's listing
1249 items.SetPath(item->GetPath());
1250 items.RemoveDiscCache(GetID());
1253 case CONTEXT_BUTTON_TAGS_REMOVE_ITEMS:
1255 CVideoDbUrl videoUrl;
1256 if (!videoUrl.FromString(item->GetPath()))
1259 std::string mediaType = videoUrl.GetItemType();
1260 mediaType = mediaType.substr(0, mediaType.length() - 1);
1262 CFileItemList items;
1263 CStdString localizedType = GetLocalizedType(mediaType);
1264 CStdString strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType.c_str());
1265 if (!GetItemsForTag(strLabel, mediaType, items, item->GetVideoInfoTag()->m_iDbId, false))
1268 CVideoDatabase videodb;
1269 if (!videodb.Open())
1272 for (int index = 0; index < items.Size(); index++)
1274 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1277 videodb.RemoveTagFromItem(items[index]->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_iDbId, mediaType);
1280 // we need to clear any cached version of this tag's listing
1281 items.SetPath(item->GetPath());
1282 items.RemoveDiscCache(GetID());
1285 case CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS:
1287 CFileItemList originalItems;
1288 CFileItemList selectedItems;
1290 if (!CGUIDialogVideoInfo::GetMoviesForSet(item.get(), originalItems, selectedItems) || selectedItems.Size() == 0) // need at least one item selected
1292 VECFILEITEMS original = originalItems.GetList();
1293 std::sort(original.begin(), original.end(), compFileItemsByDbId);
1294 VECFILEITEMS selected = selectedItems.GetList();
1295 std::sort(selected.begin(), selected.end(), compFileItemsByDbId);
1297 bool refreshNeeded = false;
1298 // update the "added" items
1299 VECFILEITEMS addedItems;
1300 set_difference(selected.begin(),selected.end(), original.begin(),original.end(), std::back_inserter(addedItems), compFileItemsByDbId);
1301 for (VECFILEITEMS::iterator it = addedItems.begin(); it != addedItems.end(); ++it)
1303 if (CGUIDialogVideoInfo::SetMovieSet(it->get(), item.get()))
1304 refreshNeeded = true;
1306 // update the "deleted" items
1307 CFileItemPtr clearItem(new CFileItem());
1308 clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
1309 VECFILEITEMS deletedItems;
1310 set_difference(original.begin(),original.end(), selected.begin(),selected.end(), std::back_inserter(deletedItems), compFileItemsByDbId);
1311 for (VECFILEITEMS::iterator it = deletedItems.begin(); it != deletedItems.end(); ++it)
1313 if (CGUIDialogVideoInfo::SetMovieSet(it->get(), clearItem.get()))
1314 refreshNeeded = true;
1317 // we need to clear any cached version of this tag's listing
1322 case CONTEXT_BUTTON_GO_TO_ARTIST:
1325 CMusicDatabase database;
1327 strPath = StringUtils::Format("musicdb://artists/%ld/",
1328 database.GetArtistByName(StringUtils::Join(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator)));
1329 g_windowManager.ActivateWindow(WINDOW_MUSIC_NAV,strPath);
1332 case CONTEXT_BUTTON_GO_TO_ALBUM:
1335 CMusicDatabase database;
1337 strPath = StringUtils::Format("musicdb://albums/%ld/",
1338 database.GetAlbumByName(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strAlbum));
1339 g_windowManager.ActivateWindow(WINDOW_MUSIC_NAV,strPath);
1342 case CONTEXT_BUTTON_PLAY_OTHER:
1344 CMusicDatabase database;
1347 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,
1348 m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strTitle),
1351 CApplicationMessenger::Get().PlayFile(song);
1360 return CGUIWindowVideoBase::OnContextButton(itemNumber, button);
1363 void CGUIWindowVideoNav::OnChooseFanart(const CFileItem &videoItem)
1365 if (!videoItem.HasVideoInfoTag())
1368 CFileItem item(videoItem);
1370 CFileItemList items;
1372 CVideoThumbLoader loader;
1373 loader.LoadItem(&item);
1375 if (item.HasArt("fanart"))
1377 CFileItemPtr itemCurrent(new CFileItem("fanart://Current",false));
1378 itemCurrent->SetArt("thumb", item.GetArt("fanart"));
1379 itemCurrent->SetLabel(g_localizeStrings.Get(20440));
1380 items.Add(itemCurrent);
1383 // add the none option
1385 CFileItemPtr itemNone(new CFileItem("fanart://None", false));
1386 itemNone->SetIconImage("DefaultVideo.png");
1387 itemNone->SetLabel(g_localizeStrings.Get(20439));
1388 items.Add(itemNone);
1392 VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
1393 g_mediaManager.GetLocalDrives(sources);
1394 CGUIDialogVideoInfo::AddItemPathToFileBrowserSources(sources, item);
1396 if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) || result.Equals("fanart://Current"))
1399 if (result.Equals("fanart://None") || !CFile::Exists(result))
1401 if (!result.IsEmpty() && flip)
1402 result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
1408 db.SetArtForItem(item.GetVideoInfoTag()->m_iDbId, item.GetVideoInfoTag()->m_type, "fanart", result);
1412 // clear view cache and reload images
1413 CUtil::DeleteVideoDatabaseDirectoryCache();
1418 bool CGUIWindowVideoNav::OnClick(int iItem)
1420 CFileItemPtr item = m_vecItems->Get(iItem);
1421 if (!item->m_bIsFolder && item->IsVideoDb() && !item->Exists())
1423 CLog::Log(LOGDEBUG, "%s called on '%s' but file doesn't exist", __FUNCTION__, item->GetPath().c_str());
1424 if (!DeleteItem(item.get(), true))
1429 m_viewControl.SetSelectedItem(iItem);
1432 else if (StringUtils::StartsWithNoCase(item->GetPath(), "newtag://"))
1434 // dont allow update while scanning
1435 if (g_application.IsVideoScanning())
1437 CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1443 if (!CGUIKeyboardFactory::ShowAndGetInput(strTag, g_localizeStrings.Get(20462), false))
1446 CVideoDatabase videodb;
1447 if (!videodb.Open())
1450 // get the media type and convert from plural to singular (by removing the trailing "s")
1451 CStdString mediaType = item->GetPath().Mid(9);
1452 mediaType = mediaType.Left(mediaType.size() - 1);
1453 CStdString localizedType = GetLocalizedType(mediaType);
1454 if (localizedType.empty())
1457 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())
1459 CStdString strError = StringUtils::Format(g_localizeStrings.Get(20463), strTag.c_str());
1460 CGUIDialogOK::ShowAndGetInput(20462, "", strError, "");
1464 int idTag = videodb.AddTag(strTag);
1465 CFileItemList items;
1466 CStdString strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType.c_str());
1467 if (GetItemsForTag(strLabel, mediaType, items, idTag))
1469 for (int index = 0; index < items.Size(); index++)
1471 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1474 videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, idTag, mediaType);
1482 return CGUIWindowVideoBase::OnClick(iItem);
1485 CStdString CGUIWindowVideoNav::GetStartFolder(const CStdString &dir)
1487 if (dir.Equals("MovieGenres"))
1488 return "videodb://movies/genres/";
1489 else if (dir.Equals("MovieTitles"))
1490 return "videodb://movies/titles/";
1491 else if (dir.Equals("MovieYears"))
1492 return "videodb://movies/years/";
1493 else if (dir.Equals("MovieActors"))
1494 return "videodb://movies/actors/";
1495 else if (dir.Equals("MovieDirectors"))
1496 return "videodb://movies/directors/";
1497 else if (dir.Equals("MovieStudios"))
1498 return "videodb://movies/studios/";
1499 else if (dir.Equals("MovieSets"))
1500 return "videodb://movies/sets/";
1501 else if (dir.Equals("MovieCountries"))
1502 return "videodb://movies/countries/";
1503 else if (dir.Equals("MovieTags"))
1504 return "videodb://movies/tags/";
1505 else if (dir.Equals("Movies"))
1506 return "videodb://movies/";
1507 else if (dir.Equals("TvShowGenres"))
1508 return "videodb://tvshows/genres/";
1509 else if (dir.Equals("TvShowTitles"))
1510 return "videodb://tvshows/titles/";
1511 else if (dir.Equals("TvShowYears"))
1512 return "videodb://tvshows/years/";
1513 else if (dir.Equals("TvShowActors"))
1514 return "videodb://tvshows/actors/";
1515 else if (dir.Equals("TvShowStudios"))
1516 return "videodb://tvshows/studios/";
1517 else if (dir.Equals("TvShowTags"))
1518 return "videodb://tvshows/tags/";
1519 else if (dir.Equals("TvShows"))
1520 return "videodb://tvshows/";
1521 else if (dir.Equals("MusicVideoGenres"))
1522 return "videodb://musicvideos/genres/";
1523 else if (dir.Equals("MusicVideoTitles"))
1524 return "videodb://musicvideos/titles/";
1525 else if (dir.Equals("MusicVideoYears"))
1526 return "videodb://musicvideos/years/";
1527 else if (dir.Equals("MusicVideoArtists"))
1528 return "videodb://musicvideos/artists/";
1529 else if (dir.Equals("MusicVideoAlbums"))
1530 return "videodb://musicvideos/albums/";
1531 else if (dir.Equals("MusicVideoDirectors"))
1532 return "videodb://musicvideos/directors/";
1533 else if (dir.Equals("MusicVideoStudios"))
1534 return "videodb://musicvideos/studios/";
1535 else if (dir.Equals("MusicVideoTags"))
1536 return "videodb://musicvideos/tags/";
1537 else if (dir.Equals("MusicVideos"))
1538 return "videodb://musicvideos/";
1539 else if (dir.Equals("RecentlyAddedMovies"))
1540 return "videodb://recentlyaddedmovies/";
1541 else if (dir.Equals("RecentlyAddedEpisodes"))
1542 return "videodb://recentlyaddedepisodes/";
1543 else if (dir.Equals("RecentlyAddedMusicVideos"))
1544 return "videodb://recentlyaddedmusicvideos/";
1545 else if (dir.Equals("Files"))
1546 return "sources://video/";
1547 return CGUIWindowVideoBase::GetStartFolder(dir);
1550 bool CGUIWindowVideoNav::ApplyWatchedFilter(CFileItemList &items)
1552 bool listchanged = false;
1553 CVideoDatabaseDirectory dir;
1554 NODE_TYPE node = dir.GetDirectoryChildType(items.GetPath());
1556 // now filter watched items as necessary
1557 bool filterWatched=false;
1558 if (node == NODE_TYPE_EPISODES
1559 || node == NODE_TYPE_SEASONS
1560 || node == NODE_TYPE_SETS
1561 || node == NODE_TYPE_TAGS
1562 || node == NODE_TYPE_TITLE_MOVIES
1563 || node == NODE_TYPE_TITLE_TVSHOWS
1564 || node == NODE_TYPE_TITLE_MUSICVIDEOS
1565 || node == NODE_TYPE_RECENTLY_ADDED_EPISODES
1566 || node == NODE_TYPE_RECENTLY_ADDED_MOVIES
1567 || node == NODE_TYPE_RECENTLY_ADDED_MUSICVIDEOS)
1568 filterWatched = true;
1569 if (!items.IsVideoDb())
1570 filterWatched = true;
1571 if (items.GetContent() == "tvshows" &&
1572 (items.IsSmartPlayList() || items.IsLibraryFolder()))
1573 node = NODE_TYPE_TITLE_TVSHOWS; // so that the check below works
1575 int watchMode = CMediaSettings::Get().GetWatchedMode(m_vecItems->GetContent());
1577 for (int i = 0; i < items.Size(); i++)
1579 CFileItemPtr item = items.Get(i);
1581 if(item->HasVideoInfoTag() && (node == NODE_TYPE_TITLE_TVSHOWS || node == NODE_TYPE_SEASONS))
1583 if (watchMode == WatchedModeUnwatched)
1584 item->GetVideoInfoTag()->m_iEpisode = (int)item->GetProperty("unwatchedepisodes").asInteger();
1585 if (watchMode == WatchedModeWatched)
1586 item->GetVideoInfoTag()->m_iEpisode = (int)item->GetProperty("watchedepisodes").asInteger();
1587 if (watchMode == WatchedModeAll)
1588 item->GetVideoInfoTag()->m_iEpisode = (int)item->GetProperty("totalepisodes").asInteger();
1589 item->SetProperty("numepisodes", item->GetVideoInfoTag()->m_iEpisode);
1595 if((watchMode==WatchedModeWatched && item->GetVideoInfoTag()->m_playCount== 0)
1596 || (watchMode==WatchedModeUnwatched && item->GetVideoInfoTag()->m_playCount > 0))
1605 if(node == NODE_TYPE_TITLE_TVSHOWS || node == NODE_TYPE_SEASONS)
1607 // the watched filter may change the "numepisodes" property which is reflected in the TV_SHOWS and SEASONS nodes
1608 // therefore, the items labels have to be refreshed, and possibly the list needs resorting as well.
1609 items.ClearSortState(); // this is needed to force resorting even if sort method did not change
1610 FormatAndSort(items);
1616 bool CGUIWindowVideoNav::GetItemsForTag(const CStdString &strHeading, const std::string &type, CFileItemList &items, int idTag /* = -1 */, bool showAll /* = true */)
1618 CVideoDatabase videodb;
1619 if (!videodb.Open())
1622 MediaType mediaType = MediaTypeNone;
1623 std::string baseDir = "videodb://";
1624 std::string idColumn;
1625 if (type.compare("movie") == 0)
1627 mediaType = MediaTypeMovie;
1628 baseDir += "movies";
1629 idColumn = "idMovie";
1631 else if (type.compare("tvshow") == 0)
1633 mediaType = MediaTypeTvShow;
1634 baseDir += "tvshows";
1635 idColumn = "idShow";
1637 else if (type.compare("musicvideo") == 0)
1639 mediaType = MediaTypeMusicVideo;
1640 baseDir += "musicvideos";
1641 idColumn = "idMVideo";
1644 baseDir += "/titles/";
1645 CVideoDbUrl videoUrl;
1646 if (!videoUrl.FromString(baseDir))
1649 CVideoDatabase::Filter filter;
1653 videoUrl.AddOption("tagid", idTag);
1655 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());
1658 CFileItemList listItems;
1659 if (!videodb.GetSortedVideos(mediaType, videoUrl.ToString(), SortDescription(), listItems, filter) || listItems.Size() <= 0)
1662 CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1666 listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1669 dialog->SetMultiSelection(true);
1670 dialog->SetHeading(strHeading);
1671 dialog->SetItems(&listItems);
1672 dialog->EnableButton(true, 186);
1675 items.Copy(dialog->GetSelectedItems());
1676 return items.Size() > 0;
1679 CStdString CGUIWindowVideoNav::GetLocalizedType(const std::string &strType)
1681 if (strType == "movie" || strType == "movies")
1682 return g_localizeStrings.Get(20342);
1683 else if (strType == "tvshow" || strType == "tvshows")
1684 return g_localizeStrings.Get(20343);
1685 else if (strType == "episode" || strType == "episodes")
1686 return g_localizeStrings.Get(20359);
1687 else if (strType == "musicvideo" || strType == "musicvideos")
1688 return g_localizeStrings.Get(20391);