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