Merge pull request #4173 from Montellese/settings_fixes
[vuplus_xbmc] / xbmc / video / dialogs / GUIDialogVideoInfo.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #include "GUIDialogVideoInfo.h"
22 #include "Application.h"
23 #include "guilib/GUIWindow.h"
24 #include "Util.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"
40 #include "FileItem.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"
52 #include "URL.h"
53 #include "video/VideoThumbLoader.h"
54 #include "filesystem/Directory.h"
55 #include "filesystem/VideoDatabaseDirectory.h"
56 #include "filesystem/VideoDatabaseDirectory/QueryParams.h"
57 #ifdef HAS_UPNP
58 #include "network/upnp/UPnP.h"
59 #endif
60 #include "utils/FileUtils.h"
61
62 using namespace std;
63 using namespace XFILE::VIDEODATABASEDIRECTORY;
64 using namespace XFILE;
65
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
76
77 #define CONTROL_LIST                50
78
79 // predicate used by sorting and set_difference
80 bool compFileItemsByDbId(const CFileItemPtr& lhs, const CFileItemPtr& rhs) 
81 {
82   return lhs->HasVideoInfoTag() && rhs->HasVideoInfoTag() && lhs->GetVideoInfoTag()->m_iDbId < rhs->GetVideoInfoTag()->m_iDbId;
83 }
84
85 CGUIDialogVideoInfo::CGUIDialogVideoInfo(void)
86     : CGUIDialog(WINDOW_DIALOG_VIDEO_INFO, "DialogVideoInfo.xml")
87     , m_movieItem(new CFileItem)
88 {
89   m_bRefreshAll = true;
90   m_bRefresh = false;
91   m_hasUpdatedThumb = false;
92   m_castList = new CFileItemList;
93   m_loadType = KEEP_IN_MEMORY;
94 }
95
96 CGUIDialogVideoInfo::~CGUIDialogVideoInfo(void)
97 {
98   delete m_castList;
99 }
100
101 bool CGUIDialogVideoInfo::OnMessage(CGUIMessage& message)
102 {
103   switch ( message.GetMessage() )
104   {
105   case GUI_MSG_WINDOW_DEINIT:
106     {
107       ClearCastList();
108     }
109     break;
110
111   case GUI_MSG_CLICKED:
112     {
113       int iControl = message.GetSenderId();
114       if (iControl == CONTROL_BTN_REFRESH)
115       {
116         if (m_movieItem->GetVideoInfoTag()->m_iSeason < 0 && !m_movieItem->GetVideoInfoTag()->m_strShowTitle.empty()) // tv show
117         {
118           bool bCanceled=false;
119           if (CGUIDialogYesNo::ShowAndGetInput(20377,20378,-1,-1,bCanceled))
120           {
121             m_bRefreshAll = true;
122             CVideoDatabase db;
123             if (db.Open())
124             {
125               db.SetPathHash(m_movieItem->GetVideoInfoTag()->m_strPath,"");
126               db.Close();
127             }
128           }
129           else
130             m_bRefreshAll = false;
131
132           if (bCanceled)
133             return false;
134         }
135         m_bRefresh = true;
136         Close();
137         return true;
138       }
139       else if (iControl == CONTROL_BTN_TRACKS)
140       {
141         m_bViewReview = !m_bViewReview;
142         Update();
143       }
144       else if (iControl == CONTROL_BTN_PLAY)
145       {
146         Play();
147       }
148       else if (iControl == CONTROL_BTN_RESUME)
149       {
150         Play(true);
151       }
152       else if (iControl == CONTROL_BTN_GET_THUMB)
153       {
154         OnGetArt();
155       }
156       else if (iControl == CONTROL_BTN_PLAY_TRAILER)
157       {
158         PlayTrailer();
159       }
160       else if (iControl == CONTROL_BTN_GET_FANART)
161       {
162         OnGetFanart();
163       }
164       else if (iControl == CONTROL_BTN_DIRECTOR)
165       {
166         CStdString strDirector = StringUtils::Join(m_movieItem->GetVideoInfoTag()->m_director, g_advancedSettings.m_videoItemSeparator);
167         OnSearch(strDirector);
168       }
169       else if (iControl == CONTROL_LIST)
170       {
171         int iAction = message.GetParam1();
172         if (ACTION_SELECT_ITEM == iAction || ACTION_MOUSE_LEFT_CLICK == iAction)
173         {
174           CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl);
175           OnMessage(msg);
176           int iItem = msg.GetParam1();
177           if (iItem < 0 || iItem >= m_castList->Size())
178             break;
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);
185           OnSearch(tmp);
186         }
187       }
188     }
189     break;
190   case GUI_MSG_NOTIFY_ALL:
191     {
192       if (IsActive() && message.GetParam1() == GUI_MSG_UPDATE_ITEM && message.GetItem())
193       {
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;
200         }
201         return true;
202       }
203     }
204   }
205
206   return CGUIDialog::OnMessage(message);
207 }
208
209 void CGUIDialogVideoInfo::OnInitWindow()
210 {
211   m_bRefresh = false;
212   m_bRefreshAll = true;
213   m_hasUpdatedThumb = false;
214   m_bViewReview = true;
215
216   CVideoDatabase database;
217   ADDON::ScraperPtr scraper;
218
219   if(database.Open())
220   {
221     scraper = database.GetScraperForPath(m_movieItem->GetVideoInfoTag()->GetPath());
222     database.Close();
223   }
224
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"));
227
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"));
231   else
232     CONTROL_DISABLE(CONTROL_BTN_GET_FANART);
233
234   Update();
235
236   CGUIDialog::OnInitWindow();
237 }
238
239 bool CGUIDialogVideoInfo::OnAction(const CAction &action)
240 {
241   if (action.GetID() == ACTION_SHOW_INFO)
242   {
243     Close();
244     return true;
245   }
246   return CGUIDialog::OnAction(action);
247 }
248
249 void CGUIDialogVideoInfo::SetMovie(const CFileItem *item)
250 {
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)
256   ClearCastList();
257   VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)m_movieItem->GetVideoContentType();
258   if (type == VIDEODB_CONTENT_MUSICVIDEOS)
259   { // music video
260     CMusicDatabase database;
261     database.Open();
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)
264     {
265       int idArtist = database.GetArtistByName(*it);
266       CStdString thumb = database.GetArtForItem(idArtist, "artist", "thumb");
267       CFileItemPtr item(new CFileItem(*it));
268       if (!thumb.empty())
269         item->SetArt("thumb", thumb);
270       item->SetIconImage("DefaultArtist.png");
271       m_castList->Add(item);
272     }
273     m_castList->SetContent("musicvideos");
274   }
275   else
276   { // movie/show/episode
277     for (CVideoInfoTag::iCast it = m_movieItem->GetVideoInfoTag()->m_cast.begin(); it != m_movieItem->GetVideoInfoTag()->m_cast.end(); ++it)
278     {
279       CStdString character;
280       if (it->strRole.empty())
281         character = it->strName;
282       else
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());
290         if (!thumb.empty())
291         {
292           item->SetArt("thumb", thumb);
293           CTextureCache::Get().BackgroundCacheImage(thumb);
294         }
295       }
296       item->SetIconImage("DefaultActor.png");
297       item->SetLabel(character);
298       m_castList->Add(item);
299     }
300     // determine type:
301     if (type == VIDEODB_CONTENT_TVSHOWS)
302     {
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"))
309       {
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;
315       }
316     }
317     else if (type == VIDEODB_CONTENT_EPISODES)
318     {
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?
326       CVideoDatabase db;
327       if (db.Open())
328       {
329         if (m_movieItem->GetVideoInfoTag()->m_iSeason > -1)
330         {
331           int seasonID = m_movieItem->GetVideoInfoTag()->m_iIdSeason;
332           if (seasonID < 0)
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))
337           {
338             for (CGUIListItem::ArtMap::iterator i = thumbs.begin(); i != thumbs.end(); i++)
339               m_movieItem->SetArt("season." + i->first, i->second);
340           }
341         }
342         db.Close();
343       }
344     }
345     else if (type == VIDEODB_CONTENT_MOVIES)
346     {
347       m_castList->SetContent("movies");
348
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))
353       {
354         CStdString localTrailer = m_movieItem->FindTrailer();
355         if (!localTrailer.empty())
356         {
357           m_movieItem->GetVideoInfoTag()->m_strTrailer = localTrailer;
358           CVideoDatabase database;
359           if(database.Open())
360           {
361             database.SetSingleValue(VIDEODB_CONTENT_MOVIES, VIDEODB_ID_TRAILER,
362                                     m_movieItem->GetVideoInfoTag()->m_iDbId,
363                                     m_movieItem->GetVideoInfoTag()->m_strTrailer);
364             database.Close();
365             CUtil::DeleteVideoDatabaseDirectoryCache();
366           }
367         }
368       }
369     }
370   }
371   CVideoThumbLoader loader;
372   loader.LoadItem(m_movieItem.get());
373 }
374
375 void CGUIDialogVideoInfo::Update()
376 {
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);
382
383   StringUtils::Trim(strTmp);
384   SetLabel(CONTROL_TEXTAREA, strTmp);
385
386   CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST, 0, 0, m_castList);
387   OnMessage(msg);
388
389   if (GetControl(CONTROL_BTN_TRACKS)) // if no CONTROL_BTN_TRACKS found - allow skinner full visibility control over CONTROL_TEXTAREA and CONTROL_LIST
390   {
391     if (m_bViewReview)
392     {
393       if (!m_movieItem->GetVideoInfoTag()->m_artist.empty())
394       {
395         SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 133);
396       }
397       else
398       {
399         SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 206);
400       }
401
402       SET_CONTROL_HIDDEN(CONTROL_LIST);
403       SET_CONTROL_VISIBLE(CONTROL_TEXTAREA);
404     }
405     else
406     {
407       SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 207);
408
409       SET_CONTROL_HIDDEN(CONTROL_TEXTAREA);
410       SET_CONTROL_VISIBLE(CONTROL_LIST);
411     }
412   }
413
414   // Check for resumability
415   if (m_movieItem->GetVideoInfoTag()->m_resumePoint.timeInSeconds > 0.0)
416     CONTROL_ENABLE(CONTROL_BTN_RESUME);
417   else
418     CONTROL_DISABLE(CONTROL_BTN_RESUME);
419
420   CONTROL_ENABLE(CONTROL_BTN_PLAY);
421
422   // update the thumbnail
423   const CGUIControl* pControl = GetControl(CONTROL_IMAGE);
424   if (pControl)
425   {
426     CGUIImage* pImageControl = (CGUIImage*)pControl;
427     pImageControl->FreeResources();
428     pImageControl->SetFileName(m_movieItem->GetArt("thumb"));
429   }
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)
433   {
434     CGUIMessage reload(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
435     g_windowManager.SendMessage(reload);
436   }
437 }
438
439 bool CGUIDialogVideoInfo::NeedRefresh() const
440 {
441   return m_bRefresh;
442 }
443
444 bool CGUIDialogVideoInfo::RefreshAll() const
445 {
446   return m_bRefreshAll;
447 }
448
449 /// \brief Search the current directory for a string got from the virtual keyboard
450 void CGUIDialogVideoInfo::OnSearch(CStdString& strSearch)
451 {
452   CGUIDialogProgress *progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
453   if (progress)
454   {
455     progress->SetHeading(194);
456     progress->SetLine(0, strSearch);
457     progress->SetLine(1, "");
458     progress->SetLine(2, "");
459     progress->StartModal();
460     progress->Progress();
461   }
462   CFileItemList items;
463   DoSearch(strSearch, items);
464
465   if (progress)
466     progress->Close();
467
468   if (items.Size())
469   {
470     CGUIDialogSelect* pDlgSelect = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
471     pDlgSelect->Reset();
472     pDlgSelect->SetHeading(283);
473
474     for (int i = 0; i < (int)items.Size(); i++)
475     {
476       CFileItemPtr pItem = items[i];
477       pDlgSelect->Add(pItem->GetLabel());
478     }
479
480     pDlgSelect->DoModal();
481
482     int iItem = pDlgSelect->GetSelectedLabel();
483     if (iItem < 0)
484       return;
485
486     CFileItem* pSelItem = new CFileItem(*items[iItem]);
487
488     OnSearchItemFound(pSelItem);
489
490     delete pSelItem;
491   }
492   else
493   {
494     CGUIDialogOK::ShowAndGetInput(194, 284, 0, 0);
495   }
496 }
497
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)
502 {
503   CVideoDatabase db;
504   if (!db.Open())
505     return;
506
507   CFileItemList movies;
508   db.GetMoviesByActor(strSearch, movies);
509   for (int i = 0; i < movies.Size(); ++i)
510   {
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);
515   }
516   CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20338) + "] ", items);
517
518   db.GetTvShowsByActor(strSearch, movies);
519   for (int i = 0; i < movies.Size(); ++i)
520   {
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);
525   }
526   CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20364) + "] ", items);
527
528   db.GetEpisodesByActor(strSearch, movies);
529   for (int i = 0; i < movies.Size(); ++i)
530   {
531     CStdString label = movies[i]->GetVideoInfoTag()->m_strTitle + " (" +  movies[i]->GetVideoInfoTag()->m_strShowTitle + ")";
532     movies[i]->SetLabel(label);
533   }
534   CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20359) + "] ", items);
535
536   db.GetMusicVideosByArtist(strSearch, movies);
537   for (int i = 0; i < movies.Size(); ++i)
538   {
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);
543   }
544   CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20391) + "] ", items);
545   db.Close();
546 }
547
548 /// \brief React on the selected search item
549 /// \param pItem Search result item
550 void CGUIDialogVideoInfo::OnSearchItemFound(const CFileItem* pItem)
551 {
552   VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)pItem->GetVideoContentType();
553
554   CVideoDatabase db;
555   if (!db.Open())
556     return;
557
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);
567   db.Close();
568
569   CFileItem item(*pItem);
570   *item.GetVideoInfoTag() = movieDetails;
571   SetMovie(&item);
572   // refresh our window entirely
573   Close();
574   DoModal();
575 }
576
577 void CGUIDialogVideoInfo::ClearCastList()
578 {
579   CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_LIST);
580   OnMessage(msg);
581   m_castList->Clear();
582 }
583
584 void CGUIDialogVideoInfo::Play(bool resume)
585 {
586   if (!m_movieItem->GetVideoInfoTag()->m_strEpisodeGuide.empty())
587   {
588     CStdString strPath = StringUtils::Format("videodb://tvshows/titles/%i/",m_movieItem->GetVideoInfoTag()->m_iDbId);
589     Close();
590     g_windowManager.ActivateWindow(WINDOW_VIDEO_NAV,strPath);
591     return;
592   }
593
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);
598   if (pWindow)
599   {
600     // close our dialog
601     Close(true);
602     if (resume)
603       movie.m_lStartOffset = STARTOFFSET_RESUME;
604     else if (!CGUIWindowVideoBase::ShowResumeMenu(movie)) 
605     {
606       // The Resume dialog was closed without any choice
607       DoModal();
608       return;
609     }
610     pWindow->PlayMovie(&movie);
611   }
612 }
613
614 string CGUIDialogVideoInfo::ChooseArtType(const CFileItem &videoItem, map<string, string> &currentArt)
615 {
616   // prompt for choice
617   CGUIDialogSelect *dialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
618   if (!dialog || !videoItem.HasVideoInfoTag())
619     return "";
620
621   CFileItemList items;
622   dialog->SetHeading(13511);
623   dialog->Reset();
624   dialog->SetUseDetails(true);
625   dialog->EnableButton(true, 13516);
626
627   CVideoDatabase db;
628   db.Open();
629
630   vector<string> artTypes = CVideoThumbLoader::GetArtTypes(videoItem.GetVideoInfoTag()->m_type);
631
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)
635   {
636     if (!i->second.empty() && find(artTypes.begin(), artTypes.end(), i->first) == artTypes.end())
637       artTypes.push_back(i->first);
638   }
639
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++)
644   {
645     if (find(artTypes.begin(), artTypes.end(), *it) == artTypes.end())
646       artTypes.push_back(*it);
647   }
648
649   for (vector<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
650   {
651     string type = *i;
652     CFileItemPtr item(new CFileItem(type, "false"));
653     item->SetLabel(type);
654     if (videoItem.HasArt(type))
655       item->SetArt("thumb", videoItem.GetArt(type));
656     items.Add(item);
657   }
658
659   dialog->SetItems(&items);
660   dialog->DoModal();
661
662   if (dialog->IsButtonPressed())
663   {
664     // Get the new artwork name
665     CStdString strArtworkName;
666     if (!CGUIKeyboardFactory::ShowAndGetInput(strArtworkName, g_localizeStrings.Get(13516), false))
667       return "";
668
669     return strArtworkName;
670   }
671
672   return dialog->GetSelectedItem()->GetLabel();
673 }
674
675 void CGUIDialogVideoInfo::OnGetArt()
676 {
677   map<string, string> currentArt;
678   string type = ChooseArtType(*m_movieItem, currentArt);
679   if (type.empty())
680     return; // cancelled
681
682   // TODO: this can be removed once these are unified.
683   if (type == "fanart")
684     OnGetFanart();
685   else
686   {
687     CFileItemList items;
688
689     // Current thumb
690     if (m_movieItem->HasArt(type))
691     {
692       CFileItemPtr item(new CFileItem("thumb://Current", false));
693       item->SetArt("thumb", m_movieItem->GetArt(type));
694       item->SetLabel(g_localizeStrings.Get(13512));
695       items.Add(item);
696     }
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));
702       items.Add(item);
703     }
704
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);
709
710     for (unsigned int i = 0; i < thumbs.size(); ++i)
711     {
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));
717
718       // TODO: Do we need to clear the cached image?
719       //    CTextureCache::Get().ClearCachedImage(thumb);
720       items.Add(item);
721     }
722
723     CStdString localThumb = CVideoThumbLoader::GetLocalArt(*m_movieItem, type);
724     if (!localThumb.empty())
725     {
726       CFileItemPtr item(new CFileItem("thumb://Local", false));
727       item->SetArt("thumb", localThumb);
728       item->SetLabel(g_localizeStrings.Get(13514));
729       items.Add(item);
730     }
731     else
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));
738       items.Add(item);
739     }
740
741     CStdString result;
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
747     {
748       CStdString newThumb;
749       if (StringUtils::StartsWith(result, "thumb://Remote"))
750       {
751         int number = atoi(result.substr(14).c_str());
752         newThumb = thumbs[number];
753       }
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))
759         newThumb = result;
760       else // none
761         newThumb.clear();
762
763       // update thumb in the database
764       CVideoDatabase db;
765       if (db.Open())
766       {
767         db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, type, newThumb);
768         db.Close();
769       }
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);
775       }
776       m_hasUpdatedThumb = true;
777     }
778   }
779
780   // Update our screen
781   Update();
782
783   // re-open the art selection dialog as we come back from
784   // the image selection dialog
785   OnGetArt();
786 }
787
788 // Allow user to select a Fanart
789 void CGUIDialogVideoInfo::OnGetFanart()
790 {
791   CFileItemList items;
792
793   CFileItem item(*m_movieItem->GetVideoInfoTag());
794   if (item.HasArt("fanart"))
795   {
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);
800   }
801
802   // ensure the fanart is unpacked
803   m_movieItem->GetVideoInfoTag()->m_fanart.Unpack();
804
805   // Grab the thumbnails from the web
806   for (unsigned int i = 0; i < m_movieItem->GetVideoInfoTag()->m_fanart.GetNumFanarts(); i++)
807   {
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));
814
815     // TODO: Do we need to clear the cached image?
816 //    CTextureCache::Get().ClearCachedImage(thumb);
817     items.Add(item);
818   }
819
820   CStdString strLocal = item.GetLocalFanart();
821   if (!strLocal.empty())
822   {
823     CFileItemPtr itemLocal(new CFileItem("fanart://Local",false));
824     itemLocal->SetArt("thumb", strLocal);
825     itemLocal->SetLabel(g_localizeStrings.Get(20438));
826
827     // TODO: Do we need to clear the cached image?
828     CTextureCache::Get().ClearCachedImage(strLocal);
829     items.Add(itemLocal);
830   }
831   else
832   {
833     CFileItemPtr itemNone(new CFileItem("fanart://None", false));
834     itemNone->SetIconImage("DefaultVideo.png");
835     itemNone->SetLabel(g_localizeStrings.Get(20439));
836     items.Add(itemNone);
837   }
838
839   CStdString result;
840   VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
841   AddItemPathToFileBrowserSources(sources, item);
842   g_mediaManager.GetLocalDrives(sources);
843   bool flip=false;
844   if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) || result.Equals("fanart://Current"))
845     return;   // user cancelled
846
847   if (result.Equals("fanart://Local"))
848     result = strLocal;
849
850   if (StringUtils::StartsWith(result, "fanart://Remote"))
851   {
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);
855     CVideoDatabase db;
856     if (db.Open())
857     {
858       db.UpdateFanart(*m_movieItem, (VIDEODB_CONTENT_TYPE)m_movieItem->GetVideoContentType());
859       db.Close();
860     }
861     result = m_movieItem->GetVideoInfoTag()->m_fanart.GetImageURL();
862   }
863   else if (result.Equals("fanart://None") || !CFile::Exists(result))
864     result.clear();
865
866   // set the fanart image
867   if (flip && !result.empty())
868     result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
869   CVideoDatabase db;
870   if (db.Open())
871   {
872     db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, "fanart", result);
873     db.Close();
874   }
875
876   CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
877   m_movieItem->SetArt("fanart", result);
878   m_hasUpdatedThumb = true;
879
880   // Update our screen
881   Update();
882 }
883
884 void CGUIDialogVideoInfo::PlayTrailer()
885 {
886   CFileItem item;
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;
896
897   // Close the dialog.
898   Close(true);
899
900   if (item.IsPlayList())
901     CApplicationMessenger::Get().MediaPlay(item);
902   else
903     CApplicationMessenger::Get().PlayFile(item);
904 }
905
906 void CGUIDialogVideoInfo::SetLabel(int iControl, const CStdString &strLabel)
907 {
908   if (strLabel.empty())
909   {
910     SET_CONTROL_LABEL(iControl, 416);  // "Not available"
911   }
912   else
913   {
914     SET_CONTROL_LABEL(iControl, strLabel);
915   }
916 }
917
918 std::string CGUIDialogVideoInfo::GetThumbnail() const
919 {
920   return m_movieItem->GetArt("thumb");
921 }
922
923 void CGUIDialogVideoInfo::AddItemPathToFileBrowserSources(VECSOURCES &sources, const CFileItem &item)
924 {
925   if (!item.HasVideoInfoTag())
926     return;
927
928   CStdString itemDir = item.GetVideoInfoTag()->m_basePath;
929
930   //season
931   if (itemDir.empty())
932     itemDir = item.GetVideoInfoTag()->GetPath();
933
934   CFileItem itemTmp(itemDir, false);
935   if (itemTmp.IsVideo())
936     itemDir = URIUtils::GetParentPath(itemDir);
937
938   if (!itemDir.empty() && CDirectory::Exists(itemDir))
939   {
940     CMediaSource itemSource;
941     itemSource.strName = g_localizeStrings.Get(36041);
942     itemSource.strPath = itemDir;
943     sources.push_back(itemSource);
944   }
945 }
946
947 int CGUIDialogVideoInfo::ManageVideoItem(const CFileItemPtr &item)
948 {
949   if (item == NULL || !item->IsVideoDb() || !item->HasVideoInfoTag() || item->GetVideoInfoTag()->m_iDbId < 0)
950     return -1;
951
952   CVideoDatabase database;
953   if (!database.Open())
954     return -1;
955
956   const std::string &type = item->GetVideoInfoTag()->m_type;
957   int dbId = item->GetVideoInfoTag()->m_iDbId;
958
959   CContextButtons buttons;
960   if (type == "movie" || type == "set" ||
961       type == "tvshow" || type == "episode" ||
962       type == "musicvideo")
963     buttons.Add(CONTEXT_BUTTON_EDIT, 16105);
964
965   if (type == "movie" || type == "tvshow")
966     buttons.Add(CONTEXT_BUTTON_EDIT_SORTTITLE, 16107);
967
968   if (item->m_bIsFolder)
969   {
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
973   }
974   else
975   {
976     if (item->GetOverlayImage().Equals("OverlayWatched.png"))
977       buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
978     else
979       buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103);   //Mark as Watched
980   }
981
982   if (type == "movie")
983   {
984     // only show link/unlink if there are tvshows available
985     if (database.HasContent(VIDEODB_CONTENT_TVSHOWS))
986     {
987       buttons.Add(CONTEXT_BUTTON_LINK_MOVIE, 20384);
988       if (database.IsLinkedToTvshow(dbId))
989         buttons.Add(CONTEXT_BUTTON_UNLINK_MOVIE, 20385);
990     }
991
992     // set or change movie set the movie belongs to
993     buttons.Add(CONTEXT_BUTTON_SET_MOVIESET, 20465);
994   }
995
996   if (type == "episode" &&
997       item->GetVideoInfoTag()->m_iBookmarkId > 0)
998     buttons.Add(CONTEXT_BUTTON_UNLINK_BOOKMARK, 20405);
999
1000   // movie sets
1001   if (item->m_bIsFolder && type == "set")
1002   {
1003     buttons.Add(CONTEXT_BUTTON_SET_MOVIESET_ART, 13511);
1004     buttons.Add(CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS, 20465);
1005   }
1006
1007   // tags
1008   if (item->m_bIsFolder && type == "tag")
1009   {
1010     CVideoDbUrl videoUrl;
1011     if (videoUrl.FromString(item->GetPath()))
1012     {
1013       const std::string &mediaType = videoUrl.GetItemType();
1014
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()));
1017     }
1018   }
1019
1020   buttons.Add(CONTEXT_BUTTON_DELETE, 646);
1021
1022   bool result = false;
1023   int button = CGUIDialogContextMenu::ShowAndGetChoice(buttons);
1024   if (button >= 0)
1025   {
1026     switch ((CONTEXT_BUTTON)button)
1027     {
1028       case CONTEXT_BUTTON_EDIT:
1029         result = UpdateVideoItemTitle(item);
1030         break;
1031
1032       case CONTEXT_BUTTON_EDIT_SORTTITLE:
1033         result = UpdateVideoItemSortTitle(item);
1034         break;
1035
1036       case CONTEXT_BUTTON_MARK_WATCHED:
1037         result = MarkWatched(item, true);
1038         break;
1039
1040       case CONTEXT_BUTTON_MARK_UNWATCHED:
1041         result = MarkWatched(item, false);
1042         break;
1043
1044       case CONTEXT_BUTTON_LINK_MOVIE:
1045         result = LinkMovieToTvShow(item, false, database);
1046         break;
1047
1048       case CONTEXT_BUTTON_UNLINK_MOVIE:
1049         result = LinkMovieToTvShow(item, true, database);
1050         break;
1051
1052       case CONTEXT_BUTTON_SET_MOVIESET:
1053       {
1054         CFileItemPtr selectedSet;
1055         if (GetSetForMovie(item.get(), selectedSet))
1056           result = SetMovieSet(item.get(), selectedSet.get());
1057         break;
1058       }
1059
1060       case CONTEXT_BUTTON_UNLINK_BOOKMARK:
1061         database.DeleteBookMarkForEpisode(*item->GetVideoInfoTag());
1062         result = true;
1063         break;
1064
1065       case CONTEXT_BUTTON_DELETE:
1066         result = DeleteVideoItem(item);
1067         break;
1068
1069       case CONTEXT_BUTTON_SET_MOVIESET_ART:
1070         result = ManageVideoItemArtwork(item, "set");
1071         break;
1072
1073       case CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS:
1074         result = ManageMovieSets(item);
1075         break;
1076
1077       case CONTEXT_BUTTON_TAGS_ADD_ITEMS:
1078         result = AddItemsToTag(item);
1079         break;
1080
1081       case CONTEXT_BUTTON_TAGS_REMOVE_ITEMS:
1082         result = RemoveItemsFromTag(item);
1083         break;
1084
1085       default:
1086         result = false;
1087         break;
1088     }
1089   }
1090
1091   database.Close();
1092
1093   if (result)
1094     return button;
1095
1096   return -1;
1097 }
1098
1099 //Add change a title's name
1100 bool CGUIDialogVideoInfo::UpdateVideoItemTitle(const CFileItemPtr &pItem)
1101 {
1102   if (pItem == NULL || !pItem->HasVideoInfoTag())
1103     return false;
1104
1105   // dont allow update while scanning
1106   if (g_application.IsVideoScanning())
1107   {
1108     CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1109     return false;
1110   }
1111
1112   CVideoDatabase database;
1113   if (!database.Open())
1114     return false;
1115
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);
1129
1130   // get the new title
1131   if (!CGUIKeyboardFactory::ShowAndGetInput(detail.m_strTitle, g_localizeStrings.Get(16105), false))
1132     return false;
1133
1134   database.UpdateMovieTitle(iDbId, detail.m_strTitle, iType);
1135   return true;
1136 }
1137
1138 bool CGUIDialogVideoInfo::MarkWatched(const CFileItemPtr &item, bool bMark)
1139 {
1140   if (item == NULL || !CProfilesManager::Get().GetCurrentProfile().canWriteDatabases())
1141     return false;
1142
1143   // dont allow update while scanning
1144   if (g_application.IsVideoScanning())
1145   {
1146     CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1147     return false;
1148   }
1149
1150   CVideoDatabase database;
1151   if (!database.Open())
1152     return false;
1153
1154   CFileItemList items;
1155   if (item->m_bIsFolder)
1156   {
1157     CStdString strPath = item->GetPath();
1158     CDirectory::GetDirectory(strPath, items);
1159   }
1160   else
1161     items.Add(item);
1162
1163   for (int i = 0; i < items.Size(); ++i)
1164   {
1165     CFileItemPtr pTmpItem = items[i];
1166     if (pTmpItem->m_bIsFolder)
1167     {
1168       MarkWatched(pTmpItem, bMark);
1169       continue;
1170     }
1171
1172     if (pTmpItem->HasVideoInfoTag() &&
1173        ((bMark && pTmpItem->GetVideoInfoTag()->m_playCount) ||
1174         (!bMark && !pTmpItem->GetVideoInfoTag()->m_playCount)))
1175       continue;
1176
1177 #ifdef HAS_UPNP
1178     if (!URIUtils::IsUPnP(item->GetPath()) || !UPNP::CUPnP::MarkWatched(*pTmpItem, bMark))
1179 #endif
1180     {
1181       // Clear resume bookmark
1182       if (bMark)
1183         database.ClearBookMarksOfFile(pTmpItem->GetPath(), CBookmark::RESUME);
1184
1185       database.SetPlayCount(*pTmpItem, bMark ? 1 : 0);
1186     }
1187   }
1188
1189   database.Close();
1190
1191   return true;
1192 }
1193
1194 bool CGUIDialogVideoInfo::CanDeleteVideoItem(const CFileItemPtr &item)
1195 {
1196   if (item == NULL || !item->HasVideoInfoTag())
1197     return false;
1198
1199   CQueryParams params;
1200   CVideoDatabaseDirectory::GetQueryParams(item->GetPath(), params);
1201
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()));
1207 }
1208
1209 bool CGUIDialogVideoInfo::DeleteVideoItemFromDatabase(const CFileItemPtr &item, bool unavailable /* = false */)
1210 {
1211   if (item == NULL || !item->HasVideoInfoTag() ||
1212       !CanDeleteVideoItem(item))
1213     return false;
1214
1215   // dont allow update while scanning
1216   if (g_application.IsVideoScanning())
1217   {
1218     CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1219     return false;
1220   }
1221
1222   CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
1223   if (pDialog == NULL)
1224     return false;
1225   
1226   int heading = -1;
1227   VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)item->GetVideoContentType();
1228   switch (type)
1229   {
1230     case VIDEODB_CONTENT_MOVIES:
1231       heading = 432;
1232       break;
1233     case VIDEODB_CONTENT_EPISODES:
1234       heading = 20362;
1235       break;
1236     case VIDEODB_CONTENT_TVSHOWS:
1237       heading = 20363;
1238       break;
1239     case VIDEODB_CONTENT_MUSICVIDEOS:
1240       heading = 20392;
1241       break;
1242
1243     default:
1244       return false;
1245   }
1246
1247   pDialog->SetHeading(heading);
1248
1249   if (unavailable)
1250   {
1251     pDialog->SetLine(0, g_localizeStrings.Get(662));
1252     pDialog->SetLine(1, g_localizeStrings.Get(663));
1253   }
1254   else
1255   {
1256     pDialog->SetLine(0, StringUtils::Format(g_localizeStrings.Get(433), item->GetLabel().c_str()));
1257     pDialog->SetLine(1, "");
1258   }
1259   pDialog->SetLine(2, "");
1260   pDialog->DoModal();
1261
1262   if (!pDialog->IsConfirmed())
1263     return false;
1264
1265   CStdString path;
1266   CVideoDatabase database;
1267   database.Open();
1268
1269   database.GetFilePathById(item->GetVideoInfoTag()->m_iDbId, path, type);
1270   if (path.empty())
1271     return false;
1272
1273   switch (type)
1274   {
1275     case VIDEODB_CONTENT_MOVIES:
1276       database.DeleteMovie(path);
1277       break;
1278     case VIDEODB_CONTENT_EPISODES:
1279       database.DeleteEpisode(path, item->GetVideoInfoTag()->m_iDbId);
1280       break;
1281     case VIDEODB_CONTENT_TVSHOWS:
1282       database.DeleteTvShow(path);
1283       break;
1284     case VIDEODB_CONTENT_MUSICVIDEOS:
1285       database.DeleteMusicVideo(path);
1286       break;
1287   
1288     default:
1289       return false;
1290   }
1291
1292   if (type == VIDEODB_CONTENT_TVSHOWS)
1293     database.SetPathHash(path,"");
1294   else
1295     database.SetPathHash(URIUtils::GetDirectory(path), "");
1296
1297   return true;
1298 }
1299
1300 bool CGUIDialogVideoInfo::DeleteVideoItem(const CFileItemPtr &item, bool unavailable /* = false */)
1301 {
1302   if (item == NULL)
1303     return false;
1304
1305   // delete the video item from the database
1306   if (!DeleteVideoItemFromDatabase(item, unavailable))
1307     return false;
1308
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"))
1314   {
1315     CStdString strDeletePath = item->GetVideoInfoTag()->GetPath();
1316
1317     if (URIUtils::GetFileName(strDeletePath).Equals("VIDEO_TS.IFO"))
1318     {
1319       strDeletePath = URIUtils::GetDirectory(strDeletePath);
1320       if (StringUtils::EndsWithNoCase(strDeletePath, "video_ts/"))
1321       {
1322         URIUtils::RemoveSlashAtEnd(strDeletePath);
1323         strDeletePath = URIUtils::GetDirectory(strDeletePath);
1324       }
1325     }
1326     if (URIUtils::HasSlashAtEnd(strDeletePath))
1327       item->m_bIsFolder = true;
1328
1329     // check if the file/directory can be deleted
1330     if (CUtil::SupportsWriteFileOperations(strDeletePath))
1331     {
1332       item->SetPath(strDeletePath);
1333
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);
1338     }
1339   }
1340
1341   CUtil::DeleteVideoDatabaseDirectoryCache();
1342
1343   return true;
1344 }
1345
1346 bool CGUIDialogVideoInfo::ManageMovieSets(const CFileItemPtr &item)
1347 {
1348   if (item == NULL)
1349     return false;
1350
1351   CFileItemList originalItems;
1352   CFileItemList selectedItems;
1353
1354   if (!GetMoviesForSet(item.get(), originalItems, selectedItems) ||
1355       selectedItems.Size() == 0) // need at least one item selected
1356     return false;
1357
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);
1362   
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)
1368   {
1369     if (SetMovieSet(it->get(), item.get()))
1370       refreshNeeded = true;
1371   }
1372
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)
1379   {
1380     if (SetMovieSet(it->get(), clearItem.get()))
1381       refreshNeeded = true;
1382   }
1383
1384   return refreshNeeded;
1385 }
1386
1387 bool CGUIDialogVideoInfo::GetMoviesForSet(const CFileItem *setItem, CFileItemList &originalMovies, CFileItemList &selectedMovies)
1388 {
1389   if (setItem == NULL || !setItem->HasVideoInfoTag())
1390     return false;
1391
1392   CVideoDatabase videodb;
1393   if (!videodb.Open())
1394     return false;
1395
1396   CStdString strHeading = StringUtils::Format(g_localizeStrings.Get(20457));
1397   CStdString baseDir = StringUtils::Format("videodb://movies/sets/%d", setItem->GetVideoInfoTag()->m_iDbId);
1398
1399   if (!CDirectory::GetDirectory(baseDir, originalMovies) || originalMovies.Size() <= 0) // keep a copy of the original members of the set
1400     return false;
1401
1402   CFileItemList listItems;
1403   if (!videodb.GetSortedVideos(MediaTypeMovie, "videodb://movies", SortDescription(), listItems) || listItems.Size() <= 0)
1404     return false;
1405
1406   CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1407   if (dialog == NULL)
1408     return false;
1409
1410   listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1411
1412   dialog->Reset();
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++)
1418   {
1419     for (int listIndex = 0; listIndex < listItems.Size(); listIndex++)
1420     {
1421       if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == originalMovies[i]->GetVideoInfoTag()->m_iDbId)
1422       {
1423         selectedIndices.push_back(listIndex);
1424         break;
1425       }
1426     }
1427   }
1428   dialog->SetSelected(selectedIndices);
1429   dialog->EnableButton(true, 186);
1430   dialog->DoModal();
1431
1432   if (dialog->IsConfirmed())
1433   {
1434     selectedMovies.Copy(dialog->GetSelectedItems());
1435     return (selectedMovies.Size() > 0);
1436   }
1437   else
1438     return false;
1439 }
1440
1441 bool CGUIDialogVideoInfo::GetSetForMovie(const CFileItem *movieItem, CFileItemPtr &selectedSet)
1442 {
1443   if (movieItem == NULL || !movieItem->HasVideoInfoTag())
1444     return false;
1445
1446   CVideoDatabase videodb;
1447   if (!videodb.Open())
1448     return false;
1449
1450   CFileItemList listItems;
1451   CStdString baseDir = "videodb://movies/sets/";
1452   if (!CDirectory::GetDirectory(baseDir, listItems) || listItems.Size() <= 0)
1453     return false;
1454   listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1455
1456   int currentSetId = 0;
1457   CStdString currentSetLabel;
1458
1459   if (movieItem->GetVideoInfoTag()->m_iSetId > currentSetId)
1460   {
1461     currentSetId = movieItem->GetVideoInfoTag()->m_iSetId;
1462     currentSetLabel = videodb.GetSetById(currentSetId);
1463   }
1464
1465   if (currentSetId > 0)
1466   {
1467     // add clear item
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);
1477   }
1478
1479   CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1480   if (dialog == NULL)
1481     return false;
1482
1483   CStdString strHeading = StringUtils::Format(g_localizeStrings.Get(20466));
1484   dialog->Reset();
1485   dialog->SetHeading(strHeading);
1486   dialog->SetItems(&listItems);
1487   if (currentSetId >= 0)
1488   {
1489     for (int listIndex = 0; listIndex < listItems.Size(); listIndex++) 
1490     {
1491       if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == currentSetId)
1492       {
1493         dialog->SetSelected(listIndex);
1494         break;
1495       }
1496     }
1497   }
1498   dialog->EnableButton(true, 20468); // new set via button
1499   dialog->DoModal();
1500
1501   if (dialog->IsButtonPressed())
1502   { // creating new set
1503     CStdString newSetTitle;
1504     if (!CGUIKeyboardFactory::ShowAndGetInput(newSetTitle, g_localizeStrings.Get(20468), false))
1505       return false;
1506     int idSet = videodb.AddSet(newSetTitle);
1507     map<string, string> movieArt, setArt;
1508     if (!videodb.GetArtForItem(idSet, "set", setArt))
1509     {
1510       videodb.GetArtForItem(movieItem->GetVideoInfoTag()->m_iDbId, "movie", movieArt);
1511       videodb.SetArtForItem(idSet, "set", movieArt);
1512     }
1513     CFileItemPtr newSet(new CFileItem(newSetTitle));
1514     newSet->GetVideoInfoTag()->m_iDbId = idSet;
1515     selectedSet = newSet;
1516     return true;
1517   }
1518   else if (dialog->IsConfirmed())
1519   {
1520     selectedSet = dialog->GetSelectedItem();
1521     return (selectedSet != NULL);
1522   }
1523   else
1524     return false;
1525 }
1526
1527 bool CGUIDialogVideoInfo::SetMovieSet(const CFileItem *movieItem, const CFileItem *selectedSet)
1528 {
1529   if (movieItem == NULL || !movieItem->HasVideoInfoTag() ||
1530       selectedSet == NULL || !selectedSet->HasVideoInfoTag())
1531     return false;
1532
1533   CVideoDatabase videodb;
1534   if (!videodb.Open())
1535     return false;
1536
1537   videodb.SetMovieSet(movieItem->GetVideoInfoTag()->m_iDbId, selectedSet->GetVideoInfoTag()->m_iDbId);
1538   return true;
1539 }
1540
1541 bool CGUIDialogVideoInfo::GetItemsForTag(const CStdString &strHeading, const std::string &type, CFileItemList &items, int idTag /* = -1 */, bool showAll /* = true */)
1542 {
1543   CVideoDatabase videodb;
1544   if (!videodb.Open())
1545     return false;
1546
1547   MediaType mediaType = MediaTypeNone;
1548   std::string baseDir = "videodb://";
1549   std::string idColumn;
1550   if (type.compare("movie") == 0)
1551   {
1552     mediaType = MediaTypeMovie;
1553     baseDir += "movies";
1554     idColumn = "idMovie";
1555   }
1556   else if (type.compare("tvshow") == 0)
1557   {
1558     mediaType = MediaTypeTvShow;
1559     baseDir += "tvshows";
1560     idColumn = "idShow";
1561   }
1562   else if (type.compare("musicvideo") == 0)
1563   {
1564     mediaType = MediaTypeMusicVideo;
1565     baseDir += "musicvideos";
1566     idColumn = "idMVideo";
1567   }
1568
1569   baseDir += "/titles/";
1570   CVideoDbUrl videoUrl;
1571   if (!videoUrl.FromString(baseDir))
1572     return false;
1573
1574   CVideoDatabase::Filter filter;
1575   if (idTag > 0)
1576   {
1577     if (!showAll)
1578       videoUrl.AddOption("tagid", idTag);
1579     else
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());
1581   }
1582
1583   CFileItemList listItems;
1584   if (!videodb.GetSortedVideos(mediaType, videoUrl.ToString(), SortDescription(), listItems, filter) || listItems.Size() <= 0)
1585     return false;
1586
1587   CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1588   if (dialog == NULL)
1589     return false;
1590
1591   listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1592
1593   dialog->Reset();
1594   dialog->SetMultiSelection(true);
1595   dialog->SetHeading(strHeading);
1596   dialog->SetItems(&listItems);
1597   dialog->EnableButton(true, 186);
1598   dialog->DoModal();
1599
1600   items.Copy(dialog->GetSelectedItems());
1601   return items.Size() > 0;
1602 }
1603
1604 bool CGUIDialogVideoInfo::AddItemsToTag(const CFileItemPtr &tagItem)
1605 {
1606   if (tagItem == NULL || !tagItem->HasVideoInfoTag())
1607     return false;
1608
1609   CVideoDbUrl videoUrl;
1610   if (!videoUrl.FromString(tagItem->GetPath()))
1611     return false;
1612
1613   CVideoDatabase videodb;
1614   if (!videodb.Open())
1615     return true;
1616
1617   std::string mediaType = videoUrl.GetItemType();
1618   mediaType = mediaType.substr(0, mediaType.length() - 1);
1619
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))
1624     return true;
1625
1626   for (int index = 0; index < items.Size(); index++)
1627   {
1628     if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1629       continue;
1630
1631     videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, tagItem->GetVideoInfoTag()->m_iDbId, mediaType);
1632   }
1633
1634   return true;
1635 }
1636
1637 bool CGUIDialogVideoInfo::RemoveItemsFromTag(const CFileItemPtr &tagItem)
1638 {
1639   if (tagItem == NULL || !tagItem->HasVideoInfoTag())
1640     return false;
1641
1642   CVideoDbUrl videoUrl;
1643   if (!videoUrl.FromString(tagItem->GetPath()))
1644     return false;
1645
1646   CVideoDatabase videodb;
1647   if (!videodb.Open())
1648     return true;
1649
1650   std::string mediaType = videoUrl.GetItemType();
1651   mediaType = mediaType.substr(0, mediaType.length() - 1);
1652
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))
1657     return true;
1658
1659   for (int index = 0; index < items.Size(); index++)
1660   {
1661     if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1662       continue;
1663
1664     videodb.RemoveTagFromItem(items[index]->GetVideoInfoTag()->m_iDbId, tagItem->GetVideoInfoTag()->m_iDbId, mediaType);
1665   }
1666
1667   return true;
1668 }
1669
1670 bool CGUIDialogVideoInfo::ManageVideoItemArtwork(const CFileItemPtr &item, const std::string &type)
1671 {
1672   if (item == NULL || !item->HasVideoInfoTag() || type.empty())
1673     return false;
1674
1675   CVideoDatabase videodb;
1676   if (!videodb.Open())
1677     return true;
1678
1679   // Grab the thumbnails from the web
1680   CFileItemList items;
1681   CFileItemPtr noneitem(new CFileItem("thumb://None", false));
1682   std::string currentThumb;
1683   int idArtist = -1;
1684   CStdString artistPath;
1685   string artType = "thumb";
1686   if (type == "artist")
1687   {
1688     CMusicDatabase musicdb;
1689     if (musicdb.Open())
1690     {
1691       idArtist = musicdb.GetArtistByName(item->GetLabel());
1692       if (idArtist >= 0 && musicdb.GetArtistPath(idArtist, artistPath))
1693       {
1694         currentThumb = musicdb.GetArtForItem(idArtist, "artist", "thumb");
1695         if (currentThumb.empty())
1696           currentThumb = videodb.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType);
1697       }
1698     }
1699   }
1700   else if (type == "actor")
1701     currentThumb = videodb.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType);
1702   else
1703   { // SEASON, SET
1704     map<string, string> currentArt;
1705     artType = ChooseArtType(*item, currentArt);
1706     if (artType.empty())
1707       return false;
1708
1709     if (artType == "fanart")
1710       return OnGetFanart(item);
1711
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"];
1716   }
1717
1718   if (!currentThumb.empty())
1719   {
1720     CFileItemPtr item(new CFileItem("thumb://Current", false));
1721     item->SetArt("thumb", currentThumb);
1722     item->SetLabel(g_localizeStrings.Get(13512));
1723     items.Add(item);
1724   }
1725   noneitem->SetIconImage("DefaultFolder.png");
1726   noneitem->SetLabel(g_localizeStrings.Get(13515));
1727
1728   bool local = false;
1729   vector<CStdString> thumbs;
1730   if (type != "artist")
1731   {
1732     CVideoInfoTag tag;
1733     if (type == "season")
1734       videodb.GetTvShowInfo("", tag, item->GetVideoInfoTag()->m_iIdShow);
1735     else
1736       tag = *item->GetVideoInfoTag();
1737
1738     tag.m_strPictureURL.GetThumbURLs(thumbs, artType, type == "season" ? item->GetVideoInfoTag()->m_iSeason : -1);
1739
1740     for (size_t i = 0; i < thumbs.size(); i++)
1741     {
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));
1746       items.Add(item);
1747
1748       // TODO: Do we need to clear the cached image?
1749       //    CTextureCache::Get().ClearCachedImage(thumbs[i]);
1750     }
1751
1752     if (type == "actor")
1753     {
1754       CStdString picturePath;
1755       CStdString strThumb = URIUtils::AddFileToFolder(picturePath, "folder.jpg");
1756       if (XFILE::CFile::Exists(strThumb))
1757       {
1758         CFileItemPtr pItem(new CFileItem(strThumb,false));
1759         pItem->SetLabel(g_localizeStrings.Get(13514));
1760         pItem->SetArt("thumb", strThumb);
1761         items.Add(pItem);
1762         local = true;
1763       }
1764       else
1765         noneitem->SetIconImage("DefaultActor.png");
1766     }
1767
1768     if (type == "set")
1769       noneitem->SetIconImage("DefaultVideo.png");
1770   }
1771   else
1772   {
1773     CStdString strThumb = URIUtils::AddFileToFolder(artistPath, "folder.jpg");
1774     if (XFILE::CFile::Exists(strThumb))
1775     {
1776       CFileItemPtr pItem(new CFileItem(strThumb, false));
1777       pItem->SetLabel(g_localizeStrings.Get(13514));
1778       pItem->SetArt("thumb", strThumb);
1779       items.Add(pItem);
1780       local = true;
1781     }
1782     else
1783       noneitem->SetIconImage("DefaultArtist.png");
1784   }
1785
1786   if (!local)
1787     items.Add(noneitem);
1788   
1789   CStdString result;
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
1795
1796   if (result == "thumb://Current")
1797     result = currentThumb;   // user chose the one they have
1798   
1799   // delete the thumbnail if that's what the user wants, else overwrite with the
1800   // new thumbnail
1801   if (result == "thumb://None")
1802     result.clear();
1803   else if (StringUtils::StartsWith(result, "thumb://Remote"))
1804   {
1805     int number = atoi(StringUtils::Mid(result, 14).c_str());
1806     result = thumbs[number];
1807   }
1808
1809   // write the selected artwork to the database
1810   if (type == "set" ||
1811       type == "actor" ||
1812       type == "season" ||
1813       (type == "artist" && idArtist < 0))
1814     videodb.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType, result);
1815   else
1816   {
1817     CMusicDatabase musicdb;
1818     if (musicdb.Open())
1819       musicdb.SetArtForItem(idArtist, "artist", artType, result);
1820   }
1821
1822   CUtil::DeleteVideoDatabaseDirectoryCache();
1823   CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
1824   g_windowManager.SendMessage(msg);
1825
1826   return true;
1827 }
1828
1829 std::string CGUIDialogVideoInfo::GetLocalizedVideoType(const std::string &strType)
1830 {
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);
1839
1840   return "";
1841 }
1842
1843 bool CGUIDialogVideoInfo::UpdateVideoItemSortTitle(const CFileItemPtr &pItem)
1844 {
1845   // dont allow update while scanning
1846   if (g_application.IsVideoScanning())
1847   {
1848     CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1849     return false;
1850   }
1851
1852   CVideoDatabase database;
1853   if (!database.Open())
1854     return false;
1855
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);
1863
1864   CStdString currentTitle;
1865   if (detail.m_strSortTitle.empty())
1866     currentTitle = detail.m_strTitle;
1867   else
1868     currentTitle = detail.m_strSortTitle;
1869   
1870   // get the new sort title
1871   if (!CGUIKeyboardFactory::ShowAndGetInput(currentTitle, g_localizeStrings.Get(16107), false))
1872     return false;
1873
1874   return database.UpdateVideoSortTitle(iDbId, currentTitle, iType);
1875 }
1876
1877 bool CGUIDialogVideoInfo::LinkMovieToTvShow(const CFileItemPtr &item, bool bRemove, CVideoDatabase &database)
1878 {
1879   int dbId = item->GetVideoInfoTag()->m_iDbId;
1880
1881   CFileItemList list;
1882   if (bRemove)
1883   {
1884     vector<int> ids;
1885     if (!database.GetLinksToTvShow(dbId, ids))
1886       return false;
1887
1888     for (unsigned int i = 0; i < ids.size(); ++i)
1889     {
1890       CVideoInfoTag tag;
1891       database.GetTvShowInfo("", tag, ids[i]);
1892       CFileItemPtr show(new CFileItem(tag));
1893       list.Add(show);
1894     }
1895   }
1896   else
1897   {
1898     database.GetTvShowsNav("videodb://tvshows/titles", list);
1899
1900     // remove already linked shows
1901     vector<int> ids;
1902     if (!database.GetLinksToTvShow(dbId, ids))
1903       return false;
1904
1905     for (int i = 0; i < list.Size(); )
1906     {
1907       size_t j;
1908       for (j = 0; j < ids.size(); ++j)
1909       {
1910         if (list[i]->GetVideoInfoTag()->m_iDbId == ids[j])
1911           break;
1912       }
1913       if (j == ids.size())
1914         i++;
1915       else
1916         list.Remove(i);
1917     }
1918   }
1919
1920   int iSelectedLabel = 0;
1921   if (list.Size() > 1)
1922   {
1923     list.Sort(SortByLabel, SortOrderAscending, CSettings::Get().GetBool("filelists.ignorethewhensorting") ? SortAttributeIgnoreArticle : SortAttributeNone);
1924     CGUIDialogSelect* pDialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1925     pDialog->Reset();
1926     pDialog->SetItems(&list);
1927     pDialog->SetHeading(20356);
1928     pDialog->DoModal();
1929     iSelectedLabel = pDialog->GetSelectedLabel();
1930   }
1931
1932   if (iSelectedLabel > -1)
1933     return database.LinkMovieToTvshow(dbId, list[iSelectedLabel]->GetVideoInfoTag()->m_iDbId, bRemove);
1934
1935   return false;
1936 }
1937
1938 bool CGUIDialogVideoInfo::OnGetFanart(const CFileItemPtr &videoItem)
1939 {
1940   if (videoItem == NULL || !videoItem->HasVideoInfoTag())
1941     return false;
1942
1943   // update the db
1944   CVideoDatabase videodb;
1945   if (!videodb.Open())
1946     return false;
1947
1948   CVideoThumbLoader loader;
1949   CFileItem item(*videoItem);
1950   loader.LoadItem(&item);
1951   
1952   CFileItemList items;
1953   if (item.HasArt("fanart"))
1954   {
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);
1959   }
1960
1961   // add the none option
1962   {
1963     CFileItemPtr itemNone(new CFileItem("fanart://None", false));
1964     itemNone->SetIconImage("DefaultVideo.png");
1965     itemNone->SetLabel(g_localizeStrings.Get(20439));
1966     items.Add(itemNone);
1967   }
1968
1969   CStdString result;
1970   VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
1971   g_mediaManager.GetLocalDrives(sources);
1972   AddItemPathToFileBrowserSources(sources, item);
1973   bool flip = false;
1974   if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) ||
1975       StringUtils::EqualsNoCase(result, "fanart://Current"))
1976     return false;
1977
1978   if (StringUtils::EqualsNoCase(result, "fanart://None") || !CFile::Exists(result))
1979     result.clear();
1980   if (!result.empty() && flip)
1981     result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
1982
1983   videodb.SetArtForItem(item.GetVideoInfoTag()->m_iDbId, item.GetVideoInfoTag()->m_type, "fanart", result);
1984
1985   // clear view cache and reload images
1986   CUtil::DeleteVideoDatabaseDirectoryCache();
1987
1988   return true;
1989 }