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().empty() && 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().empty() && !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
411 content = database.GetContentForPath(items.GetPath());
412 items.SetContent(content.empty() ? "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.empty())
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.empty()) /* 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.empty())
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/" &&
935 item->GetPath() != "special://videoplaylists/" &&
936 !StringUtils::StartsWith(item->GetPath(), "newsmartplaylist://") &&
937 !StringUtils::StartsWith(item->GetPath(), "newplaylist://") &&
938 !StringUtils::StartsWith(item->GetPath(), "newtag://"))
940 if (item->m_bIsFolder)
942 // Have both options for folders since we don't know whether all childs are watched/unwatched
943 buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
944 buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103); //Mark as Watched
948 if (item->GetOverlayImage().Equals("OverlayWatched.png"))
949 buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
951 buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103); //Mark as Watched
954 if (!g_application.IsVideoScanning() && item->IsVideoDb() && item->HasVideoInfoTag() &&
955 (!item->m_bIsFolder || m_vecItems->GetContent().Equals("movies") || m_vecItems->GetContent().Equals("tvshows")))
957 buttons.Add(CONTEXT_BUTTON_EDIT, 16106);
960 if (node == NODE_TYPE_SEASONS && item->m_bIsFolder)
961 buttons.Add(CONTEXT_BUTTON_SET_SEASON_ART, 13511);
963 if (StringUtils::StartsWithNoCase(item->GetPath(), "videodb://movies/sets/") && item->GetPath().size() > 22 && item->m_bIsFolder) // sets
965 buttons.Add(CONTEXT_BUTTON_SET_MOVIESET_ART, 13511);
966 buttons.Add(CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS, 20465);
967 buttons.Add(CONTEXT_BUTTON_DELETE, 646);
970 if (m_vecItems->GetContent() == "tags" && item->m_bIsFolder) // tags
972 CVideoDbUrl videoUrl;
973 if (videoUrl.FromString(item->GetPath()))
975 std::string mediaType = videoUrl.GetItemType();
977 CStdString strLabelAdd = StringUtils::Format(g_localizeStrings.Get(20460), GetLocalizedType(videoUrl.GetItemType()).c_str());
978 CStdString strLabelRemove = StringUtils::Format(g_localizeStrings.Get(20461), GetLocalizedType(videoUrl.GetItemType()).c_str());
979 buttons.Add(CONTEXT_BUTTON_TAGS_ADD_ITEMS, strLabelAdd);
980 buttons.Add(CONTEXT_BUTTON_TAGS_REMOVE_ITEMS, strLabelRemove);
981 buttons.Add(CONTEXT_BUTTON_DELETE, 646);
985 if (node == NODE_TYPE_ACTOR && !dir.IsAllItem(item->GetPath()) && item->m_bIsFolder)
987 if (StringUtils::StartsWithNoCase(m_vecItems->GetPath(), "videodb://musicvideos")) // mvids
988 buttons.Add(CONTEXT_BUTTON_SET_ARTIST_THUMB, 13359);
990 buttons.Add(CONTEXT_BUTTON_SET_ACTOR_THUMB, 20403);
992 if (item->IsVideoDb() && item->HasVideoInfoTag() &&
993 (!item->m_bIsFolder || node == NODE_TYPE_TITLE_TVSHOWS))
994 buttons.Add(CONTEXT_BUTTON_DELETE, 646);
997 if (!m_vecItems->IsVideoDb() && !m_vecItems->IsVirtualDirectoryRoot())
998 { // non-video db items, file operations are allowed
999 if ((CSettings::Get().GetBool("filelists.allowfiledeletion") &&
1000 CUtil::SupportsWriteFileOperations(item->GetPath())) ||
1001 (inPlaylists && !URIUtils::GetFileName(item->GetPath()).Equals("PartyMode-Video.xsp")
1002 && (item->IsPlayList() || item->IsSmartPlayList())))
1004 buttons.Add(CONTEXT_BUTTON_DELETE, 117);
1005 buttons.Add(CONTEXT_BUTTON_RENAME, 118);
1007 // add "Set/Change content" to folders
1008 if (item->m_bIsFolder && !item->IsVideoDb() && !item->IsPlayList() && !item->IsSmartPlayList() && !item->IsLibraryFolder() && !item->IsLiveTV() && !item->IsPlugin() && !item->IsAddonsPath() && !URIUtils::IsUPnP(item->GetPath()))
1010 if (!g_application.IsVideoScanning())
1012 if (info && info->Content() != CONTENT_NONE)
1014 buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20442);
1015 buttons.Add(CONTEXT_BUTTON_SCAN, 13349);
1018 buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20333);
1022 if (item->IsPlugin() || item->IsScript() || m_vecItems->IsPlugin())
1023 buttons.Add(CONTEXT_BUTTON_PLUGIN_SETTINGS, 1045);
1028 // predicate used by sorting and set_difference
1029 bool compFileItemsByDbId(const CFileItemPtr& lhs, const CFileItemPtr& rhs)
1031 return lhs->HasVideoInfoTag() && rhs->HasVideoInfoTag() && lhs->GetVideoInfoTag()->m_iDbId < rhs->GetVideoInfoTag()->m_iDbId;
1034 bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
1037 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
1038 item = m_vecItems->Get(itemNumber);
1039 if (CGUIDialogContextMenu::OnContextButton("video", item, button))
1041 //TODO should we search DB for entries from plugins?
1042 if (button == CONTEXT_BUTTON_REMOVE_SOURCE && !item->IsPlugin()
1043 && !item->IsLiveTV() &&!item->IsRSS() && !URIUtils::IsUPnP(item->GetPath()))
1045 OnUnAssignContent(item->GetPath(),20375,20340,20341);
1052 case CONTEXT_BUTTON_EDIT:
1054 CONTEXT_BUTTON ret = (CONTEXT_BUTTON)CGUIDialogVideoInfo::ManageVideoItem(item);
1057 if (ret == CONTEXT_BUTTON_MARK_WATCHED)
1058 m_viewControl.SetSelectedItem(itemNumber + 1);
1065 case CONTEXT_BUTTON_SET_SEASON_ART:
1066 case CONTEXT_BUTTON_SET_ACTOR_THUMB:
1067 case CONTEXT_BUTTON_SET_ARTIST_THUMB:
1068 case CONTEXT_BUTTON_SET_MOVIESET_ART:
1070 // Grab the thumbnails from the web
1071 CFileItemList items;
1072 CFileItemPtr noneitem(new CFileItem("thumb://None", false));
1073 CStdString currentThumb;
1075 CStdString artistPath;
1076 string artType = "thumb";
1077 if (button == CONTEXT_BUTTON_SET_ARTIST_THUMB)
1079 CMusicDatabase database;
1081 idArtist = database.GetArtistByName(m_vecItems->Get(itemNumber)->GetLabel());
1082 database.GetArtistPath(idArtist, artistPath);
1083 currentThumb = database.GetArtForItem(idArtist, "artist", "thumb");
1084 if (currentThumb.empty())
1085 currentThumb = m_database.GetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, artType);
1087 else if (button == CONTEXT_BUTTON_SET_ACTOR_THUMB)
1088 currentThumb = m_database.GetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, artType);
1091 map<string, string> currentArt;
1092 artType = CGUIDialogVideoInfo::ChooseArtType(*m_vecItems->Get(itemNumber), currentArt);
1093 if (artType.empty())
1096 if (artType == "fanart")
1098 OnChooseFanart(*m_vecItems->Get(itemNumber));
1102 if (currentArt.find(artType) != currentArt.end())
1103 currentThumb = currentArt[artType];
1104 else if ((artType == "poster" || artType == "banner") && currentArt.find("thumb") != currentArt.end())
1105 currentThumb = currentArt["thumb"];
1107 if (!currentThumb.empty())
1109 CFileItemPtr item(new CFileItem("thumb://Current", false));
1110 item->SetArt("thumb", currentThumb);
1111 item->SetLabel(g_localizeStrings.Get(13512));
1114 noneitem->SetIconImage("DefaultFolder.png");
1115 noneitem->SetLabel(g_localizeStrings.Get(13515));
1117 vector<CStdString> thumbs;
1118 if (button != CONTEXT_BUTTON_SET_ARTIST_THUMB)
1121 if (button == CONTEXT_BUTTON_SET_SEASON_ART)
1122 m_database.GetTvShowInfo("",tag,m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iIdShow);
1124 tag = *m_vecItems->Get(itemNumber)->GetVideoInfoTag();
1125 if (button == CONTEXT_BUTTON_SET_SEASON_ART)
1126 tag.m_strPictureURL.GetThumbURLs(thumbs, artType, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iSeason);
1128 tag.m_strPictureURL.GetThumbURLs(thumbs, artType);
1130 for (unsigned int i = 0; i < thumbs.size(); i++)
1132 CStdString strItemPath = StringUtils::Format("thumb://Remote%i",i);
1133 CFileItemPtr item(new CFileItem(strItemPath, false));
1134 item->SetArt("thumb", thumbs[i]);
1135 item->SetIconImage("DefaultPicture.png");
1136 item->SetLabel(g_localizeStrings.Get(13513));
1139 // TODO: Do we need to clear the cached image?
1140 // CTextureCache::Get().ClearCachedImage(thumbs[i]);
1145 if (button == CONTEXT_BUTTON_SET_ARTIST_THUMB)
1147 CStdString strThumb = URIUtils::AddFileToFolder(artistPath, "folder.jpg");
1148 if (XFILE::CFile::Exists(strThumb))
1150 CFileItemPtr pItem(new CFileItem(strThumb,false));
1151 pItem->SetLabel(g_localizeStrings.Get(13514));
1152 pItem->SetArt("thumb", strThumb);
1157 noneitem->SetIconImage("DefaultArtist.png");
1160 if (button == CONTEXT_BUTTON_SET_ACTOR_THUMB)
1162 CStdString picturePath;
1163 CStdString strThumb = URIUtils::AddFileToFolder(picturePath, "folder.jpg");
1164 if (XFILE::CFile::Exists(strThumb))
1166 CFileItemPtr pItem(new CFileItem(strThumb,false));
1167 pItem->SetLabel(g_localizeStrings.Get(13514));
1168 pItem->SetArt("thumb", strThumb);
1173 noneitem->SetIconImage("DefaultActor.png");
1176 if (button == CONTEXT_BUTTON_SET_MOVIESET_ART)
1177 noneitem->SetIconImage("DefaultVideo.png");
1180 items.Add(noneitem);
1182 VECSOURCES sources=*CMediaSourceSettings::Get().GetSources("video");
1183 g_mediaManager.GetLocalDrives(sources);
1185 CGUIDialogVideoInfo::AddItemPathToFileBrowserSources(sources, *item);
1186 if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources,
1187 g_localizeStrings.Get(13511), result))
1189 return false; // user cancelled
1192 if (result == "thumb://Current")
1193 result = currentThumb; // user chose the one they have
1195 // delete the thumbnail if that's what the user wants, else overwrite with the
1197 if (StringUtils::StartsWith(result, "thumb://Remote"))
1199 int number = atoi(result.substr(14).c_str());
1200 result = thumbs[number];
1202 else if (result == "thumb://None")
1204 if (button == CONTEXT_BUTTON_SET_MOVIESET_ART ||
1205 button == CONTEXT_BUTTON_SET_ACTOR_THUMB ||
1206 button == CONTEXT_BUTTON_SET_SEASON_ART ||
1207 (button == CONTEXT_BUTTON_SET_ARTIST_THUMB && idArtist < 0))
1208 m_database.SetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, artType, result);
1213 db.SetArtForItem(idArtist, "artist", artType, result);
1216 CUtil::DeleteVideoDatabaseDirectoryCache();
1217 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
1218 g_windowManager.SendMessage(msg);
1223 case CONTEXT_BUTTON_TAGS_ADD_ITEMS:
1225 CVideoDbUrl videoUrl;
1226 if (!videoUrl.FromString(item->GetPath()))
1229 std::string mediaType = videoUrl.GetItemType();
1230 mediaType = mediaType.substr(0, mediaType.length() - 1);
1232 CFileItemList items;
1233 CStdString localizedType = GetLocalizedType(mediaType);
1234 CStdString strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType.c_str());
1235 if (!GetItemsForTag(strLabel, mediaType, items, item->GetVideoInfoTag()->m_iDbId))
1238 CVideoDatabase videodb;
1239 if (!videodb.Open())
1242 for (int index = 0; index < items.Size(); index++)
1244 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1247 videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_iDbId, mediaType);
1250 // we need to clear any cached version of this tag's listing
1251 items.SetPath(item->GetPath());
1252 items.RemoveDiscCache(GetID());
1255 case CONTEXT_BUTTON_TAGS_REMOVE_ITEMS:
1257 CVideoDbUrl videoUrl;
1258 if (!videoUrl.FromString(item->GetPath()))
1261 std::string mediaType = videoUrl.GetItemType();
1262 mediaType = mediaType.substr(0, mediaType.length() - 1);
1264 CFileItemList items;
1265 CStdString localizedType = GetLocalizedType(mediaType);
1266 CStdString strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType.c_str());
1267 if (!GetItemsForTag(strLabel, mediaType, items, item->GetVideoInfoTag()->m_iDbId, false))
1270 CVideoDatabase videodb;
1271 if (!videodb.Open())
1274 for (int index = 0; index < items.Size(); index++)
1276 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1279 videodb.RemoveTagFromItem(items[index]->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_iDbId, mediaType);
1282 // we need to clear any cached version of this tag's listing
1283 items.SetPath(item->GetPath());
1284 items.RemoveDiscCache(GetID());
1287 case CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS:
1289 CFileItemList originalItems;
1290 CFileItemList selectedItems;
1292 if (!CGUIDialogVideoInfo::GetMoviesForSet(item.get(), originalItems, selectedItems) || selectedItems.Size() == 0) // need at least one item selected
1294 VECFILEITEMS original = originalItems.GetList();
1295 std::sort(original.begin(), original.end(), compFileItemsByDbId);
1296 VECFILEITEMS selected = selectedItems.GetList();
1297 std::sort(selected.begin(), selected.end(), compFileItemsByDbId);
1299 bool refreshNeeded = false;
1300 // update the "added" items
1301 VECFILEITEMS addedItems;
1302 set_difference(selected.begin(),selected.end(), original.begin(),original.end(), std::back_inserter(addedItems), compFileItemsByDbId);
1303 for (VECFILEITEMS::iterator it = addedItems.begin(); it != addedItems.end(); ++it)
1305 if (CGUIDialogVideoInfo::SetMovieSet(it->get(), item.get()))
1306 refreshNeeded = true;
1308 // update the "deleted" items
1309 CFileItemPtr clearItem(new CFileItem());
1310 clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
1311 VECFILEITEMS deletedItems;
1312 set_difference(original.begin(),original.end(), selected.begin(),selected.end(), std::back_inserter(deletedItems), compFileItemsByDbId);
1313 for (VECFILEITEMS::iterator it = deletedItems.begin(); it != deletedItems.end(); ++it)
1315 if (CGUIDialogVideoInfo::SetMovieSet(it->get(), clearItem.get()))
1316 refreshNeeded = true;
1319 // we need to clear any cached version of this tag's listing
1324 case CONTEXT_BUTTON_GO_TO_ARTIST:
1327 CMusicDatabase database;
1329 strPath = StringUtils::Format("musicdb://artists/%ld/",
1330 database.GetArtistByName(StringUtils::Join(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator)));
1331 g_windowManager.ActivateWindow(WINDOW_MUSIC_NAV,strPath);
1334 case CONTEXT_BUTTON_GO_TO_ALBUM:
1337 CMusicDatabase database;
1339 strPath = StringUtils::Format("musicdb://albums/%ld/",
1340 database.GetAlbumByName(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strAlbum));
1341 g_windowManager.ActivateWindow(WINDOW_MUSIC_NAV,strPath);
1344 case CONTEXT_BUTTON_PLAY_OTHER:
1346 CMusicDatabase database;
1349 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,
1350 m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strTitle),
1353 CApplicationMessenger::Get().PlayFile(song);
1362 return CGUIWindowVideoBase::OnContextButton(itemNumber, button);
1365 void CGUIWindowVideoNav::OnChooseFanart(const CFileItem &videoItem)
1367 if (!videoItem.HasVideoInfoTag())
1370 CFileItem item(videoItem);
1372 CFileItemList items;
1374 CVideoThumbLoader loader;
1375 loader.LoadItem(&item);
1377 if (item.HasArt("fanart"))
1379 CFileItemPtr itemCurrent(new CFileItem("fanart://Current",false));
1380 itemCurrent->SetArt("thumb", item.GetArt("fanart"));
1381 itemCurrent->SetLabel(g_localizeStrings.Get(20440));
1382 items.Add(itemCurrent);
1385 // add the none option
1387 CFileItemPtr itemNone(new CFileItem("fanart://None", false));
1388 itemNone->SetIconImage("DefaultVideo.png");
1389 itemNone->SetLabel(g_localizeStrings.Get(20439));
1390 items.Add(itemNone);
1394 VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
1395 g_mediaManager.GetLocalDrives(sources);
1396 CGUIDialogVideoInfo::AddItemPathToFileBrowserSources(sources, item);
1398 if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) || result.Equals("fanart://Current"))
1401 if (result.Equals("fanart://None") || !CFile::Exists(result))
1403 if (!result.empty() && flip)
1404 result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
1410 db.SetArtForItem(item.GetVideoInfoTag()->m_iDbId, item.GetVideoInfoTag()->m_type, "fanart", result);
1414 // clear view cache and reload images
1415 CUtil::DeleteVideoDatabaseDirectoryCache();
1420 bool CGUIWindowVideoNav::OnClick(int iItem)
1422 CFileItemPtr item = m_vecItems->Get(iItem);
1423 if (!item->m_bIsFolder && item->IsVideoDb() && !item->Exists())
1425 CLog::Log(LOGDEBUG, "%s called on '%s' but file doesn't exist", __FUNCTION__, item->GetPath().c_str());
1426 if (!DeleteItem(item.get(), true))
1431 m_viewControl.SetSelectedItem(iItem);
1434 else if (StringUtils::StartsWithNoCase(item->GetPath(), "newtag://"))
1436 // dont allow update while scanning
1437 if (g_application.IsVideoScanning())
1439 CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1445 if (!CGUIKeyboardFactory::ShowAndGetInput(strTag, g_localizeStrings.Get(20462), false))
1448 CVideoDatabase videodb;
1449 if (!videodb.Open())
1452 // get the media type and convert from plural to singular (by removing the trailing "s")
1453 CStdString mediaType = item->GetPath().substr(9);
1454 mediaType = mediaType.substr(0, mediaType.size() - 1);
1455 CStdString localizedType = GetLocalizedType(mediaType);
1456 if (localizedType.empty())
1459 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())
1461 CStdString strError = StringUtils::Format(g_localizeStrings.Get(20463), strTag.c_str());
1462 CGUIDialogOK::ShowAndGetInput(20462, "", strError, "");
1466 int idTag = videodb.AddTag(strTag);
1467 CFileItemList items;
1468 CStdString strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType.c_str());
1469 if (GetItemsForTag(strLabel, mediaType, items, idTag))
1471 for (int index = 0; index < items.Size(); index++)
1473 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1476 videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, idTag, mediaType);
1484 return CGUIWindowVideoBase::OnClick(iItem);
1487 CStdString CGUIWindowVideoNav::GetStartFolder(const CStdString &dir)
1489 if (dir.Equals("MovieGenres"))
1490 return "videodb://movies/genres/";
1491 else if (dir.Equals("MovieTitles"))
1492 return "videodb://movies/titles/";
1493 else if (dir.Equals("MovieYears"))
1494 return "videodb://movies/years/";
1495 else if (dir.Equals("MovieActors"))
1496 return "videodb://movies/actors/";
1497 else if (dir.Equals("MovieDirectors"))
1498 return "videodb://movies/directors/";
1499 else if (dir.Equals("MovieStudios"))
1500 return "videodb://movies/studios/";
1501 else if (dir.Equals("MovieSets"))
1502 return "videodb://movies/sets/";
1503 else if (dir.Equals("MovieCountries"))
1504 return "videodb://movies/countries/";
1505 else if (dir.Equals("MovieTags"))
1506 return "videodb://movies/tags/";
1507 else if (dir.Equals("Movies"))
1508 return "videodb://movies/";
1509 else if (dir.Equals("TvShowGenres"))
1510 return "videodb://tvshows/genres/";
1511 else if (dir.Equals("TvShowTitles"))
1512 return "videodb://tvshows/titles/";
1513 else if (dir.Equals("TvShowYears"))
1514 return "videodb://tvshows/years/";
1515 else if (dir.Equals("TvShowActors"))
1516 return "videodb://tvshows/actors/";
1517 else if (dir.Equals("TvShowStudios"))
1518 return "videodb://tvshows/studios/";
1519 else if (dir.Equals("TvShowTags"))
1520 return "videodb://tvshows/tags/";
1521 else if (dir.Equals("TvShows"))
1522 return "videodb://tvshows/";
1523 else if (dir.Equals("MusicVideoGenres"))
1524 return "videodb://musicvideos/genres/";
1525 else if (dir.Equals("MusicVideoTitles"))
1526 return "videodb://musicvideos/titles/";
1527 else if (dir.Equals("MusicVideoYears"))
1528 return "videodb://musicvideos/years/";
1529 else if (dir.Equals("MusicVideoArtists"))
1530 return "videodb://musicvideos/artists/";
1531 else if (dir.Equals("MusicVideoAlbums"))
1532 return "videodb://musicvideos/albums/";
1533 else if (dir.Equals("MusicVideoDirectors"))
1534 return "videodb://musicvideos/directors/";
1535 else if (dir.Equals("MusicVideoStudios"))
1536 return "videodb://musicvideos/studios/";
1537 else if (dir.Equals("MusicVideoTags"))
1538 return "videodb://musicvideos/tags/";
1539 else if (dir.Equals("MusicVideos"))
1540 return "videodb://musicvideos/";
1541 else if (dir.Equals("RecentlyAddedMovies"))
1542 return "videodb://recentlyaddedmovies/";
1543 else if (dir.Equals("RecentlyAddedEpisodes"))
1544 return "videodb://recentlyaddedepisodes/";
1545 else if (dir.Equals("RecentlyAddedMusicVideos"))
1546 return "videodb://recentlyaddedmusicvideos/";
1547 else if (dir.Equals("Files"))
1548 return "sources://video/";
1549 return CGUIWindowVideoBase::GetStartFolder(dir);
1552 bool CGUIWindowVideoNav::ApplyWatchedFilter(CFileItemList &items)
1554 bool listchanged = false;
1555 CVideoDatabaseDirectory dir;
1556 NODE_TYPE node = dir.GetDirectoryChildType(items.GetPath());
1558 // now filter watched items as necessary
1559 bool filterWatched=false;
1560 if (node == NODE_TYPE_EPISODES
1561 || node == NODE_TYPE_SEASONS
1562 || node == NODE_TYPE_SETS
1563 || node == NODE_TYPE_TAGS
1564 || node == NODE_TYPE_TITLE_MOVIES
1565 || node == NODE_TYPE_TITLE_TVSHOWS
1566 || node == NODE_TYPE_TITLE_MUSICVIDEOS
1567 || node == NODE_TYPE_RECENTLY_ADDED_EPISODES
1568 || node == NODE_TYPE_RECENTLY_ADDED_MOVIES
1569 || node == NODE_TYPE_RECENTLY_ADDED_MUSICVIDEOS)
1570 filterWatched = true;
1571 if (!items.IsVideoDb())
1572 filterWatched = true;
1573 if (items.GetContent() == "tvshows" &&
1574 (items.IsSmartPlayList() || items.IsLibraryFolder()))
1575 node = NODE_TYPE_TITLE_TVSHOWS; // so that the check below works
1577 int watchMode = CMediaSettings::Get().GetWatchedMode(m_vecItems->GetContent());
1579 for (int i = 0; i < items.Size(); i++)
1581 CFileItemPtr item = items.Get(i);
1583 if(item->HasVideoInfoTag() && (node == NODE_TYPE_TITLE_TVSHOWS || node == NODE_TYPE_SEASONS))
1585 if (watchMode == WatchedModeUnwatched)
1586 item->GetVideoInfoTag()->m_iEpisode = (int)item->GetProperty("unwatchedepisodes").asInteger();
1587 if (watchMode == WatchedModeWatched)
1588 item->GetVideoInfoTag()->m_iEpisode = (int)item->GetProperty("watchedepisodes").asInteger();
1589 if (watchMode == WatchedModeAll)
1590 item->GetVideoInfoTag()->m_iEpisode = (int)item->GetProperty("totalepisodes").asInteger();
1591 item->SetProperty("numepisodes", item->GetVideoInfoTag()->m_iEpisode);
1597 if((watchMode==WatchedModeWatched && item->GetVideoInfoTag()->m_playCount== 0)
1598 || (watchMode==WatchedModeUnwatched && item->GetVideoInfoTag()->m_playCount > 0))
1607 if(node == NODE_TYPE_TITLE_TVSHOWS || node == NODE_TYPE_SEASONS)
1609 // the watched filter may change the "numepisodes" property which is reflected in the TV_SHOWS and SEASONS nodes
1610 // therefore, the items labels have to be refreshed, and possibly the list needs resorting as well.
1611 items.ClearSortState(); // this is needed to force resorting even if sort method did not change
1612 FormatAndSort(items);
1618 bool CGUIWindowVideoNav::GetItemsForTag(const CStdString &strHeading, const std::string &type, CFileItemList &items, int idTag /* = -1 */, bool showAll /* = true */)
1620 CVideoDatabase videodb;
1621 if (!videodb.Open())
1624 MediaType mediaType = MediaTypeNone;
1625 std::string baseDir = "videodb://";
1626 std::string idColumn;
1627 if (type.compare("movie") == 0)
1629 mediaType = MediaTypeMovie;
1630 baseDir += "movies";
1631 idColumn = "idMovie";
1633 else if (type.compare("tvshow") == 0)
1635 mediaType = MediaTypeTvShow;
1636 baseDir += "tvshows";
1637 idColumn = "idShow";
1639 else if (type.compare("musicvideo") == 0)
1641 mediaType = MediaTypeMusicVideo;
1642 baseDir += "musicvideos";
1643 idColumn = "idMVideo";
1646 baseDir += "/titles/";
1647 CVideoDbUrl videoUrl;
1648 if (!videoUrl.FromString(baseDir))
1651 CVideoDatabase::Filter filter;
1655 videoUrl.AddOption("tagid", idTag);
1657 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());
1660 CFileItemList listItems;
1661 if (!videodb.GetSortedVideos(mediaType, videoUrl.ToString(), SortDescription(), listItems, filter) || listItems.Size() <= 0)
1664 CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1668 listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1671 dialog->SetMultiSelection(true);
1672 dialog->SetHeading(strHeading);
1673 dialog->SetItems(&listItems);
1674 dialog->EnableButton(true, 186);
1677 items.Copy(dialog->GetSelectedItems());
1678 return items.Size() > 0;
1681 CStdString CGUIWindowVideoNav::GetLocalizedType(const std::string &strType)
1683 if (strType == "movie" || strType == "movies")
1684 return g_localizeStrings.Get(20342);
1685 else if (strType == "tvshow" || strType == "tvshows")
1686 return g_localizeStrings.Get(20343);
1687 else if (strType == "episode" || strType == "episodes")
1688 return g_localizeStrings.Get(20359);
1689 else if (strType == "musicvideo" || strType == "musicvideos")
1690 return g_localizeStrings.Get(20391);