Merge pull request #3746 from amet/setDefaultSubServices
[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       m_movieItem->SetProperty("totalepisodes", m_movieItem->GetVideoInfoTag()->m_iEpisode);
309       m_movieItem->SetProperty("numepisodes", m_movieItem->GetVideoInfoTag()->m_iEpisode); // info view has no concept of current watched/unwatched filter as we could come here from files view, but set for consistency
310       m_movieItem->SetProperty("watchedepisodes", m_movieItem->GetVideoInfoTag()->m_playCount);
311       m_movieItem->SetProperty("unwatchedepisodes", m_movieItem->GetVideoInfoTag()->m_iEpisode - m_movieItem->GetVideoInfoTag()->m_playCount);
312       m_movieItem->GetVideoInfoTag()->m_playCount = (m_movieItem->GetVideoInfoTag()->m_iEpisode == m_movieItem->GetVideoInfoTag()->m_playCount) ? 1 : 0;
313     }
314     else if (type == VIDEODB_CONTENT_EPISODES)
315     {
316       m_castList->SetContent("episodes");
317       // special case stuff for episodes (not currently retrieved from the library in filemode (ref: GetEpisodeInfo vs GetEpisodesByWhere)
318       m_movieItem->m_dateTime = m_movieItem->GetVideoInfoTag()->m_firstAired;
319       if(m_movieItem->GetVideoInfoTag()->m_iYear == 0 && m_movieItem->m_dateTime.IsValid())
320         m_movieItem->GetVideoInfoTag()->m_iYear = m_movieItem->m_dateTime.GetYear();
321       // retrieve the season thumb.
322       // TODO: should we use the thumbloader for this?
323       CVideoDatabase db;
324       if (db.Open())
325       {
326         if (m_movieItem->GetVideoInfoTag()->m_iSeason > -1)
327         {
328           int seasonID = m_movieItem->GetVideoInfoTag()->m_iIdSeason;
329           if (seasonID < 0)
330             seasonID = db.GetSeasonId(m_movieItem->GetVideoInfoTag()->m_iIdShow,
331                                       m_movieItem->GetVideoInfoTag()->m_iSeason);
332           CGUIListItem::ArtMap thumbs;
333           if (db.GetArtForItem(seasonID, "season", thumbs))
334           {
335             for (CGUIListItem::ArtMap::iterator i = thumbs.begin(); i != thumbs.end(); i++)
336               m_movieItem->SetArt("season." + i->first, i->second);
337           }
338         }
339         db.Close();
340       }
341     }
342     else if (type == VIDEODB_CONTENT_MOVIES)
343     {
344       m_castList->SetContent("movies");
345
346       // local trailers should always override non-local, so check 
347       // for a local one if the registered trailer is online
348       if (m_movieItem->GetVideoInfoTag()->m_strTrailer.empty() ||
349           URIUtils::IsInternetStream(m_movieItem->GetVideoInfoTag()->m_strTrailer))
350       {
351         CStdString localTrailer = m_movieItem->FindTrailer();
352         if (!localTrailer.empty())
353         {
354           m_movieItem->GetVideoInfoTag()->m_strTrailer = localTrailer;
355           CVideoDatabase database;
356           if(database.Open())
357           {
358             database.SetSingleValue(VIDEODB_CONTENT_MOVIES, VIDEODB_ID_TRAILER,
359                                     m_movieItem->GetVideoInfoTag()->m_iDbId,
360                                     m_movieItem->GetVideoInfoTag()->m_strTrailer);
361             database.Close();
362             CUtil::DeleteVideoDatabaseDirectoryCache();
363           }
364         }
365       }
366     }
367   }
368   CVideoThumbLoader loader;
369   loader.LoadItem(m_movieItem.get());
370 }
371
372 void CGUIDialogVideoInfo::Update()
373 {
374   // setup plot text area
375   CStdString strTmp = m_movieItem->GetVideoInfoTag()->m_strPlot;
376   if (!(!m_movieItem->GetVideoInfoTag()->m_strShowTitle.empty() && m_movieItem->GetVideoInfoTag()->m_iSeason == 0)) // dont apply to tvshows
377     if (m_movieItem->GetVideoInfoTag()->m_playCount == 0 && !CSettings::Get().GetBool("videolibrary.showunwatchedplots"))
378       strTmp = g_localizeStrings.Get(20370);
379
380   StringUtils::Trim(strTmp);
381   SetLabel(CONTROL_TEXTAREA, strTmp);
382
383   CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST, 0, 0, m_castList);
384   OnMessage(msg);
385
386   if (GetControl(CONTROL_BTN_TRACKS)) // if no CONTROL_BTN_TRACKS found - allow skinner full visibility control over CONTROL_TEXTAREA and CONTROL_LIST
387   {
388     if (m_bViewReview)
389     {
390       if (!m_movieItem->GetVideoInfoTag()->m_artist.empty())
391       {
392         SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 133);
393       }
394       else
395       {
396         SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 206);
397       }
398
399       SET_CONTROL_HIDDEN(CONTROL_LIST);
400       SET_CONTROL_VISIBLE(CONTROL_TEXTAREA);
401     }
402     else
403     {
404       SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 207);
405
406       SET_CONTROL_HIDDEN(CONTROL_TEXTAREA);
407       SET_CONTROL_VISIBLE(CONTROL_LIST);
408     }
409   }
410
411   // Check for resumability
412   if (m_movieItem->GetVideoInfoTag()->m_resumePoint.timeInSeconds > 0.0)
413     CONTROL_ENABLE(CONTROL_BTN_RESUME);
414   else
415     CONTROL_DISABLE(CONTROL_BTN_RESUME);
416
417   CONTROL_ENABLE(CONTROL_BTN_PLAY);
418
419   // update the thumbnail
420   const CGUIControl* pControl = GetControl(CONTROL_IMAGE);
421   if (pControl)
422   {
423     CGUIImage* pImageControl = (CGUIImage*)pControl;
424     pImageControl->FreeResources();
425     pImageControl->SetFileName(m_movieItem->GetArt("thumb"));
426   }
427   // tell our GUI to completely reload all controls (as some of them
428   // are likely to have had this image in use so will need refreshing)
429   if (m_hasUpdatedThumb)
430   {
431     CGUIMessage reload(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
432     g_windowManager.SendMessage(reload);
433   }
434 }
435
436 bool CGUIDialogVideoInfo::NeedRefresh() const
437 {
438   return m_bRefresh;
439 }
440
441 bool CGUIDialogVideoInfo::RefreshAll() const
442 {
443   return m_bRefreshAll;
444 }
445
446 /// \brief Search the current directory for a string got from the virtual keyboard
447 void CGUIDialogVideoInfo::OnSearch(CStdString& strSearch)
448 {
449   CGUIDialogProgress *progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
450   if (progress)
451   {
452     progress->SetHeading(194);
453     progress->SetLine(0, strSearch);
454     progress->SetLine(1, "");
455     progress->SetLine(2, "");
456     progress->StartModal();
457     progress->Progress();
458   }
459   CFileItemList items;
460   DoSearch(strSearch, items);
461
462   if (progress)
463     progress->Close();
464
465   if (items.Size())
466   {
467     CGUIDialogSelect* pDlgSelect = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
468     pDlgSelect->Reset();
469     pDlgSelect->SetHeading(283);
470
471     for (int i = 0; i < (int)items.Size(); i++)
472     {
473       CFileItemPtr pItem = items[i];
474       pDlgSelect->Add(pItem->GetLabel());
475     }
476
477     pDlgSelect->DoModal();
478
479     int iItem = pDlgSelect->GetSelectedLabel();
480     if (iItem < 0)
481       return;
482
483     CFileItem* pSelItem = new CFileItem(*items[iItem]);
484
485     OnSearchItemFound(pSelItem);
486
487     delete pSelItem;
488   }
489   else
490   {
491     CGUIDialogOK::ShowAndGetInput(194, 284, 0, 0);
492   }
493 }
494
495 /// \brief Make the actual search for the OnSearch function.
496 /// \param strSearch The search string
497 /// \param items Items Found
498 void CGUIDialogVideoInfo::DoSearch(CStdString& strSearch, CFileItemList& items)
499 {
500   CVideoDatabase db;
501   if (!db.Open())
502     return;
503
504   CFileItemList movies;
505   db.GetMoviesByActor(strSearch, movies);
506   for (int i = 0; i < movies.Size(); ++i)
507   {
508     CStdString label = movies[i]->GetVideoInfoTag()->m_strTitle;
509     if (movies[i]->GetVideoInfoTag()->m_iYear > 0)
510       label += StringUtils::Format(" (%i)", movies[i]->GetVideoInfoTag()->m_iYear);
511     movies[i]->SetLabel(label);
512   }
513   CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20338) + "] ", items);
514
515   db.GetTvShowsByActor(strSearch, movies);
516   for (int i = 0; i < movies.Size(); ++i)
517   {
518     CStdString label = movies[i]->GetVideoInfoTag()->m_strShowTitle;
519     if (movies[i]->GetVideoInfoTag()->m_iYear > 0)
520       label += StringUtils::Format(" (%i)", movies[i]->GetVideoInfoTag()->m_iYear);
521     movies[i]->SetLabel(label);
522   }
523   CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20364) + "] ", items);
524
525   db.GetEpisodesByActor(strSearch, movies);
526   for (int i = 0; i < movies.Size(); ++i)
527   {
528     CStdString label = movies[i]->GetVideoInfoTag()->m_strTitle + " (" +  movies[i]->GetVideoInfoTag()->m_strShowTitle + ")";
529     movies[i]->SetLabel(label);
530   }
531   CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20359) + "] ", items);
532
533   db.GetMusicVideosByArtist(strSearch, movies);
534   for (int i = 0; i < movies.Size(); ++i)
535   {
536     CStdString label = StringUtils::Join(movies[i]->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator) + " - " + movies[i]->GetVideoInfoTag()->m_strTitle;
537     if (movies[i]->GetVideoInfoTag()->m_iYear > 0)
538       label += StringUtils::Format(" (%i)", movies[i]->GetVideoInfoTag()->m_iYear);
539     movies[i]->SetLabel(label);
540   }
541   CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20391) + "] ", items);
542   db.Close();
543 }
544
545 /// \brief React on the selected search item
546 /// \param pItem Search result item
547 void CGUIDialogVideoInfo::OnSearchItemFound(const CFileItem* pItem)
548 {
549   VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)pItem->GetVideoContentType();
550
551   CVideoDatabase db;
552   if (!db.Open())
553     return;
554
555   CVideoInfoTag movieDetails;
556   if (type == VIDEODB_CONTENT_MOVIES)
557     db.GetMovieInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
558   if (type == VIDEODB_CONTENT_EPISODES)
559     db.GetEpisodeInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
560   if (type == VIDEODB_CONTENT_TVSHOWS)
561     db.GetTvShowInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
562   if (type == VIDEODB_CONTENT_MUSICVIDEOS)
563     db.GetMusicVideoInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
564   db.Close();
565
566   CFileItem item(*pItem);
567   *item.GetVideoInfoTag() = movieDetails;
568   SetMovie(&item);
569   // refresh our window entirely
570   Close();
571   DoModal();
572 }
573
574 void CGUIDialogVideoInfo::ClearCastList()
575 {
576   CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_LIST);
577   OnMessage(msg);
578   m_castList->Clear();
579 }
580
581 void CGUIDialogVideoInfo::Play(bool resume)
582 {
583   if (!m_movieItem->GetVideoInfoTag()->m_strEpisodeGuide.empty())
584   {
585     CStdString strPath = StringUtils::Format("videodb://tvshows/titles/%i/",m_movieItem->GetVideoInfoTag()->m_iDbId);
586     Close();
587     g_windowManager.ActivateWindow(WINDOW_VIDEO_NAV,strPath);
588     return;
589   }
590
591   CFileItem movie(*m_movieItem->GetVideoInfoTag());
592   if (m_movieItem->GetVideoInfoTag()->m_strFileNameAndPath.empty())
593     movie.SetPath(m_movieItem->GetPath());
594   CGUIWindowVideoNav* pWindow = (CGUIWindowVideoNav*)g_windowManager.GetWindow(WINDOW_VIDEO_NAV);
595   if (pWindow)
596   {
597     // close our dialog
598     Close(true);
599     if (resume)
600       movie.m_lStartOffset = STARTOFFSET_RESUME;
601     else if (!CGUIWindowVideoBase::ShowResumeMenu(movie)) 
602     {
603       // The Resume dialog was closed without any choice
604       DoModal();
605       return;
606     }
607     pWindow->PlayMovie(&movie);
608   }
609 }
610
611 string CGUIDialogVideoInfo::ChooseArtType(const CFileItem &videoItem, map<string, string> &currentArt)
612 {
613   // prompt for choice
614   CGUIDialogSelect *dialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
615   if (!dialog || !videoItem.HasVideoInfoTag())
616     return "";
617
618   CFileItemList items;
619   dialog->SetHeading(13511);
620   dialog->Reset();
621   dialog->SetUseDetails(true);
622   dialog->EnableButton(true, 13516);
623
624   CVideoDatabase db;
625   db.Open();
626
627   vector<string> artTypes = CVideoThumbLoader::GetArtTypes(videoItem.GetVideoInfoTag()->m_type);
628
629   // add in any stored art for this item that is non-empty.
630   db.GetArtForItem(videoItem.GetVideoInfoTag()->m_iDbId, videoItem.GetVideoInfoTag()->m_type, currentArt);
631   for (CGUIListItem::ArtMap::iterator i = currentArt.begin(); i != currentArt.end(); ++i)
632   {
633     if (!i->second.empty() && find(artTypes.begin(), artTypes.end(), i->first) == artTypes.end())
634       artTypes.push_back(i->first);
635   }
636
637   // add any art types that exist for other media items of the same type
638   vector<string> dbArtTypes;
639   db.GetArtTypes(videoItem.GetVideoInfoTag()->m_type, dbArtTypes);
640   for (vector<string>::const_iterator it = dbArtTypes.begin(); it != dbArtTypes.end(); it++)
641   {
642     if (find(artTypes.begin(), artTypes.end(), *it) == artTypes.end())
643       artTypes.push_back(*it);
644   }
645
646   for (vector<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
647   {
648     string type = *i;
649     CFileItemPtr item(new CFileItem(type, "false"));
650     item->SetLabel(type);
651     if (videoItem.HasArt(type))
652       item->SetArt("thumb", videoItem.GetArt(type));
653     items.Add(item);
654   }
655
656   dialog->SetItems(&items);
657   dialog->DoModal();
658
659   if (dialog->IsButtonPressed())
660   {
661     // Get the new artwork name
662     CStdString strArtworkName;
663     if (!CGUIKeyboardFactory::ShowAndGetInput(strArtworkName, g_localizeStrings.Get(13516), false))
664       return "";
665
666     return strArtworkName;
667   }
668
669   return dialog->GetSelectedItem()->GetLabel();
670 }
671
672 void CGUIDialogVideoInfo::OnGetArt()
673 {
674   map<string, string> currentArt;
675   string type = ChooseArtType(*m_movieItem, currentArt);
676   if (type.empty())
677     return; // cancelled
678
679   // TODO: this can be removed once these are unified.
680   if (type == "fanart")
681     OnGetFanart();
682   else
683   {
684     CFileItemList items;
685
686     // Current thumb
687     if (m_movieItem->HasArt(type))
688     {
689       CFileItemPtr item(new CFileItem("thumb://Current", false));
690       item->SetArt("thumb", m_movieItem->GetArt(type));
691       item->SetLabel(g_localizeStrings.Get(13512));
692       items.Add(item);
693     }
694     else if ((type == "poster" || type == "banner") && currentArt.find("thumb") != currentArt.end())
695     { // add the 'thumb' type in
696       CFileItemPtr item(new CFileItem("thumb://Thumb", false));
697       item->SetArt("thumb", currentArt["thumb"]);
698       item->SetLabel(g_localizeStrings.Get(13512));
699       items.Add(item);
700     }
701
702     // Grab the thumbnails from the web
703     vector<CStdString> thumbs;
704     int season = (m_movieItem->GetVideoInfoTag()->m_type == "season") ? m_movieItem->GetVideoInfoTag()->m_iSeason : -1;
705     m_movieItem->GetVideoInfoTag()->m_strPictureURL.GetThumbURLs(thumbs, type, season);
706
707     for (unsigned int i = 0; i < thumbs.size(); ++i)
708     {
709       CStdString strItemPath = StringUtils::Format("thumb://Remote%i", i);
710       CFileItemPtr item(new CFileItem(strItemPath, false));
711       item->SetArt("thumb", thumbs[i]);
712       item->SetIconImage("DefaultPicture.png");
713       item->SetLabel(g_localizeStrings.Get(13513));
714
715       // TODO: Do we need to clear the cached image?
716       //    CTextureCache::Get().ClearCachedImage(thumb);
717       items.Add(item);
718     }
719
720     CStdString localThumb = CVideoThumbLoader::GetLocalArt(*m_movieItem, type);
721     if (!localThumb.empty())
722     {
723       CFileItemPtr item(new CFileItem("thumb://Local", false));
724       item->SetArt("thumb", localThumb);
725       item->SetLabel(g_localizeStrings.Get(13514));
726       items.Add(item);
727     }
728     else
729     { // no local thumb exists, so we are just using the IMDb thumb or cached thumb
730       // which is probably the IMDb thumb.  These could be wrong, so allow the user
731       // to delete the incorrect thumb
732       CFileItemPtr item(new CFileItem("thumb://None", false));
733       item->SetIconImage("DefaultVideo.png");
734       item->SetLabel(g_localizeStrings.Get(13515));
735       items.Add(item);
736     }
737
738     CStdString result;
739     VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
740     AddItemPathToFileBrowserSources(sources, *m_movieItem);
741     g_mediaManager.GetLocalDrives(sources);
742     if (CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result) &&
743         result != "thumb://Current") // user didn't choose the one they have
744     {
745       CStdString newThumb;
746       if (StringUtils::StartsWith(result, "thumb://Remote"))
747       {
748         int number = atoi(result.substr(14).c_str());
749         newThumb = thumbs[number];
750       }
751       else if (result == "thumb://Thumb")
752         newThumb = currentArt["thumb"];
753       else if (result == "thumb://Local")
754         newThumb = localThumb;
755       else if (CFile::Exists(result))
756         newThumb = result;
757       else // none
758         newThumb.clear();
759
760       // update thumb in the database
761       CVideoDatabase db;
762       if (db.Open())
763       {
764         db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, type, newThumb);
765         db.Close();
766       }
767       CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
768       m_movieItem->SetArt(type, newThumb);
769       if (m_movieItem->HasProperty("set_folder_thumb"))
770       { // have a folder thumb to set as well
771         VIDEO::CVideoInfoScanner::ApplyThumbToFolder(m_movieItem->GetProperty("set_folder_thumb").asString(), newThumb);
772       }
773       m_hasUpdatedThumb = true;
774     }
775   }
776
777   // Update our screen
778   Update();
779
780   // re-open the art selection dialog as we come back from
781   // the image selection dialog
782   OnGetArt();
783 }
784
785 // Allow user to select a Fanart
786 void CGUIDialogVideoInfo::OnGetFanart()
787 {
788   CFileItemList items;
789
790   CFileItem item(*m_movieItem->GetVideoInfoTag());
791   if (item.HasArt("fanart"))
792   {
793     CFileItemPtr itemCurrent(new CFileItem("fanart://Current",false));
794     itemCurrent->SetArt("thumb", item.GetArt("fanart"));
795     itemCurrent->SetLabel(g_localizeStrings.Get(20440));
796     items.Add(itemCurrent);
797   }
798
799   // ensure the fanart is unpacked
800   m_movieItem->GetVideoInfoTag()->m_fanart.Unpack();
801
802   // Grab the thumbnails from the web
803   for (unsigned int i = 0; i < m_movieItem->GetVideoInfoTag()->m_fanart.GetNumFanarts(); i++)
804   {
805     CStdString strItemPath = StringUtils::Format("fanart://Remote%i",i);
806     CFileItemPtr item(new CFileItem(strItemPath, false));
807     CStdString thumb = m_movieItem->GetVideoInfoTag()->m_fanart.GetPreviewURL(i);
808     item->SetArt("thumb", CTextureUtils::GetWrappedThumbURL(thumb));
809     item->SetIconImage("DefaultPicture.png");
810     item->SetLabel(g_localizeStrings.Get(20441));
811
812     // TODO: Do we need to clear the cached image?
813 //    CTextureCache::Get().ClearCachedImage(thumb);
814     items.Add(item);
815   }
816
817   CStdString strLocal = item.GetLocalFanart();
818   if (!strLocal.empty())
819   {
820     CFileItemPtr itemLocal(new CFileItem("fanart://Local",false));
821     itemLocal->SetArt("thumb", strLocal);
822     itemLocal->SetLabel(g_localizeStrings.Get(20438));
823
824     // TODO: Do we need to clear the cached image?
825     CTextureCache::Get().ClearCachedImage(strLocal);
826     items.Add(itemLocal);
827   }
828   else
829   {
830     CFileItemPtr itemNone(new CFileItem("fanart://None", false));
831     itemNone->SetIconImage("DefaultVideo.png");
832     itemNone->SetLabel(g_localizeStrings.Get(20439));
833     items.Add(itemNone);
834   }
835
836   CStdString result;
837   VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
838   AddItemPathToFileBrowserSources(sources, item);
839   g_mediaManager.GetLocalDrives(sources);
840   bool flip=false;
841   if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) || result.Equals("fanart://Current"))
842     return;   // user cancelled
843
844   if (result.Equals("fanart://Local"))
845     result = strLocal;
846
847   if (StringUtils::StartsWith(result, "fanart://Remote"))
848   {
849     int iFanart = atoi(result.substr(15).c_str());
850     // set new primary fanart, and update our database accordingly
851     m_movieItem->GetVideoInfoTag()->m_fanart.SetPrimaryFanart(iFanart);
852     CVideoDatabase db;
853     if (db.Open())
854     {
855       db.UpdateFanart(*m_movieItem, (VIDEODB_CONTENT_TYPE)m_movieItem->GetVideoContentType());
856       db.Close();
857     }
858     result = m_movieItem->GetVideoInfoTag()->m_fanart.GetImageURL();
859   }
860   else if (result.Equals("fanart://None") || !CFile::Exists(result))
861     result.clear();
862
863   // set the fanart image
864   if (flip && !result.empty())
865     result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
866   CVideoDatabase db;
867   if (db.Open())
868   {
869     db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, "fanart", result);
870     db.Close();
871   }
872
873   CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
874   m_movieItem->SetArt("fanart", result);
875   m_hasUpdatedThumb = true;
876
877   // Update our screen
878   Update();
879 }
880
881 void CGUIDialogVideoInfo::PlayTrailer()
882 {
883   CFileItem item;
884   item.SetPath(m_movieItem->GetVideoInfoTag()->m_strTrailer);
885   *item.GetVideoInfoTag() = *m_movieItem->GetVideoInfoTag();
886   item.GetVideoInfoTag()->m_streamDetails.Reset();
887   item.GetVideoInfoTag()->m_strTitle = StringUtils::Format("%s (%s)",
888                                                            m_movieItem->GetVideoInfoTag()->m_strTitle.c_str(),
889                                                            g_localizeStrings.Get(20410).c_str());
890   CVideoThumbLoader::SetArt(item, m_movieItem->GetArt());
891   item.GetVideoInfoTag()->m_iDbId = -1;
892   item.GetVideoInfoTag()->m_iFileId = -1;
893
894   // Close the dialog.
895   Close(true);
896
897   if (item.IsPlayList())
898     CApplicationMessenger::Get().MediaPlay(item);
899   else
900     CApplicationMessenger::Get().PlayFile(item);
901 }
902
903 void CGUIDialogVideoInfo::SetLabel(int iControl, const CStdString &strLabel)
904 {
905   if (strLabel.empty())
906   {
907     SET_CONTROL_LABEL(iControl, 416);  // "Not available"
908   }
909   else
910   {
911     SET_CONTROL_LABEL(iControl, strLabel);
912   }
913 }
914
915 std::string CGUIDialogVideoInfo::GetThumbnail() const
916 {
917   return m_movieItem->GetArt("thumb");
918 }
919
920 void CGUIDialogVideoInfo::AddItemPathToFileBrowserSources(VECSOURCES &sources, const CFileItem &item)
921 {
922   if (!item.HasVideoInfoTag())
923     return;
924
925   CStdString itemDir = item.GetVideoInfoTag()->m_basePath;
926
927   //season
928   if (itemDir.empty())
929     itemDir = item.GetVideoInfoTag()->GetPath();
930
931   CFileItem itemTmp(itemDir, false);
932   if (itemTmp.IsVideo())
933     itemDir = URIUtils::GetParentPath(itemDir);
934
935   if (!itemDir.empty() && CDirectory::Exists(itemDir))
936   {
937     CMediaSource itemSource;
938     itemSource.strName = g_localizeStrings.Get(36041);
939     itemSource.strPath = itemDir;
940     sources.push_back(itemSource);
941   }
942 }
943
944 int CGUIDialogVideoInfo::ManageVideoItem(const CFileItemPtr &item)
945 {
946   if (item == NULL || !item->IsVideoDb() || !item->HasVideoInfoTag() || item->GetVideoInfoTag()->m_iDbId < 0)
947     return -1;
948
949   CVideoDatabase database;
950   if (!database.Open())
951     return -1;
952
953   const std::string &type = item->GetVideoInfoTag()->m_type;
954   int dbId = item->GetVideoInfoTag()->m_iDbId;
955
956   CContextButtons buttons;
957   if (type == "movie" || type == "set" ||
958       type == "tvshow" || type == "episode" ||
959       type == "musicvideo")
960     buttons.Add(CONTEXT_BUTTON_EDIT, 16105);
961
962   if (type == "movie" || type == "tvshow")
963     buttons.Add(CONTEXT_BUTTON_EDIT_SORTTITLE, 16107);
964
965   if (item->m_bIsFolder)
966   {
967     // Have both options for folders since we don't know whether all childs are watched/unwatched
968     buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
969     buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103);   //Mark as Watched
970   }
971   else
972   {
973     if (item->GetOverlayImage().Equals("OverlayWatched.png"))
974       buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
975     else
976       buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103);   //Mark as Watched
977   }
978
979   if (type == "movie")
980   {
981     // only show link/unlink if there are tvshows available
982     if (database.HasContent(VIDEODB_CONTENT_TVSHOWS))
983     {
984       buttons.Add(CONTEXT_BUTTON_LINK_MOVIE, 20384);
985       if (database.IsLinkedToTvshow(dbId))
986         buttons.Add(CONTEXT_BUTTON_UNLINK_MOVIE, 20385);
987     }
988
989     // set or change movie set the movie belongs to
990     buttons.Add(CONTEXT_BUTTON_SET_MOVIESET, 20465);
991   }
992
993   if (type == "episode" &&
994       item->GetVideoInfoTag()->m_iBookmarkId > 0)
995     buttons.Add(CONTEXT_BUTTON_UNLINK_BOOKMARK, 20405);
996
997   // movie sets
998   if (item->m_bIsFolder && type == "set")
999   {
1000     buttons.Add(CONTEXT_BUTTON_SET_MOVIESET_ART, 13511);
1001     buttons.Add(CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS, 20465);
1002   }
1003
1004   // tags
1005   if (item->m_bIsFolder && type == "tag")
1006   {
1007     CVideoDbUrl videoUrl;
1008     if (videoUrl.FromString(item->GetPath()))
1009     {
1010       const std::string &mediaType = videoUrl.GetItemType();
1011
1012       buttons.Add(CONTEXT_BUTTON_TAGS_ADD_ITEMS, StringUtils::Format(g_localizeStrings.Get(20460), GetLocalizedVideoType(mediaType).c_str()));
1013       buttons.Add(CONTEXT_BUTTON_TAGS_REMOVE_ITEMS, StringUtils::Format(g_localizeStrings.Get(20461), GetLocalizedVideoType(mediaType).c_str()));
1014     }
1015   }
1016
1017   buttons.Add(CONTEXT_BUTTON_DELETE, 646);
1018
1019   bool result = false;
1020   int button = CGUIDialogContextMenu::ShowAndGetChoice(buttons);
1021   if (button >= 0)
1022   {
1023     switch ((CONTEXT_BUTTON)button)
1024     {
1025       case CONTEXT_BUTTON_EDIT:
1026         result = UpdateVideoItemTitle(item);
1027         break;
1028
1029       case CONTEXT_BUTTON_EDIT_SORTTITLE:
1030         result = UpdateVideoItemSortTitle(item);
1031         break;
1032
1033       case CONTEXT_BUTTON_MARK_WATCHED:
1034         result = MarkWatched(item, true);
1035         break;
1036
1037       case CONTEXT_BUTTON_MARK_UNWATCHED:
1038         result = MarkWatched(item, false);
1039         break;
1040
1041       case CONTEXT_BUTTON_LINK_MOVIE:
1042         result = LinkMovieToTvShow(item, false, database);
1043         break;
1044
1045       case CONTEXT_BUTTON_UNLINK_MOVIE:
1046         result = LinkMovieToTvShow(item, true, database);
1047         break;
1048
1049       case CONTEXT_BUTTON_SET_MOVIESET:
1050       {
1051         CFileItemPtr selectedSet;
1052         if (GetSetForMovie(item.get(), selectedSet))
1053           result = SetMovieSet(item.get(), selectedSet.get());
1054         break;
1055       }
1056
1057       case CONTEXT_BUTTON_UNLINK_BOOKMARK:
1058         database.DeleteBookMarkForEpisode(*item->GetVideoInfoTag());
1059         result = true;
1060         break;
1061
1062       case CONTEXT_BUTTON_DELETE:
1063         result = DeleteVideoItem(item);
1064         break;
1065
1066       case CONTEXT_BUTTON_SET_MOVIESET_ART:
1067         result = ManageVideoItemArtwork(item, "set");
1068         break;
1069
1070       case CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS:
1071         result = ManageMovieSets(item);
1072         break;
1073
1074       case CONTEXT_BUTTON_TAGS_ADD_ITEMS:
1075         result = AddItemsToTag(item);
1076         break;
1077
1078       case CONTEXT_BUTTON_TAGS_REMOVE_ITEMS:
1079         result = RemoveItemsFromTag(item);
1080         break;
1081
1082       default:
1083         result = false;
1084         break;
1085     }
1086   }
1087
1088   database.Close();
1089
1090   if (result)
1091     return button;
1092
1093   return -1;
1094 }
1095
1096 //Add change a title's name
1097 bool CGUIDialogVideoInfo::UpdateVideoItemTitle(const CFileItemPtr &pItem)
1098 {
1099   if (pItem == NULL || !pItem->HasVideoInfoTag())
1100     return false;
1101
1102   // dont allow update while scanning
1103   if (g_application.IsVideoScanning())
1104   {
1105     CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1106     return false;
1107   }
1108
1109   CVideoDatabase database;
1110   if (!database.Open())
1111     return false;
1112
1113   int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
1114   CVideoInfoTag detail;
1115   VIDEODB_CONTENT_TYPE iType = (VIDEODB_CONTENT_TYPE)pItem->GetVideoContentType();
1116   if (iType == VIDEODB_CONTENT_MOVIES)
1117     database.GetMovieInfo("", detail, iDbId);
1118   else if (iType == VIDEODB_CONTENT_MOVIE_SETS)
1119     database.GetSetInfo(iDbId, detail);
1120   else if (iType == VIDEODB_CONTENT_EPISODES)
1121     database.GetEpisodeInfo(pItem->GetPath(), detail, iDbId);
1122   else if (iType == VIDEODB_CONTENT_TVSHOWS)
1123     database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId);
1124   else if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
1125     database.GetMusicVideoInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId);
1126
1127   // get the new title
1128   if (!CGUIKeyboardFactory::ShowAndGetInput(detail.m_strTitle, g_localizeStrings.Get(16105), false))
1129     return false;
1130
1131   database.UpdateMovieTitle(iDbId, detail.m_strTitle, iType);
1132   return true;
1133 }
1134
1135 bool CGUIDialogVideoInfo::MarkWatched(const CFileItemPtr &item, bool bMark)
1136 {
1137   if (item == NULL || !CProfilesManager::Get().GetCurrentProfile().canWriteDatabases())
1138     return false;
1139
1140   // dont allow update while scanning
1141   if (g_application.IsVideoScanning())
1142   {
1143     CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1144     return false;
1145   }
1146
1147   CVideoDatabase database;
1148   if (!database.Open())
1149     return false;
1150
1151   CFileItemList items;
1152   if (item->m_bIsFolder)
1153   {
1154     CStdString strPath = item->GetPath();
1155     CDirectory::GetDirectory(strPath, items);
1156   }
1157   else
1158     items.Add(item);
1159
1160   for (int i = 0; i < items.Size(); ++i)
1161   {
1162     CFileItemPtr pTmpItem = items[i];
1163     if (pTmpItem->m_bIsFolder)
1164     {
1165       MarkWatched(pTmpItem, bMark);
1166       continue;
1167     }
1168
1169     if (pTmpItem->HasVideoInfoTag() &&
1170        ((bMark && pTmpItem->GetVideoInfoTag()->m_playCount) ||
1171         (!bMark && !pTmpItem->GetVideoInfoTag()->m_playCount)))
1172       continue;
1173
1174 #ifdef HAS_UPNP
1175     if (!URIUtils::IsUPnP(item->GetPath()) || !UPNP::CUPnP::MarkWatched(*pTmpItem, bMark))
1176 #endif
1177     {
1178       // Clear resume bookmark
1179       if (bMark)
1180         database.ClearBookMarksOfFile(pTmpItem->GetPath(), CBookmark::RESUME);
1181
1182       database.SetPlayCount(*pTmpItem, bMark ? 1 : 0);
1183     }
1184   }
1185
1186   database.Close();
1187
1188   return true;
1189 }
1190
1191 bool CGUIDialogVideoInfo::CanDeleteVideoItem(const CFileItemPtr &item)
1192 {
1193   if (item == NULL || !item->HasVideoInfoTag())
1194     return false;
1195
1196   CQueryParams params;
1197   CVideoDatabaseDirectory::GetQueryParams(item->GetPath(), params);
1198
1199   return params.GetMovieId()   != -1 ||
1200          params.GetEpisodeId() != -1 ||
1201          params.GetMVideoId()  != -1 ||
1202          (params.GetTvShowId() != -1 && params.GetSeason() <= -1 &&
1203           !CVideoDatabaseDirectory::IsAllItem(item->GetPath()));
1204 }
1205
1206 bool CGUIDialogVideoInfo::DeleteVideoItemFromDatabase(const CFileItemPtr &item, bool unavailable /* = false */)
1207 {
1208   if (item == NULL || !item->HasVideoInfoTag() ||
1209       !CanDeleteVideoItem(item))
1210     return false;
1211
1212   // dont allow update while scanning
1213   if (g_application.IsVideoScanning())
1214   {
1215     CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1216     return false;
1217   }
1218
1219   CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
1220   if (pDialog == NULL)
1221     return false;
1222   
1223   int heading = -1;
1224   VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)item->GetVideoContentType();
1225   switch (type)
1226   {
1227     case VIDEODB_CONTENT_MOVIES:
1228       heading = 432;
1229       break;
1230     case VIDEODB_CONTENT_EPISODES:
1231       heading = 20362;
1232       break;
1233     case VIDEODB_CONTENT_TVSHOWS:
1234       heading = 20363;
1235       break;
1236     case VIDEODB_CONTENT_MUSICVIDEOS:
1237       heading = 20392;
1238       break;
1239
1240     default:
1241       return false;
1242   }
1243
1244   pDialog->SetHeading(heading);
1245
1246   if (unavailable)
1247   {
1248     pDialog->SetLine(0, g_localizeStrings.Get(662));
1249     pDialog->SetLine(1, g_localizeStrings.Get(663));
1250   }
1251   else
1252   {
1253     pDialog->SetLine(0, StringUtils::Format(g_localizeStrings.Get(433), item->GetLabel().c_str()));
1254     pDialog->SetLine(1, "");
1255   }
1256   pDialog->SetLine(2, "");
1257   pDialog->DoModal();
1258
1259   if (!pDialog->IsConfirmed())
1260     return false;
1261
1262   CStdString path;
1263   CVideoDatabase database;
1264   database.Open();
1265
1266   database.GetFilePathById(item->GetVideoInfoTag()->m_iDbId, path, type);
1267   if (path.empty())
1268     return false;
1269
1270   switch (type)
1271   {
1272     case VIDEODB_CONTENT_MOVIES:
1273       database.DeleteMovie(path);
1274       break;
1275     case VIDEODB_CONTENT_EPISODES:
1276       database.DeleteEpisode(path, item->GetVideoInfoTag()->m_iDbId);
1277       break;
1278     case VIDEODB_CONTENT_TVSHOWS:
1279       database.DeleteTvShow(path);
1280       break;
1281     case VIDEODB_CONTENT_MUSICVIDEOS:
1282       database.DeleteMusicVideo(path);
1283       break;
1284   
1285     default:
1286       return false;
1287   }
1288
1289   if (type == VIDEODB_CONTENT_TVSHOWS)
1290     database.SetPathHash(path,"");
1291   else
1292     database.SetPathHash(URIUtils::GetDirectory(path), "");
1293
1294   return true;
1295 }
1296
1297 bool CGUIDialogVideoInfo::DeleteVideoItem(const CFileItemPtr &item, bool unavailable /* = false */)
1298 {
1299   if (item == NULL)
1300     return false;
1301
1302   // delete the video item from the database
1303   if (!DeleteVideoItemFromDatabase(item, unavailable))
1304     return false;
1305
1306   bool result = true;
1307   // check if the user is allowed to delete the actual file as well
1308   if ((CProfilesManager::Get().GetCurrentProfile().getLockMode() == LOCK_MODE_EVERYONE ||
1309        !CProfilesManager::Get().GetCurrentProfile().filesLocked() ||
1310        g_passwordManager.IsMasterLockUnlocked(true)) &&
1311       CSettings::Get().GetBool("filelists.allowfiledeletion"))
1312   {
1313     CStdString strDeletePath = item->GetVideoInfoTag()->GetPath();
1314
1315     if (URIUtils::GetFileName(strDeletePath).Equals("VIDEO_TS.IFO"))
1316     {
1317       strDeletePath = URIUtils::GetDirectory(strDeletePath);
1318       if (StringUtils::EndsWithNoCase(strDeletePath, "video_ts/"))
1319       {
1320         URIUtils::RemoveSlashAtEnd(strDeletePath);
1321         strDeletePath = URIUtils::GetDirectory(strDeletePath);
1322       }
1323     }
1324     if (URIUtils::HasSlashAtEnd(strDeletePath))
1325       item->m_bIsFolder = true;
1326
1327     // check if the file/directory can be deleted
1328     if (CUtil::SupportsWriteFileOperations(strDeletePath))
1329     {
1330       item->SetPath(strDeletePath);
1331
1332       // HACK: stacked files need to be treated as folders in order to be deleted
1333       if (item->IsStack())
1334         item->m_bIsFolder = true;
1335       result = CFileUtils::DeleteItem(item);
1336     }
1337   }
1338
1339   CUtil::DeleteVideoDatabaseDirectoryCache();
1340
1341   return result;
1342 }
1343
1344 bool CGUIDialogVideoInfo::ManageMovieSets(const CFileItemPtr &item)
1345 {
1346   if (item == NULL)
1347     return false;
1348
1349   CFileItemList originalItems;
1350   CFileItemList selectedItems;
1351
1352   if (!GetMoviesForSet(item.get(), originalItems, selectedItems) ||
1353       selectedItems.Size() == 0) // need at least one item selected
1354     return false;
1355
1356   VECFILEITEMS original = originalItems.GetList();
1357   std::sort(original.begin(), original.end(), compFileItemsByDbId);
1358   VECFILEITEMS selected = selectedItems.GetList();
1359   std::sort(selected.begin(), selected.end(), compFileItemsByDbId);
1360   
1361   bool refreshNeeded = false;
1362   // update the "added" items
1363   VECFILEITEMS addedItems;
1364   set_difference(selected.begin(),selected.end(), original.begin(),original.end(), std::back_inserter(addedItems), compFileItemsByDbId);
1365   for (VECFILEITEMS::const_iterator it = addedItems.begin();  it != addedItems.end(); ++it)
1366   {
1367     if (SetMovieSet(it->get(), item.get()))
1368       refreshNeeded = true;
1369   }
1370
1371   // update the "deleted" items
1372   CFileItemPtr clearItem(new CFileItem());
1373   clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
1374   VECFILEITEMS deletedItems;
1375   set_difference(original.begin(),original.end(), selected.begin(),selected.end(), std::back_inserter(deletedItems), compFileItemsByDbId);
1376   for (VECFILEITEMS::iterator it = deletedItems.begin();  it != deletedItems.end(); ++it)
1377   {
1378     if (SetMovieSet(it->get(), clearItem.get()))
1379       refreshNeeded = true;
1380   }
1381
1382   return refreshNeeded;
1383 }
1384
1385 bool CGUIDialogVideoInfo::GetMoviesForSet(const CFileItem *setItem, CFileItemList &originalMovies, CFileItemList &selectedMovies)
1386 {
1387   if (setItem == NULL || !setItem->HasVideoInfoTag())
1388     return false;
1389
1390   CVideoDatabase videodb;
1391   if (!videodb.Open())
1392     return false;
1393
1394   CStdString strHeading = StringUtils::Format(g_localizeStrings.Get(20457));
1395   CStdString baseDir = StringUtils::Format("videodb://movies/sets/%d", setItem->GetVideoInfoTag()->m_iDbId);
1396
1397   if (!CDirectory::GetDirectory(baseDir, originalMovies) || originalMovies.Size() <= 0) // keep a copy of the original members of the set
1398     return false;
1399
1400   CFileItemList listItems;
1401   if (!videodb.GetSortedVideos(MediaTypeMovie, "videodb://movies", SortDescription(), listItems) || listItems.Size() <= 0)
1402     return false;
1403
1404   CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1405   if (dialog == NULL)
1406     return false;
1407
1408   listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1409
1410   dialog->Reset();
1411   dialog->SetMultiSelection(true);
1412   dialog->SetHeading(strHeading);
1413   dialog->SetItems(&listItems);
1414   vector<int> selectedIndices;
1415   for (int i = 0; i < originalMovies.Size(); i++)
1416   {
1417     for (int listIndex = 0; listIndex < listItems.Size(); listIndex++)
1418     {
1419       if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == originalMovies[i]->GetVideoInfoTag()->m_iDbId)
1420       {
1421         selectedIndices.push_back(listIndex);
1422         break;
1423       }
1424     }
1425   }
1426   dialog->SetSelected(selectedIndices);
1427   dialog->EnableButton(true, 186);
1428   dialog->DoModal();
1429
1430   if (dialog->IsConfirmed())
1431   {
1432     selectedMovies.Copy(dialog->GetSelectedItems());
1433     return (selectedMovies.Size() > 0);
1434   }
1435   else
1436     return false;
1437 }
1438
1439 bool CGUIDialogVideoInfo::GetSetForMovie(const CFileItem *movieItem, CFileItemPtr &selectedSet)
1440 {
1441   if (movieItem == NULL || !movieItem->HasVideoInfoTag() ||
1442       selectedSet == NULL)
1443     return false;
1444
1445   CVideoDatabase videodb;
1446   if (!videodb.Open())
1447     return false;
1448
1449   CFileItemList listItems;
1450   CStdString baseDir = "videodb://movies/sets/";
1451   if (!CDirectory::GetDirectory(baseDir, listItems) || listItems.Size() <= 0)
1452     return false;
1453   listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1454
1455   int currentSetId = 0;
1456   CStdString currentSetLabel;
1457
1458   if (movieItem->GetVideoInfoTag()->m_iSetId > currentSetId)
1459   {
1460     currentSetId = movieItem->GetVideoInfoTag()->m_iSetId;
1461     currentSetLabel = videodb.GetSetById(currentSetId);
1462   }
1463
1464   if (currentSetId > 0)
1465   {
1466     // add clear item
1467     CStdString strClear = StringUtils::Format(g_localizeStrings.Get(20467), currentSetLabel.c_str());
1468     CFileItemPtr clearItem(new CFileItem(strClear));
1469     clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
1470     listItems.AddFront(clearItem, 0);
1471     // add keep current set item
1472     CStdString strKeep = StringUtils::Format(g_localizeStrings.Get(20469), currentSetLabel.c_str());
1473     CFileItemPtr keepItem(new CFileItem(strKeep));
1474     keepItem->GetVideoInfoTag()->m_iDbId = currentSetId;
1475     listItems.AddFront(keepItem, 1);
1476   }
1477
1478   CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1479   if (dialog == NULL)
1480     return false;
1481
1482   CStdString strHeading = StringUtils::Format(g_localizeStrings.Get(20466));
1483   dialog->Reset();
1484   dialog->SetHeading(strHeading);
1485   dialog->SetItems(&listItems);
1486   if (currentSetId >= 0)
1487   {
1488     for (int listIndex = 0; listIndex < listItems.Size(); listIndex++) 
1489     {
1490       if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == currentSetId)
1491       {
1492         dialog->SetSelected(listIndex);
1493         break;
1494       }
1495     }
1496   }
1497   dialog->EnableButton(true, 20468); // new set via button
1498   dialog->DoModal();
1499
1500   if (dialog->IsButtonPressed())
1501   { // creating new set
1502     CStdString newSetTitle;
1503     if (!CGUIKeyboardFactory::ShowAndGetInput(newSetTitle, g_localizeStrings.Get(20468), false))
1504       return false;
1505     int idSet = videodb.AddSet(newSetTitle);
1506     map<string, string> movieArt, setArt;
1507     if (!videodb.GetArtForItem(idSet, "set", setArt))
1508     {
1509       videodb.GetArtForItem(movieItem->GetVideoInfoTag()->m_iDbId, "movie", movieArt);
1510       videodb.SetArtForItem(idSet, "set", movieArt);
1511     }
1512     CFileItemPtr newSet(new CFileItem(newSetTitle));
1513     newSet->GetVideoInfoTag()->m_iDbId = idSet;
1514     selectedSet = newSet;
1515     return true;
1516   }
1517   else if (dialog->IsConfirmed())
1518   {
1519     selectedSet = dialog->GetSelectedItem();
1520     return (selectedSet != NULL);
1521   }
1522   else
1523     return false;
1524 }
1525
1526 bool CGUIDialogVideoInfo::SetMovieSet(const CFileItem *movieItem, const CFileItem *selectedSet)
1527 {
1528   if (movieItem == NULL || !movieItem->HasVideoInfoTag() ||
1529       selectedSet == NULL || !selectedSet->HasVideoInfoTag())
1530     return false;
1531
1532   CVideoDatabase videodb;
1533   if (!videodb.Open())
1534     return false;
1535
1536   videodb.SetMovieSet(movieItem->GetVideoInfoTag()->m_iDbId, selectedSet->GetVideoInfoTag()->m_iDbId);
1537   return true;
1538 }
1539
1540 bool CGUIDialogVideoInfo::GetItemsForTag(const CStdString &strHeading, const std::string &type, CFileItemList &items, int idTag /* = -1 */, bool showAll /* = true */)
1541 {
1542   CVideoDatabase videodb;
1543   if (!videodb.Open())
1544     return false;
1545
1546   MediaType mediaType = MediaTypeNone;
1547   std::string baseDir = "videodb://";
1548   std::string idColumn;
1549   if (type.compare("movie") == 0)
1550   {
1551     mediaType = MediaTypeMovie;
1552     baseDir += "movies";
1553     idColumn = "idMovie";
1554   }
1555   else if (type.compare("tvshow") == 0)
1556   {
1557     mediaType = MediaTypeTvShow;
1558     baseDir += "tvshows";
1559     idColumn = "idShow";
1560   }
1561   else if (type.compare("musicvideo") == 0)
1562   {
1563     mediaType = MediaTypeMusicVideo;
1564     baseDir += "musicvideos";
1565     idColumn = "idMVideo";
1566   }
1567
1568   baseDir += "/titles/";
1569   CVideoDbUrl videoUrl;
1570   if (!videoUrl.FromString(baseDir))
1571     return false;
1572
1573   CVideoDatabase::Filter filter;
1574   if (idTag > 0)
1575   {
1576     if (!showAll)
1577       videoUrl.AddOption("tagid", idTag);
1578     else
1579       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());
1580   }
1581
1582   CFileItemList listItems;
1583   if (!videodb.GetSortedVideos(mediaType, videoUrl.ToString(), SortDescription(), listItems, filter) || listItems.Size() <= 0)
1584     return false;
1585
1586   CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1587   if (dialog == NULL)
1588     return false;
1589
1590   listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1591
1592   dialog->Reset();
1593   dialog->SetMultiSelection(true);
1594   dialog->SetHeading(strHeading);
1595   dialog->SetItems(&listItems);
1596   dialog->EnableButton(true, 186);
1597   dialog->DoModal();
1598
1599   items.Copy(dialog->GetSelectedItems());
1600   return items.Size() > 0;
1601 }
1602
1603 bool CGUIDialogVideoInfo::AddItemsToTag(const CFileItemPtr &tagItem)
1604 {
1605   if (tagItem == NULL || !tagItem->HasVideoInfoTag())
1606     return false;
1607
1608   CVideoDbUrl videoUrl;
1609   if (!videoUrl.FromString(tagItem->GetPath()))
1610     return false;
1611
1612   CVideoDatabase videodb;
1613   if (!videodb.Open())
1614     return true;
1615
1616   std::string mediaType = videoUrl.GetItemType();
1617   mediaType = mediaType.substr(0, mediaType.length() - 1);
1618
1619   CFileItemList items;
1620   std::string localizedType = GetLocalizedVideoType(mediaType);
1621   std::string strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType.c_str());
1622   if (!GetItemsForTag(strLabel, mediaType, items, tagItem->GetVideoInfoTag()->m_iDbId))
1623     return true;
1624
1625   for (int index = 0; index < items.Size(); index++)
1626   {
1627     if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1628       continue;
1629
1630     videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, tagItem->GetVideoInfoTag()->m_iDbId, mediaType);
1631   }
1632
1633   return true;
1634 }
1635
1636 bool CGUIDialogVideoInfo::RemoveItemsFromTag(const CFileItemPtr &tagItem)
1637 {
1638   if (tagItem == NULL || !tagItem->HasVideoInfoTag())
1639     return false;
1640
1641   CVideoDbUrl videoUrl;
1642   if (!videoUrl.FromString(tagItem->GetPath()))
1643     return false;
1644
1645   CVideoDatabase videodb;
1646   if (!videodb.Open())
1647     return true;
1648
1649   std::string mediaType = videoUrl.GetItemType();
1650   mediaType = mediaType.substr(0, mediaType.length() - 1);
1651
1652   CFileItemList items;
1653   std::string localizedType = GetLocalizedVideoType(mediaType);
1654   std::string strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType.c_str());
1655   if (!GetItemsForTag(strLabel, mediaType, items, tagItem->GetVideoInfoTag()->m_iDbId, false))
1656     return true;
1657
1658   for (int index = 0; index < items.Size(); index++)
1659   {
1660     if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1661       continue;
1662
1663     videodb.RemoveTagFromItem(items[index]->GetVideoInfoTag()->m_iDbId, tagItem->GetVideoInfoTag()->m_iDbId, mediaType);
1664   }
1665
1666   return true;
1667 }
1668
1669 bool CGUIDialogVideoInfo::ManageVideoItemArtwork(const CFileItemPtr &item, const std::string &type)
1670 {
1671   if (item == NULL || !item->HasVideoInfoTag() || type.empty())
1672     return false;
1673
1674   CVideoDatabase videodb;
1675   if (!videodb.Open())
1676     return true;
1677
1678   // Grab the thumbnails from the web
1679   CFileItemList items;
1680   CFileItemPtr noneitem(new CFileItem("thumb://None", false));
1681   std::string currentThumb;
1682   int idArtist = -1;
1683   CStdString artistPath;
1684   string artType = "thumb";
1685   if (type == "artist")
1686   {
1687     CMusicDatabase musicdb;
1688     if (musicdb.Open())
1689     {
1690       idArtist = musicdb.GetArtistByName(item->GetLabel());
1691       if (idArtist >= 0 && musicdb.GetArtistPath(idArtist, artistPath))
1692       {
1693         currentThumb = musicdb.GetArtForItem(idArtist, "artist", "thumb");
1694         if (currentThumb.empty())
1695           currentThumb = videodb.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType);
1696       }
1697     }
1698   }
1699   else if (type == "actor")
1700     currentThumb = videodb.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType);
1701   else
1702   { // SEASON, SET
1703     map<string, string> currentArt;
1704     artType = ChooseArtType(*item, currentArt);
1705     if (artType.empty())
1706       return false;
1707
1708     if (artType == "fanart")
1709       return OnGetFanart(item);
1710
1711     if (currentArt.find(artType) != currentArt.end())
1712       currentThumb = currentArt[artType];
1713     else if ((artType == "poster" || artType == "banner") && currentArt.find("thumb") != currentArt.end())
1714       currentThumb = currentArt["thumb"];
1715   }
1716
1717   if (!currentThumb.empty())
1718   {
1719     CFileItemPtr item(new CFileItem("thumb://Current", false));
1720     item->SetArt("thumb", currentThumb);
1721     item->SetLabel(g_localizeStrings.Get(13512));
1722     items.Add(item);
1723   }
1724   noneitem->SetIconImage("DefaultFolder.png");
1725   noneitem->SetLabel(g_localizeStrings.Get(13515));
1726
1727   bool local = false;
1728   vector<CStdString> thumbs;
1729   if (type != "artist")
1730   {
1731     CVideoInfoTag tag;
1732     if (type == "season")
1733       videodb.GetTvShowInfo("", tag, item->GetVideoInfoTag()->m_iIdShow);
1734     else
1735       tag = *item->GetVideoInfoTag();
1736
1737     tag.m_strPictureURL.GetThumbURLs(thumbs, artType, type == "season" ? item->GetVideoInfoTag()->m_iSeason : -1);
1738
1739     for (size_t i = 0; i < thumbs.size(); i++)
1740     {
1741       CFileItemPtr item(new CFileItem(StringUtils::Format("thumb://Remote%i", i), false));
1742       item->SetArt("thumb", thumbs[i]);
1743       item->SetIconImage("DefaultPicture.png");
1744       item->SetLabel(g_localizeStrings.Get(13513));
1745       items.Add(item);
1746
1747       // TODO: Do we need to clear the cached image?
1748       //    CTextureCache::Get().ClearCachedImage(thumbs[i]);
1749     }
1750
1751     if (type == "actor")
1752     {
1753       CStdString picturePath;
1754       CStdString strThumb = URIUtils::AddFileToFolder(picturePath, "folder.jpg");
1755       if (XFILE::CFile::Exists(strThumb))
1756       {
1757         CFileItemPtr pItem(new CFileItem(strThumb,false));
1758         pItem->SetLabel(g_localizeStrings.Get(13514));
1759         pItem->SetArt("thumb", strThumb);
1760         items.Add(pItem);
1761         local = true;
1762       }
1763       else
1764         noneitem->SetIconImage("DefaultActor.png");
1765     }
1766
1767     if (type == "set")
1768       noneitem->SetIconImage("DefaultVideo.png");
1769   }
1770   else
1771   {
1772     CStdString strThumb = URIUtils::AddFileToFolder(artistPath, "folder.jpg");
1773     if (XFILE::CFile::Exists(strThumb))
1774     {
1775       CFileItemPtr pItem(new CFileItem(strThumb, false));
1776       pItem->SetLabel(g_localizeStrings.Get(13514));
1777       pItem->SetArt("thumb", strThumb);
1778       items.Add(pItem);
1779       local = true;
1780     }
1781     else
1782       noneitem->SetIconImage("DefaultArtist.png");
1783   }
1784
1785   if (!local)
1786     items.Add(noneitem);
1787   
1788   CStdString result;
1789   VECSOURCES sources=*CMediaSourceSettings::Get().GetSources("video");
1790   g_mediaManager.GetLocalDrives(sources);
1791   AddItemPathToFileBrowserSources(sources, *item);
1792   if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result))
1793     return false;   // user cancelled
1794
1795   if (result == "thumb://Current")
1796     result = currentThumb;   // user chose the one they have
1797   
1798   // delete the thumbnail if that's what the user wants, else overwrite with the
1799   // new thumbnail
1800   if (result == "thumb://None")
1801     result.clear();
1802   else if (StringUtils::StartsWith(result, "thumb://Remote"))
1803   {
1804     int number = atoi(StringUtils::Mid(result, 14).c_str());
1805     result = thumbs[number];
1806   }
1807
1808   // write the selected artwork to the database
1809   if (type == "set" ||
1810       type == "actor" ||
1811       type == "season" ||
1812       (type == "artist" && idArtist < 0))
1813     videodb.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType, result);
1814   else
1815   {
1816     CMusicDatabase musicdb;
1817     if (musicdb.Open())
1818       musicdb.SetArtForItem(idArtist, "artist", artType, result);
1819   }
1820
1821   CUtil::DeleteVideoDatabaseDirectoryCache();
1822   CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
1823   g_windowManager.SendMessage(msg);
1824
1825   return true;
1826 }
1827
1828 std::string CGUIDialogVideoInfo::GetLocalizedVideoType(const std::string &strType)
1829 {
1830   if (strType == "movie" || strType == "movies")
1831     return g_localizeStrings.Get(20342);
1832   else if (strType == "tvshow" || strType == "tvshows")
1833     return g_localizeStrings.Get(20343);
1834   else if (strType == "episode" || strType == "episodes")
1835     return g_localizeStrings.Get(20359);
1836   else if (strType == "musicvideo" || strType == "musicvideos")
1837     return g_localizeStrings.Get(20391);
1838
1839   return "";
1840 }
1841
1842 bool CGUIDialogVideoInfo::UpdateVideoItemSortTitle(const CFileItemPtr &pItem)
1843 {
1844   // dont allow update while scanning
1845   if (g_application.IsVideoScanning())
1846   {
1847     CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1848     return false;
1849   }
1850
1851   CVideoDatabase database;
1852   if (!database.Open())
1853     return false;
1854
1855   int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
1856   CVideoInfoTag detail;
1857   VIDEODB_CONTENT_TYPE iType = (VIDEODB_CONTENT_TYPE)pItem->GetVideoContentType();
1858   if (iType == VIDEODB_CONTENT_MOVIES)
1859     database.GetMovieInfo("", detail, iDbId);
1860   else if (iType == VIDEODB_CONTENT_TVSHOWS)
1861     database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId);
1862
1863   CStdString currentTitle;
1864   if (detail.m_strSortTitle.empty())
1865     currentTitle = detail.m_strTitle;
1866   else
1867     currentTitle = detail.m_strSortTitle;
1868   
1869   // get the new sort title
1870   if (!CGUIKeyboardFactory::ShowAndGetInput(currentTitle, g_localizeStrings.Get(16107), false))
1871     return false;
1872
1873   return database.UpdateVideoSortTitle(iDbId, currentTitle, iType);
1874 }
1875
1876 bool CGUIDialogVideoInfo::LinkMovieToTvShow(const CFileItemPtr &item, bool bRemove, CVideoDatabase &database)
1877 {
1878   int dbId = item->GetVideoInfoTag()->m_iDbId;
1879
1880   CFileItemList list;
1881   if (bRemove)
1882   {
1883     vector<int> ids;
1884     if (!database.GetLinksToTvShow(dbId, ids))
1885       return false;
1886
1887     for (unsigned int i = 0; i < ids.size(); ++i)
1888     {
1889       CVideoInfoTag tag;
1890       database.GetTvShowInfo("", tag, ids[i]);
1891       CFileItemPtr show(new CFileItem(tag));
1892       list.Add(show);
1893     }
1894   }
1895   else
1896   {
1897     database.GetTvShowsNav("videodb://tvshows/titles", list);
1898
1899     // remove already linked shows
1900     vector<int> ids;
1901     if (!database.GetLinksToTvShow(dbId, ids))
1902       return false;
1903
1904     for (int i = 0; i < list.Size(); )
1905     {
1906       size_t j;
1907       for (j = 0; j < ids.size(); ++j)
1908       {
1909         if (list[i]->GetVideoInfoTag()->m_iDbId == ids[j])
1910           break;
1911       }
1912       if (j == ids.size())
1913         i++;
1914       else
1915         list.Remove(i);
1916     }
1917   }
1918
1919   int iSelectedLabel = 0;
1920   if (list.Size() > 1)
1921   {
1922     list.Sort(SortByLabel, SortOrderAscending, CSettings::Get().GetBool("filelists.ignorethewhensorting") ? SortAttributeIgnoreArticle : SortAttributeNone);
1923     CGUIDialogSelect* pDialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1924     pDialog->Reset();
1925     pDialog->SetItems(&list);
1926     pDialog->SetHeading(20356);
1927     pDialog->DoModal();
1928     iSelectedLabel = pDialog->GetSelectedLabel();
1929   }
1930
1931   if (iSelectedLabel > -1)
1932     return database.LinkMovieToTvshow(dbId, list[iSelectedLabel]->GetVideoInfoTag()->m_iDbId, bRemove);
1933
1934   return false;
1935 }
1936
1937 bool CGUIDialogVideoInfo::OnGetFanart(const CFileItemPtr &videoItem)
1938 {
1939   if (videoItem == NULL || !videoItem->HasVideoInfoTag())
1940     return false;
1941
1942   // update the db
1943   CVideoDatabase videodb;
1944   if (!videodb.Open())
1945     return false;
1946
1947   CVideoThumbLoader loader;
1948   CFileItem item(*videoItem);
1949   loader.LoadItem(&item);
1950   
1951   CFileItemList items;
1952   if (item.HasArt("fanart"))
1953   {
1954     CFileItemPtr itemCurrent(new CFileItem("fanart://Current", false));
1955     itemCurrent->SetArt("thumb", item.GetArt("fanart"));
1956     itemCurrent->SetLabel(g_localizeStrings.Get(20440));
1957     items.Add(itemCurrent);
1958   }
1959
1960   // add the none option
1961   {
1962     CFileItemPtr itemNone(new CFileItem("fanart://None", false));
1963     itemNone->SetIconImage("DefaultVideo.png");
1964     itemNone->SetLabel(g_localizeStrings.Get(20439));
1965     items.Add(itemNone);
1966   }
1967
1968   CStdString result;
1969   VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
1970   g_mediaManager.GetLocalDrives(sources);
1971   AddItemPathToFileBrowserSources(sources, item);
1972   bool flip = false;
1973   if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) ||
1974       StringUtils::EqualsNoCase(result, "fanart://Current"))
1975     return false;
1976
1977   if (StringUtils::EqualsNoCase(result, "fanart://None") || !CFile::Exists(result))
1978     result.clear();
1979   if (!result.empty() && flip)
1980     result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
1981
1982   videodb.SetArtForItem(item.GetVideoInfoTag()->m_iDbId, item.GetVideoInfoTag()->m_type, "fanart", result);
1983
1984   // clear view cache and reload images
1985   CUtil::DeleteVideoDatabaseDirectoryCache();
1986
1987   return true;
1988 }