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 "GUIDialogVideoInfo.h"
22 #include "Application.h"
23 #include "guilib/GUIWindow.h"
25 #include "guilib/GUIImage.h"
26 #include "utils/StringUtils.h"
27 #include "utils/URIUtils.h"
28 #include "video/windows/GUIWindowVideoNav.h"
29 #include "dialogs/GUIDialogFileBrowser.h"
30 #include "video/VideoInfoScanner.h"
31 #include "ApplicationMessenger.h"
32 #include "video/VideoInfoTag.h"
33 #include "guilib/GUIKeyboardFactory.h"
34 #include "guilib/GUIWindowManager.h"
35 #include "dialogs/GUIDialogOK.h"
36 #include "dialogs/GUIDialogYesNo.h"
37 #include "dialogs/GUIDialogSelect.h"
38 #include "dialogs/GUIDialogProgress.h"
39 #include "filesystem/File.h"
41 #include "storage/MediaManager.h"
42 #include "utils/AsyncFileCopy.h"
43 #include "profiles/ProfilesManager.h"
44 #include "settings/AdvancedSettings.h"
45 #include "settings/MediaSourceSettings.h"
46 #include "settings/Settings.h"
47 #include "guilib/Key.h"
48 #include "guilib/LocalizeStrings.h"
49 #include "GUIUserMessages.h"
50 #include "TextureCache.h"
51 #include "music/MusicDatabase.h"
53 #include "video/VideoThumbLoader.h"
54 #include "filesystem/Directory.h"
55 #include "filesystem/VideoDatabaseDirectory.h"
56 #include "filesystem/VideoDatabaseDirectory/QueryParams.h"
58 #include "network/upnp/UPnP.h"
60 #include "utils/FileUtils.h"
63 using namespace XFILE::VIDEODATABASEDIRECTORY;
64 using namespace XFILE;
66 #define CONTROL_IMAGE 3
67 #define CONTROL_TEXTAREA 4
68 #define CONTROL_BTN_TRACKS 5
69 #define CONTROL_BTN_REFRESH 6
70 #define CONTROL_BTN_PLAY 8
71 #define CONTROL_BTN_RESUME 9
72 #define CONTROL_BTN_GET_THUMB 10
73 #define CONTROL_BTN_PLAY_TRAILER 11
74 #define CONTROL_BTN_GET_FANART 12
75 #define CONTROL_BTN_DIRECTOR 13
77 #define CONTROL_LIST 50
79 // predicate used by sorting and set_difference
80 bool compFileItemsByDbId(const CFileItemPtr& lhs, const CFileItemPtr& rhs)
82 return lhs->HasVideoInfoTag() && rhs->HasVideoInfoTag() && lhs->GetVideoInfoTag()->m_iDbId < rhs->GetVideoInfoTag()->m_iDbId;
85 CGUIDialogVideoInfo::CGUIDialogVideoInfo(void)
86 : CGUIDialog(WINDOW_DIALOG_VIDEO_INFO, "DialogVideoInfo.xml")
87 , m_movieItem(new CFileItem)
91 m_hasUpdatedThumb = false;
92 m_castList = new CFileItemList;
93 m_loadType = KEEP_IN_MEMORY;
96 CGUIDialogVideoInfo::~CGUIDialogVideoInfo(void)
101 bool CGUIDialogVideoInfo::OnMessage(CGUIMessage& message)
103 switch ( message.GetMessage() )
105 case GUI_MSG_WINDOW_DEINIT:
111 case GUI_MSG_CLICKED:
113 int iControl = message.GetSenderId();
114 if (iControl == CONTROL_BTN_REFRESH)
116 if (m_movieItem->GetVideoInfoTag()->m_iSeason < 0 && !m_movieItem->GetVideoInfoTag()->m_strShowTitle.empty()) // tv show
118 bool bCanceled=false;
119 if (CGUIDialogYesNo::ShowAndGetInput(20377,20378,-1,-1,bCanceled))
121 m_bRefreshAll = true;
125 db.SetPathHash(m_movieItem->GetVideoInfoTag()->m_strPath,"");
130 m_bRefreshAll = false;
139 else if (iControl == CONTROL_BTN_TRACKS)
141 m_bViewReview = !m_bViewReview;
144 else if (iControl == CONTROL_BTN_PLAY)
148 else if (iControl == CONTROL_BTN_RESUME)
152 else if (iControl == CONTROL_BTN_GET_THUMB)
156 else if (iControl == CONTROL_BTN_PLAY_TRAILER)
160 else if (iControl == CONTROL_BTN_GET_FANART)
164 else if (iControl == CONTROL_BTN_DIRECTOR)
166 CStdString strDirector = StringUtils::Join(m_movieItem->GetVideoInfoTag()->m_director, g_advancedSettings.m_videoItemSeparator);
167 OnSearch(strDirector);
169 else if (iControl == CONTROL_LIST)
171 int iAction = message.GetParam1();
172 if (ACTION_SELECT_ITEM == iAction || ACTION_MOUSE_LEFT_CLICK == iAction)
174 CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl);
176 int iItem = msg.GetParam1();
177 if (iItem < 0 || iItem >= m_castList->Size())
179 CStdString strItem = m_castList->Get(iItem)->GetLabel();
180 CStdString strFind = StringUtils::Format(" %s ",g_localizeStrings.Get(20347).c_str());
181 size_t iPos = strItem.find(strFind);
182 if (iPos == std::string::npos)
183 iPos = strItem.size();
184 CStdString tmp = strItem.substr(0, iPos);
190 case GUI_MSG_NOTIFY_ALL:
192 if (IsActive() && message.GetParam1() == GUI_MSG_UPDATE_ITEM && message.GetItem())
194 CFileItemPtr item = boost::static_pointer_cast<CFileItem>(message.GetItem());
195 if (item && m_movieItem->GetPath().Equals(item->GetPath()))
196 { // Just copy over the stream details and the thumb if we don't already have one
197 if (!m_movieItem->HasArt("thumb"))
198 m_movieItem->SetArt("thumb", item->GetArt("thumb"));
199 m_movieItem->GetVideoInfoTag()->m_streamDetails = item->GetVideoInfoTag()->m_streamDetails;
206 return CGUIDialog::OnMessage(message);
209 void CGUIDialogVideoInfo::OnInitWindow()
212 m_bRefreshAll = true;
213 m_hasUpdatedThumb = false;
214 m_bViewReview = true;
216 CVideoDatabase database;
217 ADDON::ScraperPtr scraper;
221 scraper = database.GetScraperForPath(m_movieItem->GetVideoInfoTag()->GetPath());
225 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_REFRESH, (CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser) && !StringUtils::StartsWithNoCase(m_movieItem->GetVideoInfoTag()->m_strIMDBNumber, "xx") && scraper);
226 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_GET_THUMB, (CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser) && !StringUtils::StartsWithNoCase(m_movieItem->GetVideoInfoTag()->m_strIMDBNumber.c_str() + 2, "plugin"));
228 VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)m_movieItem->GetVideoContentType();
229 if (type == VIDEODB_CONTENT_TVSHOWS || type == VIDEODB_CONTENT_MOVIES)
230 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_GET_FANART, (CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser) && !StringUtils::StartsWithNoCase(m_movieItem->GetVideoInfoTag()->m_strIMDBNumber.c_str() + 2, "plugin"));
232 CONTROL_DISABLE(CONTROL_BTN_GET_FANART);
236 CGUIDialog::OnInitWindow();
239 bool CGUIDialogVideoInfo::OnAction(const CAction &action)
241 if (action.GetID() == ACTION_SHOW_INFO)
246 return CGUIDialog::OnAction(action);
249 void CGUIDialogVideoInfo::SetMovie(const CFileItem *item)
251 *m_movieItem = *item;
252 // setup cast list + determine type. We need to do this here as it makes
253 // sure that content type (among other things) is set correctly for the
254 // old fixed id labels that we have floating around (they may be using
255 // content type to determine visibility, so we'll set the wrong label)
257 VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)m_movieItem->GetVideoContentType();
258 if (type == VIDEODB_CONTENT_MUSICVIDEOS)
260 CMusicDatabase database;
262 const std::vector<std::string> &artists = m_movieItem->GetVideoInfoTag()->m_artist;
263 for (std::vector<std::string>::const_iterator it = artists.begin(); it != artists.end(); ++it)
265 int idArtist = database.GetArtistByName(*it);
266 CStdString thumb = database.GetArtForItem(idArtist, "artist", "thumb");
267 CFileItemPtr item(new CFileItem(*it));
269 item->SetArt("thumb", thumb);
270 item->SetIconImage("DefaultArtist.png");
271 m_castList->Add(item);
273 m_castList->SetContent("musicvideos");
276 { // movie/show/episode
277 for (CVideoInfoTag::iCast it = m_movieItem->GetVideoInfoTag()->m_cast.begin(); it != m_movieItem->GetVideoInfoTag()->m_cast.end(); ++it)
279 CStdString character;
280 if (it->strRole.empty())
281 character = it->strName;
283 character = StringUtils::Format("%s %s %s", it->strName.c_str(), g_localizeStrings.Get(20347).c_str(), it->strRole.c_str());
284 CFileItemPtr item(new CFileItem(it->strName));
285 if (!it->thumb.empty())
286 item->SetArt("thumb", it->thumb);
287 else if (CSettings::Get().GetBool("videolibrary.actorthumbs"))
288 { // backward compatibility
289 CStdString thumb = CScraperUrl::GetThumbURL(it->thumbUrl.GetFirstThumb());
292 item->SetArt("thumb", thumb);
293 CTextureCache::Get().BackgroundCacheImage(thumb);
296 item->SetIconImage("DefaultActor.png");
297 item->SetLabel(character);
298 m_castList->Add(item);
301 if (type == VIDEODB_CONTENT_TVSHOWS)
303 m_castList->SetContent("tvshows");
304 // special case stuff for shows (not currently retrieved from the library in filemode (ref: GetTvShowInfo vs GetTVShowsByWhere)
305 m_movieItem->m_dateTime = m_movieItem->GetVideoInfoTag()->m_premiered;
306 if(m_movieItem->GetVideoInfoTag()->m_iYear == 0 && m_movieItem->m_dateTime.IsValid())
307 m_movieItem->GetVideoInfoTag()->m_iYear = m_movieItem->m_dateTime.GetYear();
308 m_movieItem->SetProperty("totalepisodes", m_movieItem->GetVideoInfoTag()->m_iEpisode);
309 m_movieItem->SetProperty("numepisodes", m_movieItem->GetVideoInfoTag()->m_iEpisode); // info view has no concept of current watched/unwatched filter as we could come here from files view, but set for consistency
310 m_movieItem->SetProperty("watchedepisodes", m_movieItem->GetVideoInfoTag()->m_playCount);
311 m_movieItem->SetProperty("unwatchedepisodes", m_movieItem->GetVideoInfoTag()->m_iEpisode - m_movieItem->GetVideoInfoTag()->m_playCount);
312 m_movieItem->GetVideoInfoTag()->m_playCount = (m_movieItem->GetVideoInfoTag()->m_iEpisode == m_movieItem->GetVideoInfoTag()->m_playCount) ? 1 : 0;
314 else if (type == VIDEODB_CONTENT_EPISODES)
316 m_castList->SetContent("episodes");
317 // special case stuff for episodes (not currently retrieved from the library in filemode (ref: GetEpisodeInfo vs GetEpisodesByWhere)
318 m_movieItem->m_dateTime = m_movieItem->GetVideoInfoTag()->m_firstAired;
319 if(m_movieItem->GetVideoInfoTag()->m_iYear == 0 && m_movieItem->m_dateTime.IsValid())
320 m_movieItem->GetVideoInfoTag()->m_iYear = m_movieItem->m_dateTime.GetYear();
321 // retrieve the season thumb.
322 // TODO: should we use the thumbloader for this?
326 if (m_movieItem->GetVideoInfoTag()->m_iSeason > -1)
328 int seasonID = m_movieItem->GetVideoInfoTag()->m_iIdSeason;
330 seasonID = db.GetSeasonId(m_movieItem->GetVideoInfoTag()->m_iIdShow,
331 m_movieItem->GetVideoInfoTag()->m_iSeason);
332 CGUIListItem::ArtMap thumbs;
333 if (db.GetArtForItem(seasonID, "season", thumbs))
335 for (CGUIListItem::ArtMap::iterator i = thumbs.begin(); i != thumbs.end(); i++)
336 m_movieItem->SetArt("season." + i->first, i->second);
342 else if (type == VIDEODB_CONTENT_MOVIES)
344 m_castList->SetContent("movies");
346 // local trailers should always override non-local, so check
347 // for a local one if the registered trailer is online
348 if (m_movieItem->GetVideoInfoTag()->m_strTrailer.empty() ||
349 URIUtils::IsInternetStream(m_movieItem->GetVideoInfoTag()->m_strTrailer))
351 CStdString localTrailer = m_movieItem->FindTrailer();
352 if (!localTrailer.empty())
354 m_movieItem->GetVideoInfoTag()->m_strTrailer = localTrailer;
355 CVideoDatabase database;
358 database.SetSingleValue(VIDEODB_CONTENT_MOVIES, VIDEODB_ID_TRAILER,
359 m_movieItem->GetVideoInfoTag()->m_iDbId,
360 m_movieItem->GetVideoInfoTag()->m_strTrailer);
362 CUtil::DeleteVideoDatabaseDirectoryCache();
368 CVideoThumbLoader loader;
369 loader.LoadItem(m_movieItem.get());
372 void CGUIDialogVideoInfo::Update()
374 // setup plot text area
375 CStdString strTmp = m_movieItem->GetVideoInfoTag()->m_strPlot;
376 if (!(!m_movieItem->GetVideoInfoTag()->m_strShowTitle.empty() && m_movieItem->GetVideoInfoTag()->m_iSeason == 0)) // dont apply to tvshows
377 if (m_movieItem->GetVideoInfoTag()->m_playCount == 0 && !CSettings::Get().GetBool("videolibrary.showunwatchedplots"))
378 strTmp = g_localizeStrings.Get(20370);
380 StringUtils::Trim(strTmp);
381 SetLabel(CONTROL_TEXTAREA, strTmp);
383 CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST, 0, 0, m_castList);
386 if (GetControl(CONTROL_BTN_TRACKS)) // if no CONTROL_BTN_TRACKS found - allow skinner full visibility control over CONTROL_TEXTAREA and CONTROL_LIST
390 if (!m_movieItem->GetVideoInfoTag()->m_artist.empty())
392 SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 133);
396 SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 206);
399 SET_CONTROL_HIDDEN(CONTROL_LIST);
400 SET_CONTROL_VISIBLE(CONTROL_TEXTAREA);
404 SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 207);
406 SET_CONTROL_HIDDEN(CONTROL_TEXTAREA);
407 SET_CONTROL_VISIBLE(CONTROL_LIST);
411 // Check for resumability
412 if (m_movieItem->GetVideoInfoTag()->m_resumePoint.timeInSeconds > 0.0)
413 CONTROL_ENABLE(CONTROL_BTN_RESUME);
415 CONTROL_DISABLE(CONTROL_BTN_RESUME);
417 CONTROL_ENABLE(CONTROL_BTN_PLAY);
419 // update the thumbnail
420 const CGUIControl* pControl = GetControl(CONTROL_IMAGE);
423 CGUIImage* pImageControl = (CGUIImage*)pControl;
424 pImageControl->FreeResources();
425 pImageControl->SetFileName(m_movieItem->GetArt("thumb"));
427 // tell our GUI to completely reload all controls (as some of them
428 // are likely to have had this image in use so will need refreshing)
429 if (m_hasUpdatedThumb)
431 CGUIMessage reload(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
432 g_windowManager.SendMessage(reload);
436 bool CGUIDialogVideoInfo::NeedRefresh() const
441 bool CGUIDialogVideoInfo::RefreshAll() const
443 return m_bRefreshAll;
446 /// \brief Search the current directory for a string got from the virtual keyboard
447 void CGUIDialogVideoInfo::OnSearch(CStdString& strSearch)
449 CGUIDialogProgress *progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
452 progress->SetHeading(194);
453 progress->SetLine(0, strSearch);
454 progress->SetLine(1, "");
455 progress->SetLine(2, "");
456 progress->StartModal();
457 progress->Progress();
460 DoSearch(strSearch, items);
467 CGUIDialogSelect* pDlgSelect = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
469 pDlgSelect->SetHeading(283);
471 for (int i = 0; i < (int)items.Size(); i++)
473 CFileItemPtr pItem = items[i];
474 pDlgSelect->Add(pItem->GetLabel());
477 pDlgSelect->DoModal();
479 int iItem = pDlgSelect->GetSelectedLabel();
483 CFileItem* pSelItem = new CFileItem(*items[iItem]);
485 OnSearchItemFound(pSelItem);
491 CGUIDialogOK::ShowAndGetInput(194, 284, 0, 0);
495 /// \brief Make the actual search for the OnSearch function.
496 /// \param strSearch The search string
497 /// \param items Items Found
498 void CGUIDialogVideoInfo::DoSearch(CStdString& strSearch, CFileItemList& items)
504 CFileItemList movies;
505 db.GetMoviesByActor(strSearch, movies);
506 for (int i = 0; i < movies.Size(); ++i)
508 CStdString label = movies[i]->GetVideoInfoTag()->m_strTitle;
509 if (movies[i]->GetVideoInfoTag()->m_iYear > 0)
510 label += StringUtils::Format(" (%i)", movies[i]->GetVideoInfoTag()->m_iYear);
511 movies[i]->SetLabel(label);
513 CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20338) + "] ", items);
515 db.GetTvShowsByActor(strSearch, movies);
516 for (int i = 0; i < movies.Size(); ++i)
518 CStdString label = movies[i]->GetVideoInfoTag()->m_strShowTitle;
519 if (movies[i]->GetVideoInfoTag()->m_iYear > 0)
520 label += StringUtils::Format(" (%i)", movies[i]->GetVideoInfoTag()->m_iYear);
521 movies[i]->SetLabel(label);
523 CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20364) + "] ", items);
525 db.GetEpisodesByActor(strSearch, movies);
526 for (int i = 0; i < movies.Size(); ++i)
528 CStdString label = movies[i]->GetVideoInfoTag()->m_strTitle + " (" + movies[i]->GetVideoInfoTag()->m_strShowTitle + ")";
529 movies[i]->SetLabel(label);
531 CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20359) + "] ", items);
533 db.GetMusicVideosByArtist(strSearch, movies);
534 for (int i = 0; i < movies.Size(); ++i)
536 CStdString label = StringUtils::Join(movies[i]->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator) + " - " + movies[i]->GetVideoInfoTag()->m_strTitle;
537 if (movies[i]->GetVideoInfoTag()->m_iYear > 0)
538 label += StringUtils::Format(" (%i)", movies[i]->GetVideoInfoTag()->m_iYear);
539 movies[i]->SetLabel(label);
541 CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20391) + "] ", items);
545 /// \brief React on the selected search item
546 /// \param pItem Search result item
547 void CGUIDialogVideoInfo::OnSearchItemFound(const CFileItem* pItem)
549 VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)pItem->GetVideoContentType();
555 CVideoInfoTag movieDetails;
556 if (type == VIDEODB_CONTENT_MOVIES)
557 db.GetMovieInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
558 if (type == VIDEODB_CONTENT_EPISODES)
559 db.GetEpisodeInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
560 if (type == VIDEODB_CONTENT_TVSHOWS)
561 db.GetTvShowInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
562 if (type == VIDEODB_CONTENT_MUSICVIDEOS)
563 db.GetMusicVideoInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
566 CFileItem item(*pItem);
567 *item.GetVideoInfoTag() = movieDetails;
569 // refresh our window entirely
574 void CGUIDialogVideoInfo::ClearCastList()
576 CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_LIST);
581 void CGUIDialogVideoInfo::Play(bool resume)
583 if (!m_movieItem->GetVideoInfoTag()->m_strEpisodeGuide.empty())
585 CStdString strPath = StringUtils::Format("videodb://tvshows/titles/%i/",m_movieItem->GetVideoInfoTag()->m_iDbId);
587 g_windowManager.ActivateWindow(WINDOW_VIDEO_NAV,strPath);
591 CFileItem movie(*m_movieItem->GetVideoInfoTag());
592 if (m_movieItem->GetVideoInfoTag()->m_strFileNameAndPath.empty())
593 movie.SetPath(m_movieItem->GetPath());
594 CGUIWindowVideoNav* pWindow = (CGUIWindowVideoNav*)g_windowManager.GetWindow(WINDOW_VIDEO_NAV);
600 movie.m_lStartOffset = STARTOFFSET_RESUME;
601 else if (!CGUIWindowVideoBase::ShowResumeMenu(movie))
603 // The Resume dialog was closed without any choice
607 pWindow->PlayMovie(&movie);
611 string CGUIDialogVideoInfo::ChooseArtType(const CFileItem &videoItem, map<string, string> ¤tArt)
614 CGUIDialogSelect *dialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
615 if (!dialog || !videoItem.HasVideoInfoTag())
619 dialog->SetHeading(13511);
621 dialog->SetUseDetails(true);
622 dialog->EnableButton(true, 13516);
627 vector<string> artTypes = CVideoThumbLoader::GetArtTypes(videoItem.GetVideoInfoTag()->m_type);
629 // add in any stored art for this item that is non-empty.
630 db.GetArtForItem(videoItem.GetVideoInfoTag()->m_iDbId, videoItem.GetVideoInfoTag()->m_type, currentArt);
631 for (CGUIListItem::ArtMap::iterator i = currentArt.begin(); i != currentArt.end(); ++i)
633 if (!i->second.empty() && find(artTypes.begin(), artTypes.end(), i->first) == artTypes.end())
634 artTypes.push_back(i->first);
637 // add any art types that exist for other media items of the same type
638 vector<string> dbArtTypes;
639 db.GetArtTypes(videoItem.GetVideoInfoTag()->m_type, dbArtTypes);
640 for (vector<string>::const_iterator it = dbArtTypes.begin(); it != dbArtTypes.end(); it++)
642 if (find(artTypes.begin(), artTypes.end(), *it) == artTypes.end())
643 artTypes.push_back(*it);
646 for (vector<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
649 CFileItemPtr item(new CFileItem(type, "false"));
650 item->SetLabel(type);
651 if (videoItem.HasArt(type))
652 item->SetArt("thumb", videoItem.GetArt(type));
656 dialog->SetItems(&items);
659 if (dialog->IsButtonPressed())
661 // Get the new artwork name
662 CStdString strArtworkName;
663 if (!CGUIKeyboardFactory::ShowAndGetInput(strArtworkName, g_localizeStrings.Get(13516), false))
666 return strArtworkName;
669 return dialog->GetSelectedItem()->GetLabel();
672 void CGUIDialogVideoInfo::OnGetArt()
674 map<string, string> currentArt;
675 string type = ChooseArtType(*m_movieItem, currentArt);
679 // TODO: this can be removed once these are unified.
680 if (type == "fanart")
687 if (m_movieItem->HasArt(type))
689 CFileItemPtr item(new CFileItem("thumb://Current", false));
690 item->SetArt("thumb", m_movieItem->GetArt(type));
691 item->SetLabel(g_localizeStrings.Get(13512));
694 else if ((type == "poster" || type == "banner") && currentArt.find("thumb") != currentArt.end())
695 { // add the 'thumb' type in
696 CFileItemPtr item(new CFileItem("thumb://Thumb", false));
697 item->SetArt("thumb", currentArt["thumb"]);
698 item->SetLabel(g_localizeStrings.Get(13512));
702 // Grab the thumbnails from the web
703 vector<CStdString> thumbs;
704 int season = (m_movieItem->GetVideoInfoTag()->m_type == "season") ? m_movieItem->GetVideoInfoTag()->m_iSeason : -1;
705 m_movieItem->GetVideoInfoTag()->m_strPictureURL.GetThumbURLs(thumbs, type, season);
707 for (unsigned int i = 0; i < thumbs.size(); ++i)
709 CStdString strItemPath = StringUtils::Format("thumb://Remote%i", i);
710 CFileItemPtr item(new CFileItem(strItemPath, false));
711 item->SetArt("thumb", thumbs[i]);
712 item->SetIconImage("DefaultPicture.png");
713 item->SetLabel(g_localizeStrings.Get(13513));
715 // TODO: Do we need to clear the cached image?
716 // CTextureCache::Get().ClearCachedImage(thumb);
720 CStdString localThumb = CVideoThumbLoader::GetLocalArt(*m_movieItem, type);
721 if (!localThumb.empty())
723 CFileItemPtr item(new CFileItem("thumb://Local", false));
724 item->SetArt("thumb", localThumb);
725 item->SetLabel(g_localizeStrings.Get(13514));
729 { // no local thumb exists, so we are just using the IMDb thumb or cached thumb
730 // which is probably the IMDb thumb. These could be wrong, so allow the user
731 // to delete the incorrect thumb
732 CFileItemPtr item(new CFileItem("thumb://None", false));
733 item->SetIconImage("DefaultVideo.png");
734 item->SetLabel(g_localizeStrings.Get(13515));
739 VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
740 AddItemPathToFileBrowserSources(sources, *m_movieItem);
741 g_mediaManager.GetLocalDrives(sources);
742 if (CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result) &&
743 result != "thumb://Current") // user didn't choose the one they have
746 if (StringUtils::StartsWith(result, "thumb://Remote"))
748 int number = atoi(result.substr(14).c_str());
749 newThumb = thumbs[number];
751 else if (result == "thumb://Thumb")
752 newThumb = currentArt["thumb"];
753 else if (result == "thumb://Local")
754 newThumb = localThumb;
755 else if (CFile::Exists(result))
760 // update thumb in the database
764 db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, type, newThumb);
767 CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
768 m_movieItem->SetArt(type, newThumb);
769 if (m_movieItem->HasProperty("set_folder_thumb"))
770 { // have a folder thumb to set as well
771 VIDEO::CVideoInfoScanner::ApplyThumbToFolder(m_movieItem->GetProperty("set_folder_thumb").asString(), newThumb);
773 m_hasUpdatedThumb = true;
780 // re-open the art selection dialog as we come back from
781 // the image selection dialog
785 // Allow user to select a Fanart
786 void CGUIDialogVideoInfo::OnGetFanart()
790 CFileItem item(*m_movieItem->GetVideoInfoTag());
791 if (item.HasArt("fanart"))
793 CFileItemPtr itemCurrent(new CFileItem("fanart://Current",false));
794 itemCurrent->SetArt("thumb", item.GetArt("fanart"));
795 itemCurrent->SetLabel(g_localizeStrings.Get(20440));
796 items.Add(itemCurrent);
799 // ensure the fanart is unpacked
800 m_movieItem->GetVideoInfoTag()->m_fanart.Unpack();
802 // Grab the thumbnails from the web
803 for (unsigned int i = 0; i < m_movieItem->GetVideoInfoTag()->m_fanart.GetNumFanarts(); i++)
805 CStdString strItemPath = StringUtils::Format("fanart://Remote%i",i);
806 CFileItemPtr item(new CFileItem(strItemPath, false));
807 CStdString thumb = m_movieItem->GetVideoInfoTag()->m_fanart.GetPreviewURL(i);
808 item->SetArt("thumb", CTextureUtils::GetWrappedThumbURL(thumb));
809 item->SetIconImage("DefaultPicture.png");
810 item->SetLabel(g_localizeStrings.Get(20441));
812 // TODO: Do we need to clear the cached image?
813 // CTextureCache::Get().ClearCachedImage(thumb);
817 CStdString strLocal = item.GetLocalFanart();
818 if (!strLocal.empty())
820 CFileItemPtr itemLocal(new CFileItem("fanart://Local",false));
821 itemLocal->SetArt("thumb", strLocal);
822 itemLocal->SetLabel(g_localizeStrings.Get(20438));
824 // TODO: Do we need to clear the cached image?
825 CTextureCache::Get().ClearCachedImage(strLocal);
826 items.Add(itemLocal);
830 CFileItemPtr itemNone(new CFileItem("fanart://None", false));
831 itemNone->SetIconImage("DefaultVideo.png");
832 itemNone->SetLabel(g_localizeStrings.Get(20439));
837 VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
838 AddItemPathToFileBrowserSources(sources, item);
839 g_mediaManager.GetLocalDrives(sources);
841 if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) || result.Equals("fanart://Current"))
842 return; // user cancelled
844 if (result.Equals("fanart://Local"))
847 if (StringUtils::StartsWith(result, "fanart://Remote"))
849 int iFanart = atoi(result.substr(15).c_str());
850 // set new primary fanart, and update our database accordingly
851 m_movieItem->GetVideoInfoTag()->m_fanart.SetPrimaryFanart(iFanart);
855 db.UpdateFanart(*m_movieItem, (VIDEODB_CONTENT_TYPE)m_movieItem->GetVideoContentType());
858 result = m_movieItem->GetVideoInfoTag()->m_fanart.GetImageURL();
860 else if (result.Equals("fanart://None") || !CFile::Exists(result))
863 // set the fanart image
864 if (flip && !result.empty())
865 result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
869 db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, "fanart", result);
873 CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
874 m_movieItem->SetArt("fanart", result);
875 m_hasUpdatedThumb = true;
881 void CGUIDialogVideoInfo::PlayTrailer()
884 item.SetPath(m_movieItem->GetVideoInfoTag()->m_strTrailer);
885 *item.GetVideoInfoTag() = *m_movieItem->GetVideoInfoTag();
886 item.GetVideoInfoTag()->m_streamDetails.Reset();
887 item.GetVideoInfoTag()->m_strTitle = StringUtils::Format("%s (%s)",
888 m_movieItem->GetVideoInfoTag()->m_strTitle.c_str(),
889 g_localizeStrings.Get(20410).c_str());
890 CVideoThumbLoader::SetArt(item, m_movieItem->GetArt());
891 item.GetVideoInfoTag()->m_iDbId = -1;
892 item.GetVideoInfoTag()->m_iFileId = -1;
897 if (item.IsPlayList())
898 CApplicationMessenger::Get().MediaPlay(item);
900 CApplicationMessenger::Get().PlayFile(item);
903 void CGUIDialogVideoInfo::SetLabel(int iControl, const CStdString &strLabel)
905 if (strLabel.empty())
907 SET_CONTROL_LABEL(iControl, 416); // "Not available"
911 SET_CONTROL_LABEL(iControl, strLabel);
915 std::string CGUIDialogVideoInfo::GetThumbnail() const
917 return m_movieItem->GetArt("thumb");
920 void CGUIDialogVideoInfo::AddItemPathToFileBrowserSources(VECSOURCES &sources, const CFileItem &item)
922 if (!item.HasVideoInfoTag())
925 CStdString itemDir = item.GetVideoInfoTag()->m_basePath;
929 itemDir = item.GetVideoInfoTag()->GetPath();
931 CFileItem itemTmp(itemDir, false);
932 if (itemTmp.IsVideo())
933 itemDir = URIUtils::GetParentPath(itemDir);
935 if (!itemDir.empty() && CDirectory::Exists(itemDir))
937 CMediaSource itemSource;
938 itemSource.strName = g_localizeStrings.Get(36041);
939 itemSource.strPath = itemDir;
940 sources.push_back(itemSource);
944 int CGUIDialogVideoInfo::ManageVideoItem(const CFileItemPtr &item)
946 if (item == NULL || !item->IsVideoDb() || !item->HasVideoInfoTag() || item->GetVideoInfoTag()->m_iDbId < 0)
949 CVideoDatabase database;
950 if (!database.Open())
953 const std::string &type = item->GetVideoInfoTag()->m_type;
954 int dbId = item->GetVideoInfoTag()->m_iDbId;
956 CContextButtons buttons;
957 if (type == "movie" || type == "set" ||
958 type == "tvshow" || type == "episode" ||
959 type == "musicvideo")
960 buttons.Add(CONTEXT_BUTTON_EDIT, 16105);
962 if (type == "movie" || type == "tvshow")
963 buttons.Add(CONTEXT_BUTTON_EDIT_SORTTITLE, 16107);
965 if (item->m_bIsFolder)
967 // Have both options for folders since we don't know whether all childs are watched/unwatched
968 buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
969 buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103); //Mark as Watched
973 if (item->GetOverlayImage().Equals("OverlayWatched.png"))
974 buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
976 buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103); //Mark as Watched
981 // only show link/unlink if there are tvshows available
982 if (database.HasContent(VIDEODB_CONTENT_TVSHOWS))
984 buttons.Add(CONTEXT_BUTTON_LINK_MOVIE, 20384);
985 if (database.IsLinkedToTvshow(dbId))
986 buttons.Add(CONTEXT_BUTTON_UNLINK_MOVIE, 20385);
989 // set or change movie set the movie belongs to
990 buttons.Add(CONTEXT_BUTTON_SET_MOVIESET, 20465);
993 if (type == "episode" &&
994 item->GetVideoInfoTag()->m_iBookmarkId > 0)
995 buttons.Add(CONTEXT_BUTTON_UNLINK_BOOKMARK, 20405);
998 if (item->m_bIsFolder && type == "set")
1000 buttons.Add(CONTEXT_BUTTON_SET_MOVIESET_ART, 13511);
1001 buttons.Add(CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS, 20465);
1005 if (item->m_bIsFolder && type == "tag")
1007 CVideoDbUrl videoUrl;
1008 if (videoUrl.FromString(item->GetPath()))
1010 const std::string &mediaType = videoUrl.GetItemType();
1012 buttons.Add(CONTEXT_BUTTON_TAGS_ADD_ITEMS, StringUtils::Format(g_localizeStrings.Get(20460), GetLocalizedVideoType(mediaType).c_str()));
1013 buttons.Add(CONTEXT_BUTTON_TAGS_REMOVE_ITEMS, StringUtils::Format(g_localizeStrings.Get(20461), GetLocalizedVideoType(mediaType).c_str()));
1017 buttons.Add(CONTEXT_BUTTON_DELETE, 646);
1019 bool result = false;
1020 int button = CGUIDialogContextMenu::ShowAndGetChoice(buttons);
1023 switch ((CONTEXT_BUTTON)button)
1025 case CONTEXT_BUTTON_EDIT:
1026 result = UpdateVideoItemTitle(item);
1029 case CONTEXT_BUTTON_EDIT_SORTTITLE:
1030 result = UpdateVideoItemSortTitle(item);
1033 case CONTEXT_BUTTON_MARK_WATCHED:
1034 result = MarkWatched(item, true);
1037 case CONTEXT_BUTTON_MARK_UNWATCHED:
1038 result = MarkWatched(item, false);
1041 case CONTEXT_BUTTON_LINK_MOVIE:
1042 result = LinkMovieToTvShow(item, false, database);
1045 case CONTEXT_BUTTON_UNLINK_MOVIE:
1046 result = LinkMovieToTvShow(item, true, database);
1049 case CONTEXT_BUTTON_SET_MOVIESET:
1051 CFileItemPtr selectedSet;
1052 if (GetSetForMovie(item.get(), selectedSet))
1053 result = SetMovieSet(item.get(), selectedSet.get());
1057 case CONTEXT_BUTTON_UNLINK_BOOKMARK:
1058 database.DeleteBookMarkForEpisode(*item->GetVideoInfoTag());
1062 case CONTEXT_BUTTON_DELETE:
1063 result = DeleteVideoItem(item);
1066 case CONTEXT_BUTTON_SET_MOVIESET_ART:
1067 result = ManageVideoItemArtwork(item, "set");
1070 case CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS:
1071 result = ManageMovieSets(item);
1074 case CONTEXT_BUTTON_TAGS_ADD_ITEMS:
1075 result = AddItemsToTag(item);
1078 case CONTEXT_BUTTON_TAGS_REMOVE_ITEMS:
1079 result = RemoveItemsFromTag(item);
1096 //Add change a title's name
1097 bool CGUIDialogVideoInfo::UpdateVideoItemTitle(const CFileItemPtr &pItem)
1099 if (pItem == NULL || !pItem->HasVideoInfoTag())
1102 // dont allow update while scanning
1103 if (g_application.IsVideoScanning())
1105 CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1109 CVideoDatabase database;
1110 if (!database.Open())
1113 int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
1114 CVideoInfoTag detail;
1115 VIDEODB_CONTENT_TYPE iType = (VIDEODB_CONTENT_TYPE)pItem->GetVideoContentType();
1116 if (iType == VIDEODB_CONTENT_MOVIES)
1117 database.GetMovieInfo("", detail, iDbId);
1118 else if (iType == VIDEODB_CONTENT_MOVIE_SETS)
1119 database.GetSetInfo(iDbId, detail);
1120 else if (iType == VIDEODB_CONTENT_EPISODES)
1121 database.GetEpisodeInfo(pItem->GetPath(), detail, iDbId);
1122 else if (iType == VIDEODB_CONTENT_TVSHOWS)
1123 database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId);
1124 else if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
1125 database.GetMusicVideoInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId);
1127 // get the new title
1128 if (!CGUIKeyboardFactory::ShowAndGetInput(detail.m_strTitle, g_localizeStrings.Get(16105), false))
1131 database.UpdateMovieTitle(iDbId, detail.m_strTitle, iType);
1135 bool CGUIDialogVideoInfo::MarkWatched(const CFileItemPtr &item, bool bMark)
1137 if (item == NULL || !CProfilesManager::Get().GetCurrentProfile().canWriteDatabases())
1140 // dont allow update while scanning
1141 if (g_application.IsVideoScanning())
1143 CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1147 CVideoDatabase database;
1148 if (!database.Open())
1151 CFileItemList items;
1152 if (item->m_bIsFolder)
1154 CStdString strPath = item->GetPath();
1155 CDirectory::GetDirectory(strPath, items);
1160 for (int i = 0; i < items.Size(); ++i)
1162 CFileItemPtr pTmpItem = items[i];
1163 if (pTmpItem->m_bIsFolder)
1165 MarkWatched(pTmpItem, bMark);
1169 if (pTmpItem->HasVideoInfoTag() &&
1170 ((bMark && pTmpItem->GetVideoInfoTag()->m_playCount) ||
1171 (!bMark && !pTmpItem->GetVideoInfoTag()->m_playCount)))
1175 if (!URIUtils::IsUPnP(item->GetPath()) || !UPNP::CUPnP::MarkWatched(*pTmpItem, bMark))
1178 // Clear resume bookmark
1180 database.ClearBookMarksOfFile(pTmpItem->GetPath(), CBookmark::RESUME);
1182 database.SetPlayCount(*pTmpItem, bMark ? 1 : 0);
1191 bool CGUIDialogVideoInfo::CanDeleteVideoItem(const CFileItemPtr &item)
1193 if (item == NULL || !item->HasVideoInfoTag())
1196 CQueryParams params;
1197 CVideoDatabaseDirectory::GetQueryParams(item->GetPath(), params);
1199 return params.GetMovieId() != -1 ||
1200 params.GetEpisodeId() != -1 ||
1201 params.GetMVideoId() != -1 ||
1202 (params.GetTvShowId() != -1 && params.GetSeason() <= -1 &&
1203 !CVideoDatabaseDirectory::IsAllItem(item->GetPath()));
1206 bool CGUIDialogVideoInfo::DeleteVideoItemFromDatabase(const CFileItemPtr &item, bool unavailable /* = false */)
1208 if (item == NULL || !item->HasVideoInfoTag() ||
1209 !CanDeleteVideoItem(item))
1212 // dont allow update while scanning
1213 if (g_application.IsVideoScanning())
1215 CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1219 CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
1220 if (pDialog == NULL)
1224 VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)item->GetVideoContentType();
1227 case VIDEODB_CONTENT_MOVIES:
1230 case VIDEODB_CONTENT_EPISODES:
1233 case VIDEODB_CONTENT_TVSHOWS:
1236 case VIDEODB_CONTENT_MUSICVIDEOS:
1244 pDialog->SetHeading(heading);
1248 pDialog->SetLine(0, g_localizeStrings.Get(662));
1249 pDialog->SetLine(1, g_localizeStrings.Get(663));
1253 pDialog->SetLine(0, StringUtils::Format(g_localizeStrings.Get(433), item->GetLabel().c_str()));
1254 pDialog->SetLine(1, "");
1256 pDialog->SetLine(2, "");
1259 if (!pDialog->IsConfirmed())
1263 CVideoDatabase database;
1266 database.GetFilePathById(item->GetVideoInfoTag()->m_iDbId, path, type);
1272 case VIDEODB_CONTENT_MOVIES:
1273 database.DeleteMovie(path);
1275 case VIDEODB_CONTENT_EPISODES:
1276 database.DeleteEpisode(path, item->GetVideoInfoTag()->m_iDbId);
1278 case VIDEODB_CONTENT_TVSHOWS:
1279 database.DeleteTvShow(path);
1281 case VIDEODB_CONTENT_MUSICVIDEOS:
1282 database.DeleteMusicVideo(path);
1289 if (type == VIDEODB_CONTENT_TVSHOWS)
1290 database.SetPathHash(path,"");
1292 database.SetPathHash(URIUtils::GetDirectory(path), "");
1297 bool CGUIDialogVideoInfo::DeleteVideoItem(const CFileItemPtr &item, bool unavailable /* = false */)
1302 // delete the video item from the database
1303 if (!DeleteVideoItemFromDatabase(item, unavailable))
1307 // check if the user is allowed to delete the actual file as well
1308 if ((CProfilesManager::Get().GetCurrentProfile().getLockMode() == LOCK_MODE_EVERYONE ||
1309 !CProfilesManager::Get().GetCurrentProfile().filesLocked() ||
1310 g_passwordManager.IsMasterLockUnlocked(true)) &&
1311 CSettings::Get().GetBool("filelists.allowfiledeletion"))
1313 CStdString strDeletePath = item->GetVideoInfoTag()->GetPath();
1315 if (URIUtils::GetFileName(strDeletePath).Equals("VIDEO_TS.IFO"))
1317 strDeletePath = URIUtils::GetDirectory(strDeletePath);
1318 if (StringUtils::EndsWithNoCase(strDeletePath, "video_ts/"))
1320 URIUtils::RemoveSlashAtEnd(strDeletePath);
1321 strDeletePath = URIUtils::GetDirectory(strDeletePath);
1324 if (URIUtils::HasSlashAtEnd(strDeletePath))
1325 item->m_bIsFolder = true;
1327 // check if the file/directory can be deleted
1328 if (CUtil::SupportsWriteFileOperations(strDeletePath))
1330 item->SetPath(strDeletePath);
1332 // HACK: stacked files need to be treated as folders in order to be deleted
1333 if (item->IsStack())
1334 item->m_bIsFolder = true;
1335 result = CFileUtils::DeleteItem(item);
1339 CUtil::DeleteVideoDatabaseDirectoryCache();
1344 bool CGUIDialogVideoInfo::ManageMovieSets(const CFileItemPtr &item)
1349 CFileItemList originalItems;
1350 CFileItemList selectedItems;
1352 if (!GetMoviesForSet(item.get(), originalItems, selectedItems) ||
1353 selectedItems.Size() == 0) // need at least one item selected
1356 VECFILEITEMS original = originalItems.GetList();
1357 std::sort(original.begin(), original.end(), compFileItemsByDbId);
1358 VECFILEITEMS selected = selectedItems.GetList();
1359 std::sort(selected.begin(), selected.end(), compFileItemsByDbId);
1361 bool refreshNeeded = false;
1362 // update the "added" items
1363 VECFILEITEMS addedItems;
1364 set_difference(selected.begin(),selected.end(), original.begin(),original.end(), std::back_inserter(addedItems), compFileItemsByDbId);
1365 for (VECFILEITEMS::const_iterator it = addedItems.begin(); it != addedItems.end(); ++it)
1367 if (SetMovieSet(it->get(), item.get()))
1368 refreshNeeded = true;
1371 // update the "deleted" items
1372 CFileItemPtr clearItem(new CFileItem());
1373 clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
1374 VECFILEITEMS deletedItems;
1375 set_difference(original.begin(),original.end(), selected.begin(),selected.end(), std::back_inserter(deletedItems), compFileItemsByDbId);
1376 for (VECFILEITEMS::iterator it = deletedItems.begin(); it != deletedItems.end(); ++it)
1378 if (SetMovieSet(it->get(), clearItem.get()))
1379 refreshNeeded = true;
1382 return refreshNeeded;
1385 bool CGUIDialogVideoInfo::GetMoviesForSet(const CFileItem *setItem, CFileItemList &originalMovies, CFileItemList &selectedMovies)
1387 if (setItem == NULL || !setItem->HasVideoInfoTag())
1390 CVideoDatabase videodb;
1391 if (!videodb.Open())
1394 CStdString strHeading = StringUtils::Format(g_localizeStrings.Get(20457));
1395 CStdString baseDir = StringUtils::Format("videodb://movies/sets/%d", setItem->GetVideoInfoTag()->m_iDbId);
1397 if (!CDirectory::GetDirectory(baseDir, originalMovies) || originalMovies.Size() <= 0) // keep a copy of the original members of the set
1400 CFileItemList listItems;
1401 if (!videodb.GetSortedVideos(MediaTypeMovie, "videodb://movies", SortDescription(), listItems) || listItems.Size() <= 0)
1404 CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1408 listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1411 dialog->SetMultiSelection(true);
1412 dialog->SetHeading(strHeading);
1413 dialog->SetItems(&listItems);
1414 vector<int> selectedIndices;
1415 for (int i = 0; i < originalMovies.Size(); i++)
1417 for (int listIndex = 0; listIndex < listItems.Size(); listIndex++)
1419 if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == originalMovies[i]->GetVideoInfoTag()->m_iDbId)
1421 selectedIndices.push_back(listIndex);
1426 dialog->SetSelected(selectedIndices);
1427 dialog->EnableButton(true, 186);
1430 if (dialog->IsConfirmed())
1432 selectedMovies.Copy(dialog->GetSelectedItems());
1433 return (selectedMovies.Size() > 0);
1439 bool CGUIDialogVideoInfo::GetSetForMovie(const CFileItem *movieItem, CFileItemPtr &selectedSet)
1441 if (movieItem == NULL || !movieItem->HasVideoInfoTag())
1444 CVideoDatabase videodb;
1445 if (!videodb.Open())
1448 CFileItemList listItems;
1449 CStdString baseDir = "videodb://movies/sets/";
1450 if (!CDirectory::GetDirectory(baseDir, listItems) || listItems.Size() <= 0)
1452 listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1454 int currentSetId = 0;
1455 CStdString currentSetLabel;
1457 if (movieItem->GetVideoInfoTag()->m_iSetId > currentSetId)
1459 currentSetId = movieItem->GetVideoInfoTag()->m_iSetId;
1460 currentSetLabel = videodb.GetSetById(currentSetId);
1463 if (currentSetId > 0)
1466 CStdString strClear = StringUtils::Format(g_localizeStrings.Get(20467), currentSetLabel.c_str());
1467 CFileItemPtr clearItem(new CFileItem(strClear));
1468 clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
1469 listItems.AddFront(clearItem, 0);
1470 // add keep current set item
1471 CStdString strKeep = StringUtils::Format(g_localizeStrings.Get(20469), currentSetLabel.c_str());
1472 CFileItemPtr keepItem(new CFileItem(strKeep));
1473 keepItem->GetVideoInfoTag()->m_iDbId = currentSetId;
1474 listItems.AddFront(keepItem, 1);
1477 CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1481 CStdString strHeading = StringUtils::Format(g_localizeStrings.Get(20466));
1483 dialog->SetHeading(strHeading);
1484 dialog->SetItems(&listItems);
1485 if (currentSetId >= 0)
1487 for (int listIndex = 0; listIndex < listItems.Size(); listIndex++)
1489 if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == currentSetId)
1491 dialog->SetSelected(listIndex);
1496 dialog->EnableButton(true, 20468); // new set via button
1499 if (dialog->IsButtonPressed())
1500 { // creating new set
1501 CStdString newSetTitle;
1502 if (!CGUIKeyboardFactory::ShowAndGetInput(newSetTitle, g_localizeStrings.Get(20468), false))
1504 int idSet = videodb.AddSet(newSetTitle);
1505 map<string, string> movieArt, setArt;
1506 if (!videodb.GetArtForItem(idSet, "set", setArt))
1508 videodb.GetArtForItem(movieItem->GetVideoInfoTag()->m_iDbId, "movie", movieArt);
1509 videodb.SetArtForItem(idSet, "set", movieArt);
1511 CFileItemPtr newSet(new CFileItem(newSetTitle));
1512 newSet->GetVideoInfoTag()->m_iDbId = idSet;
1513 selectedSet = newSet;
1516 else if (dialog->IsConfirmed())
1518 selectedSet = dialog->GetSelectedItem();
1519 return (selectedSet != NULL);
1525 bool CGUIDialogVideoInfo::SetMovieSet(const CFileItem *movieItem, const CFileItem *selectedSet)
1527 if (movieItem == NULL || !movieItem->HasVideoInfoTag() ||
1528 selectedSet == NULL || !selectedSet->HasVideoInfoTag())
1531 CVideoDatabase videodb;
1532 if (!videodb.Open())
1535 videodb.SetMovieSet(movieItem->GetVideoInfoTag()->m_iDbId, selectedSet->GetVideoInfoTag()->m_iDbId);
1539 bool CGUIDialogVideoInfo::GetItemsForTag(const CStdString &strHeading, const std::string &type, CFileItemList &items, int idTag /* = -1 */, bool showAll /* = true */)
1541 CVideoDatabase videodb;
1542 if (!videodb.Open())
1545 MediaType mediaType = MediaTypeNone;
1546 std::string baseDir = "videodb://";
1547 std::string idColumn;
1548 if (type.compare("movie") == 0)
1550 mediaType = MediaTypeMovie;
1551 baseDir += "movies";
1552 idColumn = "idMovie";
1554 else if (type.compare("tvshow") == 0)
1556 mediaType = MediaTypeTvShow;
1557 baseDir += "tvshows";
1558 idColumn = "idShow";
1560 else if (type.compare("musicvideo") == 0)
1562 mediaType = MediaTypeMusicVideo;
1563 baseDir += "musicvideos";
1564 idColumn = "idMVideo";
1567 baseDir += "/titles/";
1568 CVideoDbUrl videoUrl;
1569 if (!videoUrl.FromString(baseDir))
1572 CVideoDatabase::Filter filter;
1576 videoUrl.AddOption("tagid", idTag);
1578 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());
1581 CFileItemList listItems;
1582 if (!videodb.GetSortedVideos(mediaType, videoUrl.ToString(), SortDescription(), listItems, filter) || listItems.Size() <= 0)
1585 CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1589 listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1592 dialog->SetMultiSelection(true);
1593 dialog->SetHeading(strHeading);
1594 dialog->SetItems(&listItems);
1595 dialog->EnableButton(true, 186);
1598 items.Copy(dialog->GetSelectedItems());
1599 return items.Size() > 0;
1602 bool CGUIDialogVideoInfo::AddItemsToTag(const CFileItemPtr &tagItem)
1604 if (tagItem == NULL || !tagItem->HasVideoInfoTag())
1607 CVideoDbUrl videoUrl;
1608 if (!videoUrl.FromString(tagItem->GetPath()))
1611 CVideoDatabase videodb;
1612 if (!videodb.Open())
1615 std::string mediaType = videoUrl.GetItemType();
1616 mediaType = mediaType.substr(0, mediaType.length() - 1);
1618 CFileItemList items;
1619 std::string localizedType = GetLocalizedVideoType(mediaType);
1620 std::string strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType.c_str());
1621 if (!GetItemsForTag(strLabel, mediaType, items, tagItem->GetVideoInfoTag()->m_iDbId))
1624 for (int index = 0; index < items.Size(); index++)
1626 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1629 videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, tagItem->GetVideoInfoTag()->m_iDbId, mediaType);
1635 bool CGUIDialogVideoInfo::RemoveItemsFromTag(const CFileItemPtr &tagItem)
1637 if (tagItem == NULL || !tagItem->HasVideoInfoTag())
1640 CVideoDbUrl videoUrl;
1641 if (!videoUrl.FromString(tagItem->GetPath()))
1644 CVideoDatabase videodb;
1645 if (!videodb.Open())
1648 std::string mediaType = videoUrl.GetItemType();
1649 mediaType = mediaType.substr(0, mediaType.length() - 1);
1651 CFileItemList items;
1652 std::string localizedType = GetLocalizedVideoType(mediaType);
1653 std::string strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType.c_str());
1654 if (!GetItemsForTag(strLabel, mediaType, items, tagItem->GetVideoInfoTag()->m_iDbId, false))
1657 for (int index = 0; index < items.Size(); index++)
1659 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1662 videodb.RemoveTagFromItem(items[index]->GetVideoInfoTag()->m_iDbId, tagItem->GetVideoInfoTag()->m_iDbId, mediaType);
1668 bool CGUIDialogVideoInfo::ManageVideoItemArtwork(const CFileItemPtr &item, const std::string &type)
1670 if (item == NULL || !item->HasVideoInfoTag() || type.empty())
1673 CVideoDatabase videodb;
1674 if (!videodb.Open())
1677 // Grab the thumbnails from the web
1678 CFileItemList items;
1679 CFileItemPtr noneitem(new CFileItem("thumb://None", false));
1680 std::string currentThumb;
1682 CStdString artistPath;
1683 string artType = "thumb";
1684 if (type == "artist")
1686 CMusicDatabase musicdb;
1689 idArtist = musicdb.GetArtistByName(item->GetLabel());
1690 if (idArtist >= 0 && musicdb.GetArtistPath(idArtist, artistPath))
1692 currentThumb = musicdb.GetArtForItem(idArtist, "artist", "thumb");
1693 if (currentThumb.empty())
1694 currentThumb = videodb.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType);
1698 else if (type == "actor")
1699 currentThumb = videodb.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType);
1702 map<string, string> currentArt;
1703 artType = ChooseArtType(*item, currentArt);
1704 if (artType.empty())
1707 if (artType == "fanart")
1708 return OnGetFanart(item);
1710 if (currentArt.find(artType) != currentArt.end())
1711 currentThumb = currentArt[artType];
1712 else if ((artType == "poster" || artType == "banner") && currentArt.find("thumb") != currentArt.end())
1713 currentThumb = currentArt["thumb"];
1716 if (!currentThumb.empty())
1718 CFileItemPtr item(new CFileItem("thumb://Current", false));
1719 item->SetArt("thumb", currentThumb);
1720 item->SetLabel(g_localizeStrings.Get(13512));
1723 noneitem->SetIconImage("DefaultFolder.png");
1724 noneitem->SetLabel(g_localizeStrings.Get(13515));
1727 vector<CStdString> thumbs;
1728 if (type != "artist")
1731 if (type == "season")
1732 videodb.GetTvShowInfo("", tag, item->GetVideoInfoTag()->m_iIdShow);
1734 tag = *item->GetVideoInfoTag();
1736 tag.m_strPictureURL.GetThumbURLs(thumbs, artType, type == "season" ? item->GetVideoInfoTag()->m_iSeason : -1);
1738 for (size_t i = 0; i < thumbs.size(); i++)
1740 CFileItemPtr item(new CFileItem(StringUtils::Format("thumb://Remote%i", i), false));
1741 item->SetArt("thumb", thumbs[i]);
1742 item->SetIconImage("DefaultPicture.png");
1743 item->SetLabel(g_localizeStrings.Get(13513));
1746 // TODO: Do we need to clear the cached image?
1747 // CTextureCache::Get().ClearCachedImage(thumbs[i]);
1750 if (type == "actor")
1752 CStdString picturePath;
1753 CStdString strThumb = URIUtils::AddFileToFolder(picturePath, "folder.jpg");
1754 if (XFILE::CFile::Exists(strThumb))
1756 CFileItemPtr pItem(new CFileItem(strThumb,false));
1757 pItem->SetLabel(g_localizeStrings.Get(13514));
1758 pItem->SetArt("thumb", strThumb);
1763 noneitem->SetIconImage("DefaultActor.png");
1767 noneitem->SetIconImage("DefaultVideo.png");
1771 CStdString strThumb = URIUtils::AddFileToFolder(artistPath, "folder.jpg");
1772 if (XFILE::CFile::Exists(strThumb))
1774 CFileItemPtr pItem(new CFileItem(strThumb, false));
1775 pItem->SetLabel(g_localizeStrings.Get(13514));
1776 pItem->SetArt("thumb", strThumb);
1781 noneitem->SetIconImage("DefaultArtist.png");
1785 items.Add(noneitem);
1788 VECSOURCES sources=*CMediaSourceSettings::Get().GetSources("video");
1789 g_mediaManager.GetLocalDrives(sources);
1790 AddItemPathToFileBrowserSources(sources, *item);
1791 if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result))
1792 return false; // user cancelled
1794 if (result == "thumb://Current")
1795 result = currentThumb; // user chose the one they have
1797 // delete the thumbnail if that's what the user wants, else overwrite with the
1799 if (result == "thumb://None")
1801 else if (StringUtils::StartsWith(result, "thumb://Remote"))
1803 int number = atoi(StringUtils::Mid(result, 14).c_str());
1804 result = thumbs[number];
1807 // write the selected artwork to the database
1808 if (type == "set" ||
1811 (type == "artist" && idArtist < 0))
1812 videodb.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType, result);
1815 CMusicDatabase musicdb;
1817 musicdb.SetArtForItem(idArtist, "artist", artType, result);
1820 CUtil::DeleteVideoDatabaseDirectoryCache();
1821 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
1822 g_windowManager.SendMessage(msg);
1827 std::string CGUIDialogVideoInfo::GetLocalizedVideoType(const std::string &strType)
1829 if (strType == "movie" || strType == "movies")
1830 return g_localizeStrings.Get(20342);
1831 else if (strType == "tvshow" || strType == "tvshows")
1832 return g_localizeStrings.Get(20343);
1833 else if (strType == "episode" || strType == "episodes")
1834 return g_localizeStrings.Get(20359);
1835 else if (strType == "musicvideo" || strType == "musicvideos")
1836 return g_localizeStrings.Get(20391);
1841 bool CGUIDialogVideoInfo::UpdateVideoItemSortTitle(const CFileItemPtr &pItem)
1843 // dont allow update while scanning
1844 if (g_application.IsVideoScanning())
1846 CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1850 CVideoDatabase database;
1851 if (!database.Open())
1854 int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
1855 CVideoInfoTag detail;
1856 VIDEODB_CONTENT_TYPE iType = (VIDEODB_CONTENT_TYPE)pItem->GetVideoContentType();
1857 if (iType == VIDEODB_CONTENT_MOVIES)
1858 database.GetMovieInfo("", detail, iDbId);
1859 else if (iType == VIDEODB_CONTENT_TVSHOWS)
1860 database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId);
1862 CStdString currentTitle;
1863 if (detail.m_strSortTitle.empty())
1864 currentTitle = detail.m_strTitle;
1866 currentTitle = detail.m_strSortTitle;
1868 // get the new sort title
1869 if (!CGUIKeyboardFactory::ShowAndGetInput(currentTitle, g_localizeStrings.Get(16107), false))
1872 return database.UpdateVideoSortTitle(iDbId, currentTitle, iType);
1875 bool CGUIDialogVideoInfo::LinkMovieToTvShow(const CFileItemPtr &item, bool bRemove, CVideoDatabase &database)
1877 int dbId = item->GetVideoInfoTag()->m_iDbId;
1883 if (!database.GetLinksToTvShow(dbId, ids))
1886 for (unsigned int i = 0; i < ids.size(); ++i)
1889 database.GetTvShowInfo("", tag, ids[i]);
1890 CFileItemPtr show(new CFileItem(tag));
1896 database.GetTvShowsNav("videodb://tvshows/titles", list);
1898 // remove already linked shows
1900 if (!database.GetLinksToTvShow(dbId, ids))
1903 for (int i = 0; i < list.Size(); )
1906 for (j = 0; j < ids.size(); ++j)
1908 if (list[i]->GetVideoInfoTag()->m_iDbId == ids[j])
1911 if (j == ids.size())
1918 int iSelectedLabel = 0;
1919 if (list.Size() > 1)
1921 list.Sort(SortByLabel, SortOrderAscending, CSettings::Get().GetBool("filelists.ignorethewhensorting") ? SortAttributeIgnoreArticle : SortAttributeNone);
1922 CGUIDialogSelect* pDialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1924 pDialog->SetItems(&list);
1925 pDialog->SetHeading(20356);
1927 iSelectedLabel = pDialog->GetSelectedLabel();
1930 if (iSelectedLabel > -1)
1931 return database.LinkMovieToTvshow(dbId, list[iSelectedLabel]->GetVideoInfoTag()->m_iDbId, bRemove);
1936 bool CGUIDialogVideoInfo::OnGetFanart(const CFileItemPtr &videoItem)
1938 if (videoItem == NULL || !videoItem->HasVideoInfoTag())
1942 CVideoDatabase videodb;
1943 if (!videodb.Open())
1946 CVideoThumbLoader loader;
1947 CFileItem item(*videoItem);
1948 loader.LoadItem(&item);
1950 CFileItemList items;
1951 if (item.HasArt("fanart"))
1953 CFileItemPtr itemCurrent(new CFileItem("fanart://Current", false));
1954 itemCurrent->SetArt("thumb", item.GetArt("fanart"));
1955 itemCurrent->SetLabel(g_localizeStrings.Get(20440));
1956 items.Add(itemCurrent);
1959 // add the none option
1961 CFileItemPtr itemNone(new CFileItem("fanart://None", false));
1962 itemNone->SetIconImage("DefaultVideo.png");
1963 itemNone->SetLabel(g_localizeStrings.Get(20439));
1964 items.Add(itemNone);
1968 VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
1969 g_mediaManager.GetLocalDrives(sources);
1970 AddItemPathToFileBrowserSources(sources, item);
1972 if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) ||
1973 StringUtils::EqualsNoCase(result, "fanart://Current"))
1976 if (StringUtils::EqualsNoCase(result, "fanart://None") || !CFile::Exists(result))
1978 if (!result.empty() && flip)
1979 result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
1981 videodb.SetArtForItem(item.GetVideoInfoTag()->m_iDbId, item.GetVideoInfoTag()->m_type, "fanart", result);
1983 // clear view cache and reload images
1984 CUtil::DeleteVideoDatabaseDirectoryCache();