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 if (!m_movieItem->HasProperty("totalepisodes"))
310 m_movieItem->SetProperty("totalepisodes", m_movieItem->GetVideoInfoTag()->m_iEpisode);
311 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
312 m_movieItem->SetProperty("watchedepisodes", m_movieItem->GetVideoInfoTag()->m_playCount);
313 m_movieItem->SetProperty("unwatchedepisodes", m_movieItem->GetVideoInfoTag()->m_iEpisode - m_movieItem->GetVideoInfoTag()->m_playCount);
314 m_movieItem->GetVideoInfoTag()->m_playCount = (m_movieItem->GetVideoInfoTag()->m_iEpisode == m_movieItem->GetVideoInfoTag()->m_playCount) ? 1 : 0;
317 else if (type == VIDEODB_CONTENT_EPISODES)
319 m_castList->SetContent("episodes");
320 // special case stuff for episodes (not currently retrieved from the library in filemode (ref: GetEpisodeInfo vs GetEpisodesByWhere)
321 m_movieItem->m_dateTime = m_movieItem->GetVideoInfoTag()->m_firstAired;
322 if(m_movieItem->GetVideoInfoTag()->m_iYear == 0 && m_movieItem->m_dateTime.IsValid())
323 m_movieItem->GetVideoInfoTag()->m_iYear = m_movieItem->m_dateTime.GetYear();
324 // retrieve the season thumb.
325 // TODO: should we use the thumbloader for this?
329 if (m_movieItem->GetVideoInfoTag()->m_iSeason > -1)
331 int seasonID = m_movieItem->GetVideoInfoTag()->m_iIdSeason;
333 seasonID = db.GetSeasonId(m_movieItem->GetVideoInfoTag()->m_iIdShow,
334 m_movieItem->GetVideoInfoTag()->m_iSeason);
335 CGUIListItem::ArtMap thumbs;
336 if (db.GetArtForItem(seasonID, "season", thumbs))
338 for (CGUIListItem::ArtMap::iterator i = thumbs.begin(); i != thumbs.end(); i++)
339 m_movieItem->SetArt("season." + i->first, i->second);
345 else if (type == VIDEODB_CONTENT_MOVIES)
347 m_castList->SetContent("movies");
349 // local trailers should always override non-local, so check
350 // for a local one if the registered trailer is online
351 if (m_movieItem->GetVideoInfoTag()->m_strTrailer.empty() ||
352 URIUtils::IsInternetStream(m_movieItem->GetVideoInfoTag()->m_strTrailer))
354 CStdString localTrailer = m_movieItem->FindTrailer();
355 if (!localTrailer.empty())
357 m_movieItem->GetVideoInfoTag()->m_strTrailer = localTrailer;
358 CVideoDatabase database;
361 database.SetSingleValue(VIDEODB_CONTENT_MOVIES, VIDEODB_ID_TRAILER,
362 m_movieItem->GetVideoInfoTag()->m_iDbId,
363 m_movieItem->GetVideoInfoTag()->m_strTrailer);
365 CUtil::DeleteVideoDatabaseDirectoryCache();
371 CVideoThumbLoader loader;
372 loader.LoadItem(m_movieItem.get());
375 void CGUIDialogVideoInfo::Update()
377 // setup plot text area
378 CStdString strTmp = m_movieItem->GetVideoInfoTag()->m_strPlot;
379 if (!(!m_movieItem->GetVideoInfoTag()->m_strShowTitle.empty() && m_movieItem->GetVideoInfoTag()->m_iSeason == 0)) // dont apply to tvshows
380 if (m_movieItem->GetVideoInfoTag()->m_playCount == 0 && !CSettings::Get().GetBool("videolibrary.showunwatchedplots"))
381 strTmp = g_localizeStrings.Get(20370);
383 StringUtils::Trim(strTmp);
384 SetLabel(CONTROL_TEXTAREA, strTmp);
386 CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST, 0, 0, m_castList);
389 if (GetControl(CONTROL_BTN_TRACKS)) // if no CONTROL_BTN_TRACKS found - allow skinner full visibility control over CONTROL_TEXTAREA and CONTROL_LIST
393 if (!m_movieItem->GetVideoInfoTag()->m_artist.empty())
395 SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 133);
399 SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 206);
402 SET_CONTROL_HIDDEN(CONTROL_LIST);
403 SET_CONTROL_VISIBLE(CONTROL_TEXTAREA);
407 SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 207);
409 SET_CONTROL_HIDDEN(CONTROL_TEXTAREA);
410 SET_CONTROL_VISIBLE(CONTROL_LIST);
414 // Check for resumability
415 if (m_movieItem->GetVideoInfoTag()->m_resumePoint.timeInSeconds > 0.0)
416 CONTROL_ENABLE(CONTROL_BTN_RESUME);
418 CONTROL_DISABLE(CONTROL_BTN_RESUME);
420 CONTROL_ENABLE(CONTROL_BTN_PLAY);
422 // update the thumbnail
423 const CGUIControl* pControl = GetControl(CONTROL_IMAGE);
426 CGUIImage* pImageControl = (CGUIImage*)pControl;
427 pImageControl->FreeResources();
428 pImageControl->SetFileName(m_movieItem->GetArt("thumb"));
430 // tell our GUI to completely reload all controls (as some of them
431 // are likely to have had this image in use so will need refreshing)
432 if (m_hasUpdatedThumb)
434 CGUIMessage reload(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
435 g_windowManager.SendMessage(reload);
439 bool CGUIDialogVideoInfo::NeedRefresh() const
444 bool CGUIDialogVideoInfo::RefreshAll() const
446 return m_bRefreshAll;
449 /// \brief Search the current directory for a string got from the virtual keyboard
450 void CGUIDialogVideoInfo::OnSearch(CStdString& strSearch)
452 CGUIDialogProgress *progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
455 progress->SetHeading(194);
456 progress->SetLine(0, strSearch);
457 progress->SetLine(1, "");
458 progress->SetLine(2, "");
459 progress->StartModal();
460 progress->Progress();
463 DoSearch(strSearch, items);
470 CGUIDialogSelect* pDlgSelect = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
472 pDlgSelect->SetHeading(283);
474 for (int i = 0; i < (int)items.Size(); i++)
476 CFileItemPtr pItem = items[i];
477 pDlgSelect->Add(pItem->GetLabel());
480 pDlgSelect->DoModal();
482 int iItem = pDlgSelect->GetSelectedLabel();
486 CFileItem* pSelItem = new CFileItem(*items[iItem]);
488 OnSearchItemFound(pSelItem);
494 CGUIDialogOK::ShowAndGetInput(194, 284, 0, 0);
498 /// \brief Make the actual search for the OnSearch function.
499 /// \param strSearch The search string
500 /// \param items Items Found
501 void CGUIDialogVideoInfo::DoSearch(CStdString& strSearch, CFileItemList& items)
507 CFileItemList movies;
508 db.GetMoviesByActor(strSearch, movies);
509 for (int i = 0; i < movies.Size(); ++i)
511 CStdString label = movies[i]->GetVideoInfoTag()->m_strTitle;
512 if (movies[i]->GetVideoInfoTag()->m_iYear > 0)
513 label += StringUtils::Format(" (%i)", movies[i]->GetVideoInfoTag()->m_iYear);
514 movies[i]->SetLabel(label);
516 CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20338) + "] ", items);
518 db.GetTvShowsByActor(strSearch, movies);
519 for (int i = 0; i < movies.Size(); ++i)
521 CStdString label = movies[i]->GetVideoInfoTag()->m_strShowTitle;
522 if (movies[i]->GetVideoInfoTag()->m_iYear > 0)
523 label += StringUtils::Format(" (%i)", movies[i]->GetVideoInfoTag()->m_iYear);
524 movies[i]->SetLabel(label);
526 CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20364) + "] ", items);
528 db.GetEpisodesByActor(strSearch, movies);
529 for (int i = 0; i < movies.Size(); ++i)
531 CStdString label = movies[i]->GetVideoInfoTag()->m_strTitle + " (" + movies[i]->GetVideoInfoTag()->m_strShowTitle + ")";
532 movies[i]->SetLabel(label);
534 CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20359) + "] ", items);
536 db.GetMusicVideosByArtist(strSearch, movies);
537 for (int i = 0; i < movies.Size(); ++i)
539 CStdString label = StringUtils::Join(movies[i]->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator) + " - " + movies[i]->GetVideoInfoTag()->m_strTitle;
540 if (movies[i]->GetVideoInfoTag()->m_iYear > 0)
541 label += StringUtils::Format(" (%i)", movies[i]->GetVideoInfoTag()->m_iYear);
542 movies[i]->SetLabel(label);
544 CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20391) + "] ", items);
548 /// \brief React on the selected search item
549 /// \param pItem Search result item
550 void CGUIDialogVideoInfo::OnSearchItemFound(const CFileItem* pItem)
552 VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)pItem->GetVideoContentType();
558 CVideoInfoTag movieDetails;
559 if (type == VIDEODB_CONTENT_MOVIES)
560 db.GetMovieInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
561 if (type == VIDEODB_CONTENT_EPISODES)
562 db.GetEpisodeInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
563 if (type == VIDEODB_CONTENT_TVSHOWS)
564 db.GetTvShowInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
565 if (type == VIDEODB_CONTENT_MUSICVIDEOS)
566 db.GetMusicVideoInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
569 CFileItem item(*pItem);
570 *item.GetVideoInfoTag() = movieDetails;
572 // refresh our window entirely
577 void CGUIDialogVideoInfo::ClearCastList()
579 CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_LIST);
584 void CGUIDialogVideoInfo::Play(bool resume)
586 if (!m_movieItem->GetVideoInfoTag()->m_strEpisodeGuide.empty())
588 CStdString strPath = StringUtils::Format("videodb://tvshows/titles/%i/",m_movieItem->GetVideoInfoTag()->m_iDbId);
590 g_windowManager.ActivateWindow(WINDOW_VIDEO_NAV,strPath);
594 CFileItem movie(*m_movieItem->GetVideoInfoTag());
595 if (m_movieItem->GetVideoInfoTag()->m_strFileNameAndPath.empty())
596 movie.SetPath(m_movieItem->GetPath());
597 CGUIWindowVideoNav* pWindow = (CGUIWindowVideoNav*)g_windowManager.GetWindow(WINDOW_VIDEO_NAV);
603 movie.m_lStartOffset = STARTOFFSET_RESUME;
604 else if (!CGUIWindowVideoBase::ShowResumeMenu(movie))
606 // The Resume dialog was closed without any choice
610 pWindow->PlayMovie(&movie);
614 string CGUIDialogVideoInfo::ChooseArtType(const CFileItem &videoItem, map<string, string> ¤tArt)
617 CGUIDialogSelect *dialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
618 if (!dialog || !videoItem.HasVideoInfoTag())
622 dialog->SetHeading(13511);
624 dialog->SetUseDetails(true);
625 dialog->EnableButton(true, 13516);
630 vector<string> artTypes = CVideoThumbLoader::GetArtTypes(videoItem.GetVideoInfoTag()->m_type);
632 // add in any stored art for this item that is non-empty.
633 db.GetArtForItem(videoItem.GetVideoInfoTag()->m_iDbId, videoItem.GetVideoInfoTag()->m_type, currentArt);
634 for (CGUIListItem::ArtMap::iterator i = currentArt.begin(); i != currentArt.end(); ++i)
636 if (!i->second.empty() && find(artTypes.begin(), artTypes.end(), i->first) == artTypes.end())
637 artTypes.push_back(i->first);
640 // add any art types that exist for other media items of the same type
641 vector<string> dbArtTypes;
642 db.GetArtTypes(videoItem.GetVideoInfoTag()->m_type, dbArtTypes);
643 for (vector<string>::const_iterator it = dbArtTypes.begin(); it != dbArtTypes.end(); it++)
645 if (find(artTypes.begin(), artTypes.end(), *it) == artTypes.end())
646 artTypes.push_back(*it);
649 for (vector<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
652 CFileItemPtr item(new CFileItem(type, "false"));
653 item->SetLabel(type);
654 if (videoItem.HasArt(type))
655 item->SetArt("thumb", videoItem.GetArt(type));
659 dialog->SetItems(&items);
662 if (dialog->IsButtonPressed())
664 // Get the new artwork name
665 CStdString strArtworkName;
666 if (!CGUIKeyboardFactory::ShowAndGetInput(strArtworkName, g_localizeStrings.Get(13516), false))
669 return strArtworkName;
672 return dialog->GetSelectedItem()->GetLabel();
675 void CGUIDialogVideoInfo::OnGetArt()
677 map<string, string> currentArt;
678 string type = ChooseArtType(*m_movieItem, currentArt);
682 // TODO: this can be removed once these are unified.
683 if (type == "fanart")
690 if (m_movieItem->HasArt(type))
692 CFileItemPtr item(new CFileItem("thumb://Current", false));
693 item->SetArt("thumb", m_movieItem->GetArt(type));
694 item->SetLabel(g_localizeStrings.Get(13512));
697 else if ((type == "poster" || type == "banner") && currentArt.find("thumb") != currentArt.end())
698 { // add the 'thumb' type in
699 CFileItemPtr item(new CFileItem("thumb://Thumb", false));
700 item->SetArt("thumb", currentArt["thumb"]);
701 item->SetLabel(g_localizeStrings.Get(13512));
705 // Grab the thumbnails from the web
706 vector<CStdString> thumbs;
707 int season = (m_movieItem->GetVideoInfoTag()->m_type == "season") ? m_movieItem->GetVideoInfoTag()->m_iSeason : -1;
708 m_movieItem->GetVideoInfoTag()->m_strPictureURL.GetThumbURLs(thumbs, type, season);
710 for (unsigned int i = 0; i < thumbs.size(); ++i)
712 CStdString strItemPath = StringUtils::Format("thumb://Remote%i", i);
713 CFileItemPtr item(new CFileItem(strItemPath, false));
714 item->SetArt("thumb", thumbs[i]);
715 item->SetIconImage("DefaultPicture.png");
716 item->SetLabel(g_localizeStrings.Get(13513));
718 // TODO: Do we need to clear the cached image?
719 // CTextureCache::Get().ClearCachedImage(thumb);
723 CStdString localThumb = CVideoThumbLoader::GetLocalArt(*m_movieItem, type);
724 if (!localThumb.empty())
726 CFileItemPtr item(new CFileItem("thumb://Local", false));
727 item->SetArt("thumb", localThumb);
728 item->SetLabel(g_localizeStrings.Get(13514));
732 { // no local thumb exists, so we are just using the IMDb thumb or cached thumb
733 // which is probably the IMDb thumb. These could be wrong, so allow the user
734 // to delete the incorrect thumb
735 CFileItemPtr item(new CFileItem("thumb://None", false));
736 item->SetIconImage("DefaultVideo.png");
737 item->SetLabel(g_localizeStrings.Get(13515));
742 VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
743 AddItemPathToFileBrowserSources(sources, *m_movieItem);
744 g_mediaManager.GetLocalDrives(sources);
745 if (CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result) &&
746 result != "thumb://Current") // user didn't choose the one they have
749 if (StringUtils::StartsWith(result, "thumb://Remote"))
751 int number = atoi(result.substr(14).c_str());
752 newThumb = thumbs[number];
754 else if (result == "thumb://Thumb")
755 newThumb = currentArt["thumb"];
756 else if (result == "thumb://Local")
757 newThumb = localThumb;
758 else if (CFile::Exists(result))
763 // update thumb in the database
767 db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, type, newThumb);
770 CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
771 m_movieItem->SetArt(type, newThumb);
772 if (m_movieItem->HasProperty("set_folder_thumb"))
773 { // have a folder thumb to set as well
774 VIDEO::CVideoInfoScanner::ApplyThumbToFolder(m_movieItem->GetProperty("set_folder_thumb").asString(), newThumb);
776 m_hasUpdatedThumb = true;
783 // re-open the art selection dialog as we come back from
784 // the image selection dialog
788 // Allow user to select a Fanart
789 void CGUIDialogVideoInfo::OnGetFanart()
793 CFileItem item(*m_movieItem->GetVideoInfoTag());
794 if (item.HasArt("fanart"))
796 CFileItemPtr itemCurrent(new CFileItem("fanart://Current",false));
797 itemCurrent->SetArt("thumb", item.GetArt("fanart"));
798 itemCurrent->SetLabel(g_localizeStrings.Get(20440));
799 items.Add(itemCurrent);
802 // ensure the fanart is unpacked
803 m_movieItem->GetVideoInfoTag()->m_fanart.Unpack();
805 // Grab the thumbnails from the web
806 for (unsigned int i = 0; i < m_movieItem->GetVideoInfoTag()->m_fanart.GetNumFanarts(); i++)
808 CStdString strItemPath = StringUtils::Format("fanart://Remote%i",i);
809 CFileItemPtr item(new CFileItem(strItemPath, false));
810 CStdString thumb = m_movieItem->GetVideoInfoTag()->m_fanart.GetPreviewURL(i);
811 item->SetArt("thumb", CTextureUtils::GetWrappedThumbURL(thumb));
812 item->SetIconImage("DefaultPicture.png");
813 item->SetLabel(g_localizeStrings.Get(20441));
815 // TODO: Do we need to clear the cached image?
816 // CTextureCache::Get().ClearCachedImage(thumb);
820 CStdString strLocal = item.GetLocalFanart();
821 if (!strLocal.empty())
823 CFileItemPtr itemLocal(new CFileItem("fanart://Local",false));
824 itemLocal->SetArt("thumb", strLocal);
825 itemLocal->SetLabel(g_localizeStrings.Get(20438));
827 // TODO: Do we need to clear the cached image?
828 CTextureCache::Get().ClearCachedImage(strLocal);
829 items.Add(itemLocal);
833 CFileItemPtr itemNone(new CFileItem("fanart://None", false));
834 itemNone->SetIconImage("DefaultVideo.png");
835 itemNone->SetLabel(g_localizeStrings.Get(20439));
840 VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
841 AddItemPathToFileBrowserSources(sources, item);
842 g_mediaManager.GetLocalDrives(sources);
844 if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) || result.Equals("fanart://Current"))
845 return; // user cancelled
847 if (result.Equals("fanart://Local"))
850 if (StringUtils::StartsWith(result, "fanart://Remote"))
852 int iFanart = atoi(result.substr(15).c_str());
853 // set new primary fanart, and update our database accordingly
854 m_movieItem->GetVideoInfoTag()->m_fanart.SetPrimaryFanart(iFanart);
858 db.UpdateFanart(*m_movieItem, (VIDEODB_CONTENT_TYPE)m_movieItem->GetVideoContentType());
861 result = m_movieItem->GetVideoInfoTag()->m_fanart.GetImageURL();
863 else if (result.Equals("fanart://None") || !CFile::Exists(result))
866 // set the fanart image
867 if (flip && !result.empty())
868 result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
872 db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, "fanart", result);
876 CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
877 m_movieItem->SetArt("fanart", result);
878 m_hasUpdatedThumb = true;
884 void CGUIDialogVideoInfo::PlayTrailer()
887 item.SetPath(m_movieItem->GetVideoInfoTag()->m_strTrailer);
888 *item.GetVideoInfoTag() = *m_movieItem->GetVideoInfoTag();
889 item.GetVideoInfoTag()->m_streamDetails.Reset();
890 item.GetVideoInfoTag()->m_strTitle = StringUtils::Format("%s (%s)",
891 m_movieItem->GetVideoInfoTag()->m_strTitle.c_str(),
892 g_localizeStrings.Get(20410).c_str());
893 CVideoThumbLoader::SetArt(item, m_movieItem->GetArt());
894 item.GetVideoInfoTag()->m_iDbId = -1;
895 item.GetVideoInfoTag()->m_iFileId = -1;
900 if (item.IsPlayList())
901 CApplicationMessenger::Get().MediaPlay(item);
903 CApplicationMessenger::Get().PlayFile(item);
906 void CGUIDialogVideoInfo::SetLabel(int iControl, const CStdString &strLabel)
908 if (strLabel.empty())
910 SET_CONTROL_LABEL(iControl, 416); // "Not available"
914 SET_CONTROL_LABEL(iControl, strLabel);
918 std::string CGUIDialogVideoInfo::GetThumbnail() const
920 return m_movieItem->GetArt("thumb");
923 void CGUIDialogVideoInfo::AddItemPathToFileBrowserSources(VECSOURCES &sources, const CFileItem &item)
925 if (!item.HasVideoInfoTag())
928 CStdString itemDir = item.GetVideoInfoTag()->m_basePath;
932 itemDir = item.GetVideoInfoTag()->GetPath();
934 CFileItem itemTmp(itemDir, false);
935 if (itemTmp.IsVideo())
936 itemDir = URIUtils::GetParentPath(itemDir);
938 if (!itemDir.empty() && CDirectory::Exists(itemDir))
940 CMediaSource itemSource;
941 itemSource.strName = g_localizeStrings.Get(36041);
942 itemSource.strPath = itemDir;
943 sources.push_back(itemSource);
947 int CGUIDialogVideoInfo::ManageVideoItem(const CFileItemPtr &item)
949 if (item == NULL || !item->IsVideoDb() || !item->HasVideoInfoTag() || item->GetVideoInfoTag()->m_iDbId < 0)
952 CVideoDatabase database;
953 if (!database.Open())
956 const std::string &type = item->GetVideoInfoTag()->m_type;
957 int dbId = item->GetVideoInfoTag()->m_iDbId;
959 CContextButtons buttons;
960 if (type == "movie" || type == "set" ||
961 type == "tvshow" || type == "episode" ||
962 type == "musicvideo")
963 buttons.Add(CONTEXT_BUTTON_EDIT, 16105);
965 if (type == "movie" || type == "tvshow")
966 buttons.Add(CONTEXT_BUTTON_EDIT_SORTTITLE, 16107);
968 if (item->m_bIsFolder)
970 // Have both options for folders since we don't know whether all childs are watched/unwatched
971 buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
972 buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103); //Mark as Watched
976 if (item->GetOverlayImage().Equals("OverlayWatched.png"))
977 buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
979 buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103); //Mark as Watched
984 // only show link/unlink if there are tvshows available
985 if (database.HasContent(VIDEODB_CONTENT_TVSHOWS))
987 buttons.Add(CONTEXT_BUTTON_LINK_MOVIE, 20384);
988 if (database.IsLinkedToTvshow(dbId))
989 buttons.Add(CONTEXT_BUTTON_UNLINK_MOVIE, 20385);
992 // set or change movie set the movie belongs to
993 buttons.Add(CONTEXT_BUTTON_SET_MOVIESET, 20465);
996 if (type == "episode" &&
997 item->GetVideoInfoTag()->m_iBookmarkId > 0)
998 buttons.Add(CONTEXT_BUTTON_UNLINK_BOOKMARK, 20405);
1001 if (item->m_bIsFolder && type == "set")
1003 buttons.Add(CONTEXT_BUTTON_SET_MOVIESET_ART, 13511);
1004 buttons.Add(CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS, 20465);
1008 if (item->m_bIsFolder && type == "tag")
1010 CVideoDbUrl videoUrl;
1011 if (videoUrl.FromString(item->GetPath()))
1013 const std::string &mediaType = videoUrl.GetItemType();
1015 buttons.Add(CONTEXT_BUTTON_TAGS_ADD_ITEMS, StringUtils::Format(g_localizeStrings.Get(20460), GetLocalizedVideoType(mediaType).c_str()));
1016 buttons.Add(CONTEXT_BUTTON_TAGS_REMOVE_ITEMS, StringUtils::Format(g_localizeStrings.Get(20461), GetLocalizedVideoType(mediaType).c_str()));
1020 buttons.Add(CONTEXT_BUTTON_DELETE, 646);
1022 bool result = false;
1023 int button = CGUIDialogContextMenu::ShowAndGetChoice(buttons);
1026 switch ((CONTEXT_BUTTON)button)
1028 case CONTEXT_BUTTON_EDIT:
1029 result = UpdateVideoItemTitle(item);
1032 case CONTEXT_BUTTON_EDIT_SORTTITLE:
1033 result = UpdateVideoItemSortTitle(item);
1036 case CONTEXT_BUTTON_MARK_WATCHED:
1037 result = MarkWatched(item, true);
1040 case CONTEXT_BUTTON_MARK_UNWATCHED:
1041 result = MarkWatched(item, false);
1044 case CONTEXT_BUTTON_LINK_MOVIE:
1045 result = LinkMovieToTvShow(item, false, database);
1048 case CONTEXT_BUTTON_UNLINK_MOVIE:
1049 result = LinkMovieToTvShow(item, true, database);
1052 case CONTEXT_BUTTON_SET_MOVIESET:
1054 CFileItemPtr selectedSet;
1055 if (GetSetForMovie(item.get(), selectedSet))
1056 result = SetMovieSet(item.get(), selectedSet.get());
1060 case CONTEXT_BUTTON_UNLINK_BOOKMARK:
1061 database.DeleteBookMarkForEpisode(*item->GetVideoInfoTag());
1065 case CONTEXT_BUTTON_DELETE:
1066 result = DeleteVideoItem(item);
1069 case CONTEXT_BUTTON_SET_MOVIESET_ART:
1070 result = ManageVideoItemArtwork(item, "set");
1073 case CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS:
1074 result = ManageMovieSets(item);
1077 case CONTEXT_BUTTON_TAGS_ADD_ITEMS:
1078 result = AddItemsToTag(item);
1081 case CONTEXT_BUTTON_TAGS_REMOVE_ITEMS:
1082 result = RemoveItemsFromTag(item);
1099 //Add change a title's name
1100 bool CGUIDialogVideoInfo::UpdateVideoItemTitle(const CFileItemPtr &pItem)
1102 if (pItem == NULL || !pItem->HasVideoInfoTag())
1105 // dont allow update while scanning
1106 if (g_application.IsVideoScanning())
1108 CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1112 CVideoDatabase database;
1113 if (!database.Open())
1116 int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
1117 CVideoInfoTag detail;
1118 VIDEODB_CONTENT_TYPE iType = (VIDEODB_CONTENT_TYPE)pItem->GetVideoContentType();
1119 if (iType == VIDEODB_CONTENT_MOVIES)
1120 database.GetMovieInfo("", detail, iDbId);
1121 else if (iType == VIDEODB_CONTENT_MOVIE_SETS)
1122 database.GetSetInfo(iDbId, detail);
1123 else if (iType == VIDEODB_CONTENT_EPISODES)
1124 database.GetEpisodeInfo(pItem->GetPath(), detail, iDbId);
1125 else if (iType == VIDEODB_CONTENT_TVSHOWS)
1126 database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId);
1127 else if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
1128 database.GetMusicVideoInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId);
1130 // get the new title
1131 if (!CGUIKeyboardFactory::ShowAndGetInput(detail.m_strTitle, g_localizeStrings.Get(16105), false))
1134 database.UpdateMovieTitle(iDbId, detail.m_strTitle, iType);
1138 bool CGUIDialogVideoInfo::MarkWatched(const CFileItemPtr &item, bool bMark)
1140 if (item == NULL || !CProfilesManager::Get().GetCurrentProfile().canWriteDatabases())
1143 // dont allow update while scanning
1144 if (g_application.IsVideoScanning())
1146 CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1150 CVideoDatabase database;
1151 if (!database.Open())
1154 CFileItemList items;
1155 if (item->m_bIsFolder)
1157 CStdString strPath = item->GetPath();
1158 CDirectory::GetDirectory(strPath, items);
1163 for (int i = 0; i < items.Size(); ++i)
1165 CFileItemPtr pTmpItem = items[i];
1166 if (pTmpItem->m_bIsFolder)
1168 MarkWatched(pTmpItem, bMark);
1172 if (pTmpItem->HasVideoInfoTag() &&
1173 ((bMark && pTmpItem->GetVideoInfoTag()->m_playCount) ||
1174 (!bMark && !pTmpItem->GetVideoInfoTag()->m_playCount)))
1178 if (!URIUtils::IsUPnP(item->GetPath()) || !UPNP::CUPnP::MarkWatched(*pTmpItem, bMark))
1181 // Clear resume bookmark
1183 database.ClearBookMarksOfFile(pTmpItem->GetPath(), CBookmark::RESUME);
1185 database.SetPlayCount(*pTmpItem, bMark ? 1 : 0);
1194 bool CGUIDialogVideoInfo::CanDeleteVideoItem(const CFileItemPtr &item)
1196 if (item == NULL || !item->HasVideoInfoTag())
1199 CQueryParams params;
1200 CVideoDatabaseDirectory::GetQueryParams(item->GetPath(), params);
1202 return params.GetMovieId() != -1 ||
1203 params.GetEpisodeId() != -1 ||
1204 params.GetMVideoId() != -1 ||
1205 (params.GetTvShowId() != -1 && params.GetSeason() <= -1 &&
1206 !CVideoDatabaseDirectory::IsAllItem(item->GetPath()));
1209 bool CGUIDialogVideoInfo::DeleteVideoItemFromDatabase(const CFileItemPtr &item, bool unavailable /* = false */)
1211 if (item == NULL || !item->HasVideoInfoTag() ||
1212 !CanDeleteVideoItem(item))
1215 // dont allow update while scanning
1216 if (g_application.IsVideoScanning())
1218 CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1222 CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
1223 if (pDialog == NULL)
1227 VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)item->GetVideoContentType();
1230 case VIDEODB_CONTENT_MOVIES:
1233 case VIDEODB_CONTENT_EPISODES:
1236 case VIDEODB_CONTENT_TVSHOWS:
1239 case VIDEODB_CONTENT_MUSICVIDEOS:
1247 pDialog->SetHeading(heading);
1251 pDialog->SetLine(0, g_localizeStrings.Get(662));
1252 pDialog->SetLine(1, g_localizeStrings.Get(663));
1256 pDialog->SetLine(0, StringUtils::Format(g_localizeStrings.Get(433), item->GetLabel().c_str()));
1257 pDialog->SetLine(1, "");
1259 pDialog->SetLine(2, "");
1262 if (!pDialog->IsConfirmed())
1266 CVideoDatabase database;
1269 database.GetFilePathById(item->GetVideoInfoTag()->m_iDbId, path, type);
1275 case VIDEODB_CONTENT_MOVIES:
1276 database.DeleteMovie(path);
1278 case VIDEODB_CONTENT_EPISODES:
1279 database.DeleteEpisode(path, item->GetVideoInfoTag()->m_iDbId);
1281 case VIDEODB_CONTENT_TVSHOWS:
1282 database.DeleteTvShow(path);
1284 case VIDEODB_CONTENT_MUSICVIDEOS:
1285 database.DeleteMusicVideo(path);
1292 if (type == VIDEODB_CONTENT_TVSHOWS)
1293 database.SetPathHash(path,"");
1295 database.SetPathHash(URIUtils::GetDirectory(path), "");
1300 bool CGUIDialogVideoInfo::DeleteVideoItem(const CFileItemPtr &item, bool unavailable /* = false */)
1305 // delete the video item from the database
1306 if (!DeleteVideoItemFromDatabase(item, unavailable))
1309 // check if the user is allowed to delete the actual file as well
1310 if ((CProfilesManager::Get().GetCurrentProfile().getLockMode() == LOCK_MODE_EVERYONE ||
1311 !CProfilesManager::Get().GetCurrentProfile().filesLocked() ||
1312 g_passwordManager.IsMasterLockUnlocked(true)) &&
1313 CSettings::Get().GetBool("filelists.allowfiledeletion"))
1315 CStdString strDeletePath = item->GetVideoInfoTag()->GetPath();
1317 if (URIUtils::GetFileName(strDeletePath).Equals("VIDEO_TS.IFO"))
1319 strDeletePath = URIUtils::GetDirectory(strDeletePath);
1320 if (StringUtils::EndsWithNoCase(strDeletePath, "video_ts/"))
1322 URIUtils::RemoveSlashAtEnd(strDeletePath);
1323 strDeletePath = URIUtils::GetDirectory(strDeletePath);
1326 if (URIUtils::HasSlashAtEnd(strDeletePath))
1327 item->m_bIsFolder = true;
1329 // check if the file/directory can be deleted
1330 if (CUtil::SupportsWriteFileOperations(strDeletePath))
1332 item->SetPath(strDeletePath);
1334 // HACK: stacked files need to be treated as folders in order to be deleted
1335 if (item->IsStack())
1336 item->m_bIsFolder = true;
1337 CFileUtils::DeleteItem(item);
1341 CUtil::DeleteVideoDatabaseDirectoryCache();
1346 bool CGUIDialogVideoInfo::ManageMovieSets(const CFileItemPtr &item)
1351 CFileItemList originalItems;
1352 CFileItemList selectedItems;
1354 if (!GetMoviesForSet(item.get(), originalItems, selectedItems) ||
1355 selectedItems.Size() == 0) // need at least one item selected
1358 VECFILEITEMS original = originalItems.GetList();
1359 std::sort(original.begin(), original.end(), compFileItemsByDbId);
1360 VECFILEITEMS selected = selectedItems.GetList();
1361 std::sort(selected.begin(), selected.end(), compFileItemsByDbId);
1363 bool refreshNeeded = false;
1364 // update the "added" items
1365 VECFILEITEMS addedItems;
1366 set_difference(selected.begin(),selected.end(), original.begin(),original.end(), std::back_inserter(addedItems), compFileItemsByDbId);
1367 for (VECFILEITEMS::const_iterator it = addedItems.begin(); it != addedItems.end(); ++it)
1369 if (SetMovieSet(it->get(), item.get()))
1370 refreshNeeded = true;
1373 // update the "deleted" items
1374 CFileItemPtr clearItem(new CFileItem());
1375 clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
1376 VECFILEITEMS deletedItems;
1377 set_difference(original.begin(),original.end(), selected.begin(),selected.end(), std::back_inserter(deletedItems), compFileItemsByDbId);
1378 for (VECFILEITEMS::iterator it = deletedItems.begin(); it != deletedItems.end(); ++it)
1380 if (SetMovieSet(it->get(), clearItem.get()))
1381 refreshNeeded = true;
1384 return refreshNeeded;
1387 bool CGUIDialogVideoInfo::GetMoviesForSet(const CFileItem *setItem, CFileItemList &originalMovies, CFileItemList &selectedMovies)
1389 if (setItem == NULL || !setItem->HasVideoInfoTag())
1392 CVideoDatabase videodb;
1393 if (!videodb.Open())
1396 CStdString strHeading = StringUtils::Format(g_localizeStrings.Get(20457));
1397 CStdString baseDir = StringUtils::Format("videodb://movies/sets/%d", setItem->GetVideoInfoTag()->m_iDbId);
1399 if (!CDirectory::GetDirectory(baseDir, originalMovies) || originalMovies.Size() <= 0) // keep a copy of the original members of the set
1402 CFileItemList listItems;
1403 if (!videodb.GetSortedVideos(MediaTypeMovie, "videodb://movies", SortDescription(), listItems) || listItems.Size() <= 0)
1406 CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1410 listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1413 dialog->SetMultiSelection(true);
1414 dialog->SetHeading(strHeading);
1415 dialog->SetItems(&listItems);
1416 vector<int> selectedIndices;
1417 for (int i = 0; i < originalMovies.Size(); i++)
1419 for (int listIndex = 0; listIndex < listItems.Size(); listIndex++)
1421 if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == originalMovies[i]->GetVideoInfoTag()->m_iDbId)
1423 selectedIndices.push_back(listIndex);
1428 dialog->SetSelected(selectedIndices);
1429 dialog->EnableButton(true, 186);
1432 if (dialog->IsConfirmed())
1434 selectedMovies.Copy(dialog->GetSelectedItems());
1435 return (selectedMovies.Size() > 0);
1441 bool CGUIDialogVideoInfo::GetSetForMovie(const CFileItem *movieItem, CFileItemPtr &selectedSet)
1443 if (movieItem == NULL || !movieItem->HasVideoInfoTag())
1446 CVideoDatabase videodb;
1447 if (!videodb.Open())
1450 CFileItemList listItems;
1451 CStdString baseDir = "videodb://movies/sets/";
1452 if (!CDirectory::GetDirectory(baseDir, listItems) || listItems.Size() <= 0)
1454 listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1456 int currentSetId = 0;
1457 CStdString currentSetLabel;
1459 if (movieItem->GetVideoInfoTag()->m_iSetId > currentSetId)
1461 currentSetId = movieItem->GetVideoInfoTag()->m_iSetId;
1462 currentSetLabel = videodb.GetSetById(currentSetId);
1465 if (currentSetId > 0)
1468 CStdString strClear = StringUtils::Format(g_localizeStrings.Get(20467), currentSetLabel.c_str());
1469 CFileItemPtr clearItem(new CFileItem(strClear));
1470 clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
1471 listItems.AddFront(clearItem, 0);
1472 // add keep current set item
1473 CStdString strKeep = StringUtils::Format(g_localizeStrings.Get(20469), currentSetLabel.c_str());
1474 CFileItemPtr keepItem(new CFileItem(strKeep));
1475 keepItem->GetVideoInfoTag()->m_iDbId = currentSetId;
1476 listItems.AddFront(keepItem, 1);
1479 CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1483 CStdString strHeading = StringUtils::Format(g_localizeStrings.Get(20466));
1485 dialog->SetHeading(strHeading);
1486 dialog->SetItems(&listItems);
1487 if (currentSetId >= 0)
1489 for (int listIndex = 0; listIndex < listItems.Size(); listIndex++)
1491 if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == currentSetId)
1493 dialog->SetSelected(listIndex);
1498 dialog->EnableButton(true, 20468); // new set via button
1501 if (dialog->IsButtonPressed())
1502 { // creating new set
1503 CStdString newSetTitle;
1504 if (!CGUIKeyboardFactory::ShowAndGetInput(newSetTitle, g_localizeStrings.Get(20468), false))
1506 int idSet = videodb.AddSet(newSetTitle);
1507 map<string, string> movieArt, setArt;
1508 if (!videodb.GetArtForItem(idSet, "set", setArt))
1510 videodb.GetArtForItem(movieItem->GetVideoInfoTag()->m_iDbId, "movie", movieArt);
1511 videodb.SetArtForItem(idSet, "set", movieArt);
1513 CFileItemPtr newSet(new CFileItem(newSetTitle));
1514 newSet->GetVideoInfoTag()->m_iDbId = idSet;
1515 selectedSet = newSet;
1518 else if (dialog->IsConfirmed())
1520 selectedSet = dialog->GetSelectedItem();
1521 return (selectedSet != NULL);
1527 bool CGUIDialogVideoInfo::SetMovieSet(const CFileItem *movieItem, const CFileItem *selectedSet)
1529 if (movieItem == NULL || !movieItem->HasVideoInfoTag() ||
1530 selectedSet == NULL || !selectedSet->HasVideoInfoTag())
1533 CVideoDatabase videodb;
1534 if (!videodb.Open())
1537 videodb.SetMovieSet(movieItem->GetVideoInfoTag()->m_iDbId, selectedSet->GetVideoInfoTag()->m_iDbId);
1541 bool CGUIDialogVideoInfo::GetItemsForTag(const CStdString &strHeading, const std::string &type, CFileItemList &items, int idTag /* = -1 */, bool showAll /* = true */)
1543 CVideoDatabase videodb;
1544 if (!videodb.Open())
1547 MediaType mediaType = MediaTypeNone;
1548 std::string baseDir = "videodb://";
1549 std::string idColumn;
1550 if (type.compare("movie") == 0)
1552 mediaType = MediaTypeMovie;
1553 baseDir += "movies";
1554 idColumn = "idMovie";
1556 else if (type.compare("tvshow") == 0)
1558 mediaType = MediaTypeTvShow;
1559 baseDir += "tvshows";
1560 idColumn = "idShow";
1562 else if (type.compare("musicvideo") == 0)
1564 mediaType = MediaTypeMusicVideo;
1565 baseDir += "musicvideos";
1566 idColumn = "idMVideo";
1569 baseDir += "/titles/";
1570 CVideoDbUrl videoUrl;
1571 if (!videoUrl.FromString(baseDir))
1574 CVideoDatabase::Filter filter;
1578 videoUrl.AddOption("tagid", idTag);
1580 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());
1583 CFileItemList listItems;
1584 if (!videodb.GetSortedVideos(mediaType, videoUrl.ToString(), SortDescription(), listItems, filter) || listItems.Size() <= 0)
1587 CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1591 listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1594 dialog->SetMultiSelection(true);
1595 dialog->SetHeading(strHeading);
1596 dialog->SetItems(&listItems);
1597 dialog->EnableButton(true, 186);
1600 items.Copy(dialog->GetSelectedItems());
1601 return items.Size() > 0;
1604 bool CGUIDialogVideoInfo::AddItemsToTag(const CFileItemPtr &tagItem)
1606 if (tagItem == NULL || !tagItem->HasVideoInfoTag())
1609 CVideoDbUrl videoUrl;
1610 if (!videoUrl.FromString(tagItem->GetPath()))
1613 CVideoDatabase videodb;
1614 if (!videodb.Open())
1617 std::string mediaType = videoUrl.GetItemType();
1618 mediaType = mediaType.substr(0, mediaType.length() - 1);
1620 CFileItemList items;
1621 std::string localizedType = GetLocalizedVideoType(mediaType);
1622 std::string strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType.c_str());
1623 if (!GetItemsForTag(strLabel, mediaType, items, tagItem->GetVideoInfoTag()->m_iDbId))
1626 for (int index = 0; index < items.Size(); index++)
1628 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1631 videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, tagItem->GetVideoInfoTag()->m_iDbId, mediaType);
1637 bool CGUIDialogVideoInfo::RemoveItemsFromTag(const CFileItemPtr &tagItem)
1639 if (tagItem == NULL || !tagItem->HasVideoInfoTag())
1642 CVideoDbUrl videoUrl;
1643 if (!videoUrl.FromString(tagItem->GetPath()))
1646 CVideoDatabase videodb;
1647 if (!videodb.Open())
1650 std::string mediaType = videoUrl.GetItemType();
1651 mediaType = mediaType.substr(0, mediaType.length() - 1);
1653 CFileItemList items;
1654 std::string localizedType = GetLocalizedVideoType(mediaType);
1655 std::string strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType.c_str());
1656 if (!GetItemsForTag(strLabel, mediaType, items, tagItem->GetVideoInfoTag()->m_iDbId, false))
1659 for (int index = 0; index < items.Size(); index++)
1661 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1664 videodb.RemoveTagFromItem(items[index]->GetVideoInfoTag()->m_iDbId, tagItem->GetVideoInfoTag()->m_iDbId, mediaType);
1670 bool CGUIDialogVideoInfo::ManageVideoItemArtwork(const CFileItemPtr &item, const std::string &type)
1672 if (item == NULL || !item->HasVideoInfoTag() || type.empty())
1675 CVideoDatabase videodb;
1676 if (!videodb.Open())
1679 // Grab the thumbnails from the web
1680 CFileItemList items;
1681 CFileItemPtr noneitem(new CFileItem("thumb://None", false));
1682 std::string currentThumb;
1684 CStdString artistPath;
1685 string artType = "thumb";
1686 if (type == "artist")
1688 CMusicDatabase musicdb;
1691 idArtist = musicdb.GetArtistByName(item->GetLabel());
1692 if (idArtist >= 0 && musicdb.GetArtistPath(idArtist, artistPath))
1694 currentThumb = musicdb.GetArtForItem(idArtist, "artist", "thumb");
1695 if (currentThumb.empty())
1696 currentThumb = videodb.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType);
1700 else if (type == "actor")
1701 currentThumb = videodb.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType);
1704 map<string, string> currentArt;
1705 artType = ChooseArtType(*item, currentArt);
1706 if (artType.empty())
1709 if (artType == "fanart")
1710 return OnGetFanart(item);
1712 if (currentArt.find(artType) != currentArt.end())
1713 currentThumb = currentArt[artType];
1714 else if ((artType == "poster" || artType == "banner") && currentArt.find("thumb") != currentArt.end())
1715 currentThumb = currentArt["thumb"];
1718 if (!currentThumb.empty())
1720 CFileItemPtr item(new CFileItem("thumb://Current", false));
1721 item->SetArt("thumb", currentThumb);
1722 item->SetLabel(g_localizeStrings.Get(13512));
1725 noneitem->SetIconImage("DefaultFolder.png");
1726 noneitem->SetLabel(g_localizeStrings.Get(13515));
1729 vector<CStdString> thumbs;
1730 if (type != "artist")
1733 if (type == "season")
1734 videodb.GetTvShowInfo("", tag, item->GetVideoInfoTag()->m_iIdShow);
1736 tag = *item->GetVideoInfoTag();
1738 tag.m_strPictureURL.GetThumbURLs(thumbs, artType, type == "season" ? item->GetVideoInfoTag()->m_iSeason : -1);
1740 for (size_t i = 0; i < thumbs.size(); i++)
1742 CFileItemPtr item(new CFileItem(StringUtils::Format("thumb://Remote%i", i), false));
1743 item->SetArt("thumb", thumbs[i]);
1744 item->SetIconImage("DefaultPicture.png");
1745 item->SetLabel(g_localizeStrings.Get(13513));
1748 // TODO: Do we need to clear the cached image?
1749 // CTextureCache::Get().ClearCachedImage(thumbs[i]);
1752 if (type == "actor")
1754 CStdString picturePath;
1755 CStdString strThumb = URIUtils::AddFileToFolder(picturePath, "folder.jpg");
1756 if (XFILE::CFile::Exists(strThumb))
1758 CFileItemPtr pItem(new CFileItem(strThumb,false));
1759 pItem->SetLabel(g_localizeStrings.Get(13514));
1760 pItem->SetArt("thumb", strThumb);
1765 noneitem->SetIconImage("DefaultActor.png");
1769 noneitem->SetIconImage("DefaultVideo.png");
1773 CStdString strThumb = URIUtils::AddFileToFolder(artistPath, "folder.jpg");
1774 if (XFILE::CFile::Exists(strThumb))
1776 CFileItemPtr pItem(new CFileItem(strThumb, false));
1777 pItem->SetLabel(g_localizeStrings.Get(13514));
1778 pItem->SetArt("thumb", strThumb);
1783 noneitem->SetIconImage("DefaultArtist.png");
1787 items.Add(noneitem);
1790 VECSOURCES sources=*CMediaSourceSettings::Get().GetSources("video");
1791 g_mediaManager.GetLocalDrives(sources);
1792 AddItemPathToFileBrowserSources(sources, *item);
1793 if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result))
1794 return false; // user cancelled
1796 if (result == "thumb://Current")
1797 result = currentThumb; // user chose the one they have
1799 // delete the thumbnail if that's what the user wants, else overwrite with the
1801 if (result == "thumb://None")
1803 else if (StringUtils::StartsWith(result, "thumb://Remote"))
1805 int number = atoi(StringUtils::Mid(result, 14).c_str());
1806 result = thumbs[number];
1809 // write the selected artwork to the database
1810 if (type == "set" ||
1813 (type == "artist" && idArtist < 0))
1814 videodb.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType, result);
1817 CMusicDatabase musicdb;
1819 musicdb.SetArtForItem(idArtist, "artist", artType, result);
1822 CUtil::DeleteVideoDatabaseDirectoryCache();
1823 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
1824 g_windowManager.SendMessage(msg);
1829 std::string CGUIDialogVideoInfo::GetLocalizedVideoType(const std::string &strType)
1831 if (strType == "movie" || strType == "movies")
1832 return g_localizeStrings.Get(20342);
1833 else if (strType == "tvshow" || strType == "tvshows")
1834 return g_localizeStrings.Get(20343);
1835 else if (strType == "episode" || strType == "episodes")
1836 return g_localizeStrings.Get(20359);
1837 else if (strType == "musicvideo" || strType == "musicvideos")
1838 return g_localizeStrings.Get(20391);
1843 bool CGUIDialogVideoInfo::UpdateVideoItemSortTitle(const CFileItemPtr &pItem)
1845 // dont allow update while scanning
1846 if (g_application.IsVideoScanning())
1848 CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1852 CVideoDatabase database;
1853 if (!database.Open())
1856 int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
1857 CVideoInfoTag detail;
1858 VIDEODB_CONTENT_TYPE iType = (VIDEODB_CONTENT_TYPE)pItem->GetVideoContentType();
1859 if (iType == VIDEODB_CONTENT_MOVIES)
1860 database.GetMovieInfo("", detail, iDbId);
1861 else if (iType == VIDEODB_CONTENT_TVSHOWS)
1862 database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId);
1864 CStdString currentTitle;
1865 if (detail.m_strSortTitle.empty())
1866 currentTitle = detail.m_strTitle;
1868 currentTitle = detail.m_strSortTitle;
1870 // get the new sort title
1871 if (!CGUIKeyboardFactory::ShowAndGetInput(currentTitle, g_localizeStrings.Get(16107), false))
1874 return database.UpdateVideoSortTitle(iDbId, currentTitle, iType);
1877 bool CGUIDialogVideoInfo::LinkMovieToTvShow(const CFileItemPtr &item, bool bRemove, CVideoDatabase &database)
1879 int dbId = item->GetVideoInfoTag()->m_iDbId;
1885 if (!database.GetLinksToTvShow(dbId, ids))
1888 for (unsigned int i = 0; i < ids.size(); ++i)
1891 database.GetTvShowInfo("", tag, ids[i]);
1892 CFileItemPtr show(new CFileItem(tag));
1898 database.GetTvShowsNav("videodb://tvshows/titles", list);
1900 // remove already linked shows
1902 if (!database.GetLinksToTvShow(dbId, ids))
1905 for (int i = 0; i < list.Size(); )
1908 for (j = 0; j < ids.size(); ++j)
1910 if (list[i]->GetVideoInfoTag()->m_iDbId == ids[j])
1913 if (j == ids.size())
1920 int iSelectedLabel = 0;
1921 if (list.Size() > 1)
1923 list.Sort(SortByLabel, SortOrderAscending, CSettings::Get().GetBool("filelists.ignorethewhensorting") ? SortAttributeIgnoreArticle : SortAttributeNone);
1924 CGUIDialogSelect* pDialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1926 pDialog->SetItems(&list);
1927 pDialog->SetHeading(20356);
1929 iSelectedLabel = pDialog->GetSelectedLabel();
1932 if (iSelectedLabel > -1)
1933 return database.LinkMovieToTvshow(dbId, list[iSelectedLabel]->GetVideoInfoTag()->m_iDbId, bRemove);
1938 bool CGUIDialogVideoInfo::OnGetFanart(const CFileItemPtr &videoItem)
1940 if (videoItem == NULL || !videoItem->HasVideoInfoTag())
1944 CVideoDatabase videodb;
1945 if (!videodb.Open())
1948 CVideoThumbLoader loader;
1949 CFileItem item(*videoItem);
1950 loader.LoadItem(&item);
1952 CFileItemList items;
1953 if (item.HasArt("fanart"))
1955 CFileItemPtr itemCurrent(new CFileItem("fanart://Current", false));
1956 itemCurrent->SetArt("thumb", item.GetArt("fanart"));
1957 itemCurrent->SetLabel(g_localizeStrings.Get(20440));
1958 items.Add(itemCurrent);
1961 // add the none option
1963 CFileItemPtr itemNone(new CFileItem("fanart://None", false));
1964 itemNone->SetIconImage("DefaultVideo.png");
1965 itemNone->SetLabel(g_localizeStrings.Get(20439));
1966 items.Add(itemNone);
1970 VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
1971 g_mediaManager.GetLocalDrives(sources);
1972 AddItemPathToFileBrowserSources(sources, item);
1974 if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) ||
1975 StringUtils::EqualsNoCase(result, "fanart://Current"))
1978 if (StringUtils::EqualsNoCase(result, "fanart://None") || !CFile::Exists(result))
1980 if (!result.empty() && flip)
1981 result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
1983 videodb.SetArtForItem(item.GetVideoInfoTag()->m_iDbId, item.GetVideoInfoTag()->m_type, "fanart", result);
1985 // clear view cache and reload images
1986 CUtil::DeleteVideoDatabaseDirectoryCache();