Merge pull request #3750 from Karlson2k/update_utf8_check
[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     return false;
1443
1444   CVideoDatabase videodb;
1445   if (!videodb.Open())
1446     return false;
1447
1448   CFileItemList listItems;
1449   CStdString baseDir = "videodb://movies/sets/";
1450   if (!CDirectory::GetDirectory(baseDir, listItems) || listItems.Size() <= 0)
1451     return false;
1452   listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1453
1454   int currentSetId = 0;
1455   CStdString currentSetLabel;
1456
1457   if (movieItem->GetVideoInfoTag()->m_iSetId > currentSetId)
1458   {
1459     currentSetId = movieItem->GetVideoInfoTag()->m_iSetId;
1460     currentSetLabel = videodb.GetSetById(currentSetId);
1461   }
1462
1463   if (currentSetId > 0)
1464   {
1465     // add clear item
1466     CStdString strClear = StringUtils::Format(g_localizeStrings.Get(20467), currentSetLabel.c_str());
1467     CFileItemPtr clearItem(new CFileItem(strClear));
1468     clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
1469     listItems.AddFront(clearItem, 0);
1470     // add keep current set item
1471     CStdString strKeep = StringUtils::Format(g_localizeStrings.Get(20469), currentSetLabel.c_str());
1472     CFileItemPtr keepItem(new CFileItem(strKeep));
1473     keepItem->GetVideoInfoTag()->m_iDbId = currentSetId;
1474     listItems.AddFront(keepItem, 1);
1475   }
1476
1477   CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1478   if (dialog == NULL)
1479     return false;
1480
1481   CStdString strHeading = StringUtils::Format(g_localizeStrings.Get(20466));
1482   dialog->Reset();
1483   dialog->SetHeading(strHeading);
1484   dialog->SetItems(&listItems);
1485   if (currentSetId >= 0)
1486   {
1487     for (int listIndex = 0; listIndex < listItems.Size(); listIndex++) 
1488     {
1489       if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == currentSetId)
1490       {
1491         dialog->SetSelected(listIndex);
1492         break;
1493       }
1494     }
1495   }
1496   dialog->EnableButton(true, 20468); // new set via button
1497   dialog->DoModal();
1498
1499   if (dialog->IsButtonPressed())
1500   { // creating new set
1501     CStdString newSetTitle;
1502     if (!CGUIKeyboardFactory::ShowAndGetInput(newSetTitle, g_localizeStrings.Get(20468), false))
1503       return false;
1504     int idSet = videodb.AddSet(newSetTitle);
1505     map<string, string> movieArt, setArt;
1506     if (!videodb.GetArtForItem(idSet, "set", setArt))
1507     {
1508       videodb.GetArtForItem(movieItem->GetVideoInfoTag()->m_iDbId, "movie", movieArt);
1509       videodb.SetArtForItem(idSet, "set", movieArt);
1510     }
1511     CFileItemPtr newSet(new CFileItem(newSetTitle));
1512     newSet->GetVideoInfoTag()->m_iDbId = idSet;
1513     selectedSet = newSet;
1514     return true;
1515   }
1516   else if (dialog->IsConfirmed())
1517   {
1518     selectedSet = dialog->GetSelectedItem();
1519     return (selectedSet != NULL);
1520   }
1521   else
1522     return false;
1523 }
1524
1525 bool CGUIDialogVideoInfo::SetMovieSet(const CFileItem *movieItem, const CFileItem *selectedSet)
1526 {
1527   if (movieItem == NULL || !movieItem->HasVideoInfoTag() ||
1528       selectedSet == NULL || !selectedSet->HasVideoInfoTag())
1529     return false;
1530
1531   CVideoDatabase videodb;
1532   if (!videodb.Open())
1533     return false;
1534
1535   videodb.SetMovieSet(movieItem->GetVideoInfoTag()->m_iDbId, selectedSet->GetVideoInfoTag()->m_iDbId);
1536   return true;
1537 }
1538
1539 bool CGUIDialogVideoInfo::GetItemsForTag(const CStdString &strHeading, const std::string &type, CFileItemList &items, int idTag /* = -1 */, bool showAll /* = true */)
1540 {
1541   CVideoDatabase videodb;
1542   if (!videodb.Open())
1543     return false;
1544
1545   MediaType mediaType = MediaTypeNone;
1546   std::string baseDir = "videodb://";
1547   std::string idColumn;
1548   if (type.compare("movie") == 0)
1549   {
1550     mediaType = MediaTypeMovie;
1551     baseDir += "movies";
1552     idColumn = "idMovie";
1553   }
1554   else if (type.compare("tvshow") == 0)
1555   {
1556     mediaType = MediaTypeTvShow;
1557     baseDir += "tvshows";
1558     idColumn = "idShow";
1559   }
1560   else if (type.compare("musicvideo") == 0)
1561   {
1562     mediaType = MediaTypeMusicVideo;
1563     baseDir += "musicvideos";
1564     idColumn = "idMVideo";
1565   }
1566
1567   baseDir += "/titles/";
1568   CVideoDbUrl videoUrl;
1569   if (!videoUrl.FromString(baseDir))
1570     return false;
1571
1572   CVideoDatabase::Filter filter;
1573   if (idTag > 0)
1574   {
1575     if (!showAll)
1576       videoUrl.AddOption("tagid", idTag);
1577     else
1578       filter.where = videodb.PrepareSQL("%sview.%s NOT IN (SELECT taglinks.idMedia FROM taglinks WHERE taglinks.idTag = %d AND taglinks.media_type = '%s')", type.c_str(), idColumn.c_str(), idTag, type.c_str());
1579   }
1580
1581   CFileItemList listItems;
1582   if (!videodb.GetSortedVideos(mediaType, videoUrl.ToString(), SortDescription(), listItems, filter) || listItems.Size() <= 0)
1583     return false;
1584
1585   CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1586   if (dialog == NULL)
1587     return false;
1588
1589   listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1590
1591   dialog->Reset();
1592   dialog->SetMultiSelection(true);
1593   dialog->SetHeading(strHeading);
1594   dialog->SetItems(&listItems);
1595   dialog->EnableButton(true, 186);
1596   dialog->DoModal();
1597
1598   items.Copy(dialog->GetSelectedItems());
1599   return items.Size() > 0;
1600 }
1601
1602 bool CGUIDialogVideoInfo::AddItemsToTag(const CFileItemPtr &tagItem)
1603 {
1604   if (tagItem == NULL || !tagItem->HasVideoInfoTag())
1605     return false;
1606
1607   CVideoDbUrl videoUrl;
1608   if (!videoUrl.FromString(tagItem->GetPath()))
1609     return false;
1610
1611   CVideoDatabase videodb;
1612   if (!videodb.Open())
1613     return true;
1614
1615   std::string mediaType = videoUrl.GetItemType();
1616   mediaType = mediaType.substr(0, mediaType.length() - 1);
1617
1618   CFileItemList items;
1619   std::string localizedType = GetLocalizedVideoType(mediaType);
1620   std::string strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType.c_str());
1621   if (!GetItemsForTag(strLabel, mediaType, items, tagItem->GetVideoInfoTag()->m_iDbId))
1622     return true;
1623
1624   for (int index = 0; index < items.Size(); index++)
1625   {
1626     if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1627       continue;
1628
1629     videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, tagItem->GetVideoInfoTag()->m_iDbId, mediaType);
1630   }
1631
1632   return true;
1633 }
1634
1635 bool CGUIDialogVideoInfo::RemoveItemsFromTag(const CFileItemPtr &tagItem)
1636 {
1637   if (tagItem == NULL || !tagItem->HasVideoInfoTag())
1638     return false;
1639
1640   CVideoDbUrl videoUrl;
1641   if (!videoUrl.FromString(tagItem->GetPath()))
1642     return false;
1643
1644   CVideoDatabase videodb;
1645   if (!videodb.Open())
1646     return true;
1647
1648   std::string mediaType = videoUrl.GetItemType();
1649   mediaType = mediaType.substr(0, mediaType.length() - 1);
1650
1651   CFileItemList items;
1652   std::string localizedType = GetLocalizedVideoType(mediaType);
1653   std::string strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType.c_str());
1654   if (!GetItemsForTag(strLabel, mediaType, items, tagItem->GetVideoInfoTag()->m_iDbId, false))
1655     return true;
1656
1657   for (int index = 0; index < items.Size(); index++)
1658   {
1659     if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1660       continue;
1661
1662     videodb.RemoveTagFromItem(items[index]->GetVideoInfoTag()->m_iDbId, tagItem->GetVideoInfoTag()->m_iDbId, mediaType);
1663   }
1664
1665   return true;
1666 }
1667
1668 bool CGUIDialogVideoInfo::ManageVideoItemArtwork(const CFileItemPtr &item, const std::string &type)
1669 {
1670   if (item == NULL || !item->HasVideoInfoTag() || type.empty())
1671     return false;
1672
1673   CVideoDatabase videodb;
1674   if (!videodb.Open())
1675     return true;
1676
1677   // Grab the thumbnails from the web
1678   CFileItemList items;
1679   CFileItemPtr noneitem(new CFileItem("thumb://None", false));
1680   std::string currentThumb;
1681   int idArtist = -1;
1682   CStdString artistPath;
1683   string artType = "thumb";
1684   if (type == "artist")
1685   {
1686     CMusicDatabase musicdb;
1687     if (musicdb.Open())
1688     {
1689       idArtist = musicdb.GetArtistByName(item->GetLabel());
1690       if (idArtist >= 0 && musicdb.GetArtistPath(idArtist, artistPath))
1691       {
1692         currentThumb = musicdb.GetArtForItem(idArtist, "artist", "thumb");
1693         if (currentThumb.empty())
1694           currentThumb = videodb.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType);
1695       }
1696     }
1697   }
1698   else if (type == "actor")
1699     currentThumb = videodb.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType);
1700   else
1701   { // SEASON, SET
1702     map<string, string> currentArt;
1703     artType = ChooseArtType(*item, currentArt);
1704     if (artType.empty())
1705       return false;
1706
1707     if (artType == "fanart")
1708       return OnGetFanart(item);
1709
1710     if (currentArt.find(artType) != currentArt.end())
1711       currentThumb = currentArt[artType];
1712     else if ((artType == "poster" || artType == "banner") && currentArt.find("thumb") != currentArt.end())
1713       currentThumb = currentArt["thumb"];
1714   }
1715
1716   if (!currentThumb.empty())
1717   {
1718     CFileItemPtr item(new CFileItem("thumb://Current", false));
1719     item->SetArt("thumb", currentThumb);
1720     item->SetLabel(g_localizeStrings.Get(13512));
1721     items.Add(item);
1722   }
1723   noneitem->SetIconImage("DefaultFolder.png");
1724   noneitem->SetLabel(g_localizeStrings.Get(13515));
1725
1726   bool local = false;
1727   vector<CStdString> thumbs;
1728   if (type != "artist")
1729   {
1730     CVideoInfoTag tag;
1731     if (type == "season")
1732       videodb.GetTvShowInfo("", tag, item->GetVideoInfoTag()->m_iIdShow);
1733     else
1734       tag = *item->GetVideoInfoTag();
1735
1736     tag.m_strPictureURL.GetThumbURLs(thumbs, artType, type == "season" ? item->GetVideoInfoTag()->m_iSeason : -1);
1737
1738     for (size_t i = 0; i < thumbs.size(); i++)
1739     {
1740       CFileItemPtr item(new CFileItem(StringUtils::Format("thumb://Remote%i", i), false));
1741       item->SetArt("thumb", thumbs[i]);
1742       item->SetIconImage("DefaultPicture.png");
1743       item->SetLabel(g_localizeStrings.Get(13513));
1744       items.Add(item);
1745
1746       // TODO: Do we need to clear the cached image?
1747       //    CTextureCache::Get().ClearCachedImage(thumbs[i]);
1748     }
1749
1750     if (type == "actor")
1751     {
1752       CStdString picturePath;
1753       CStdString strThumb = URIUtils::AddFileToFolder(picturePath, "folder.jpg");
1754       if (XFILE::CFile::Exists(strThumb))
1755       {
1756         CFileItemPtr pItem(new CFileItem(strThumb,false));
1757         pItem->SetLabel(g_localizeStrings.Get(13514));
1758         pItem->SetArt("thumb", strThumb);
1759         items.Add(pItem);
1760         local = true;
1761       }
1762       else
1763         noneitem->SetIconImage("DefaultActor.png");
1764     }
1765
1766     if (type == "set")
1767       noneitem->SetIconImage("DefaultVideo.png");
1768   }
1769   else
1770   {
1771     CStdString strThumb = URIUtils::AddFileToFolder(artistPath, "folder.jpg");
1772     if (XFILE::CFile::Exists(strThumb))
1773     {
1774       CFileItemPtr pItem(new CFileItem(strThumb, false));
1775       pItem->SetLabel(g_localizeStrings.Get(13514));
1776       pItem->SetArt("thumb", strThumb);
1777       items.Add(pItem);
1778       local = true;
1779     }
1780     else
1781       noneitem->SetIconImage("DefaultArtist.png");
1782   }
1783
1784   if (!local)
1785     items.Add(noneitem);
1786   
1787   CStdString result;
1788   VECSOURCES sources=*CMediaSourceSettings::Get().GetSources("video");
1789   g_mediaManager.GetLocalDrives(sources);
1790   AddItemPathToFileBrowserSources(sources, *item);
1791   if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result))
1792     return false;   // user cancelled
1793
1794   if (result == "thumb://Current")
1795     result = currentThumb;   // user chose the one they have
1796   
1797   // delete the thumbnail if that's what the user wants, else overwrite with the
1798   // new thumbnail
1799   if (result == "thumb://None")
1800     result.clear();
1801   else if (StringUtils::StartsWith(result, "thumb://Remote"))
1802   {
1803     int number = atoi(StringUtils::Mid(result, 14).c_str());
1804     result = thumbs[number];
1805   }
1806
1807   // write the selected artwork to the database
1808   if (type == "set" ||
1809       type == "actor" ||
1810       type == "season" ||
1811       (type == "artist" && idArtist < 0))
1812     videodb.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType, result);
1813   else
1814   {
1815     CMusicDatabase musicdb;
1816     if (musicdb.Open())
1817       musicdb.SetArtForItem(idArtist, "artist", artType, result);
1818   }
1819
1820   CUtil::DeleteVideoDatabaseDirectoryCache();
1821   CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
1822   g_windowManager.SendMessage(msg);
1823
1824   return true;
1825 }
1826
1827 std::string CGUIDialogVideoInfo::GetLocalizedVideoType(const std::string &strType)
1828 {
1829   if (strType == "movie" || strType == "movies")
1830     return g_localizeStrings.Get(20342);
1831   else if (strType == "tvshow" || strType == "tvshows")
1832     return g_localizeStrings.Get(20343);
1833   else if (strType == "episode" || strType == "episodes")
1834     return g_localizeStrings.Get(20359);
1835   else if (strType == "musicvideo" || strType == "musicvideos")
1836     return g_localizeStrings.Get(20391);
1837
1838   return "";
1839 }
1840
1841 bool CGUIDialogVideoInfo::UpdateVideoItemSortTitle(const CFileItemPtr &pItem)
1842 {
1843   // dont allow update while scanning
1844   if (g_application.IsVideoScanning())
1845   {
1846     CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1847     return false;
1848   }
1849
1850   CVideoDatabase database;
1851   if (!database.Open())
1852     return false;
1853
1854   int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
1855   CVideoInfoTag detail;
1856   VIDEODB_CONTENT_TYPE iType = (VIDEODB_CONTENT_TYPE)pItem->GetVideoContentType();
1857   if (iType == VIDEODB_CONTENT_MOVIES)
1858     database.GetMovieInfo("", detail, iDbId);
1859   else if (iType == VIDEODB_CONTENT_TVSHOWS)
1860     database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId);
1861
1862   CStdString currentTitle;
1863   if (detail.m_strSortTitle.empty())
1864     currentTitle = detail.m_strTitle;
1865   else
1866     currentTitle = detail.m_strSortTitle;
1867   
1868   // get the new sort title
1869   if (!CGUIKeyboardFactory::ShowAndGetInput(currentTitle, g_localizeStrings.Get(16107), false))
1870     return false;
1871
1872   return database.UpdateVideoSortTitle(iDbId, currentTitle, iType);
1873 }
1874
1875 bool CGUIDialogVideoInfo::LinkMovieToTvShow(const CFileItemPtr &item, bool bRemove, CVideoDatabase &database)
1876 {
1877   int dbId = item->GetVideoInfoTag()->m_iDbId;
1878
1879   CFileItemList list;
1880   if (bRemove)
1881   {
1882     vector<int> ids;
1883     if (!database.GetLinksToTvShow(dbId, ids))
1884       return false;
1885
1886     for (unsigned int i = 0; i < ids.size(); ++i)
1887     {
1888       CVideoInfoTag tag;
1889       database.GetTvShowInfo("", tag, ids[i]);
1890       CFileItemPtr show(new CFileItem(tag));
1891       list.Add(show);
1892     }
1893   }
1894   else
1895   {
1896     database.GetTvShowsNav("videodb://tvshows/titles", list);
1897
1898     // remove already linked shows
1899     vector<int> ids;
1900     if (!database.GetLinksToTvShow(dbId, ids))
1901       return false;
1902
1903     for (int i = 0; i < list.Size(); )
1904     {
1905       size_t j;
1906       for (j = 0; j < ids.size(); ++j)
1907       {
1908         if (list[i]->GetVideoInfoTag()->m_iDbId == ids[j])
1909           break;
1910       }
1911       if (j == ids.size())
1912         i++;
1913       else
1914         list.Remove(i);
1915     }
1916   }
1917
1918   int iSelectedLabel = 0;
1919   if (list.Size() > 1)
1920   {
1921     list.Sort(SortByLabel, SortOrderAscending, CSettings::Get().GetBool("filelists.ignorethewhensorting") ? SortAttributeIgnoreArticle : SortAttributeNone);
1922     CGUIDialogSelect* pDialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1923     pDialog->Reset();
1924     pDialog->SetItems(&list);
1925     pDialog->SetHeading(20356);
1926     pDialog->DoModal();
1927     iSelectedLabel = pDialog->GetSelectedLabel();
1928   }
1929
1930   if (iSelectedLabel > -1)
1931     return database.LinkMovieToTvshow(dbId, list[iSelectedLabel]->GetVideoInfoTag()->m_iDbId, bRemove);
1932
1933   return false;
1934 }
1935
1936 bool CGUIDialogVideoInfo::OnGetFanart(const CFileItemPtr &videoItem)
1937 {
1938   if (videoItem == NULL || !videoItem->HasVideoInfoTag())
1939     return false;
1940
1941   // update the db
1942   CVideoDatabase videodb;
1943   if (!videodb.Open())
1944     return false;
1945
1946   CVideoThumbLoader loader;
1947   CFileItem item(*videoItem);
1948   loader.LoadItem(&item);
1949   
1950   CFileItemList items;
1951   if (item.HasArt("fanart"))
1952   {
1953     CFileItemPtr itemCurrent(new CFileItem("fanart://Current", false));
1954     itemCurrent->SetArt("thumb", item.GetArt("fanart"));
1955     itemCurrent->SetLabel(g_localizeStrings.Get(20440));
1956     items.Add(itemCurrent);
1957   }
1958
1959   // add the none option
1960   {
1961     CFileItemPtr itemNone(new CFileItem("fanart://None", false));
1962     itemNone->SetIconImage("DefaultVideo.png");
1963     itemNone->SetLabel(g_localizeStrings.Get(20439));
1964     items.Add(itemNone);
1965   }
1966
1967   CStdString result;
1968   VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
1969   g_mediaManager.GetLocalDrives(sources);
1970   AddItemPathToFileBrowserSources(sources, item);
1971   bool flip = false;
1972   if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) ||
1973       StringUtils::EqualsNoCase(result, "fanart://Current"))
1974     return false;
1975
1976   if (StringUtils::EqualsNoCase(result, "fanart://None") || !CFile::Exists(result))
1977     result.clear();
1978   if (!result.empty() && flip)
1979     result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
1980
1981   videodb.SetArtForItem(item.GetVideoInfoTag()->m_iDbId, item.GetVideoInfoTag()->m_type, "fanart", result);
1982
1983   // clear view cache and reload images
1984   CUtil::DeleteVideoDatabaseDirectoryCache();
1985
1986   return true;
1987 }