context menu: use string-based media type detection instead of VIDEO_DB_CONTENT_FOO
[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 CGUIDialogVideoInfo::CGUIDialogVideoInfo(void)
80     : CGUIDialog(WINDOW_DIALOG_VIDEO_INFO, "DialogVideoInfo.xml")
81     , m_movieItem(new CFileItem)
82 {
83   m_bRefreshAll = true;
84   m_bRefresh = false;
85   m_hasUpdatedThumb = false;
86   m_castList = new CFileItemList;
87   m_loadType = KEEP_IN_MEMORY;
88 }
89
90 CGUIDialogVideoInfo::~CGUIDialogVideoInfo(void)
91 {
92   delete m_castList;
93 }
94
95 bool CGUIDialogVideoInfo::OnMessage(CGUIMessage& message)
96 {
97   switch ( message.GetMessage() )
98   {
99   case GUI_MSG_WINDOW_DEINIT:
100     {
101       ClearCastList();
102     }
103     break;
104
105   case GUI_MSG_CLICKED:
106     {
107       int iControl = message.GetSenderId();
108       if (iControl == CONTROL_BTN_REFRESH)
109       {
110         if (m_movieItem->GetVideoInfoTag()->m_iSeason < 0 && !m_movieItem->GetVideoInfoTag()->m_strShowTitle.empty()) // tv show
111         {
112           bool bCanceled=false;
113           if (CGUIDialogYesNo::ShowAndGetInput(20377,20378,-1,-1,bCanceled))
114           {
115             m_bRefreshAll = true;
116             CVideoDatabase db;
117             if (db.Open())
118             {
119               db.SetPathHash(m_movieItem->GetVideoInfoTag()->m_strPath,"");
120               db.Close();
121             }
122           }
123           else
124             m_bRefreshAll = false;
125
126           if (bCanceled)
127             return false;
128         }
129         m_bRefresh = true;
130         Close();
131         return true;
132       }
133       else if (iControl == CONTROL_BTN_TRACKS)
134       {
135         m_bViewReview = !m_bViewReview;
136         Update();
137       }
138       else if (iControl == CONTROL_BTN_PLAY)
139       {
140         Play();
141       }
142       else if (iControl == CONTROL_BTN_RESUME)
143       {
144         Play(true);
145       }
146       else if (iControl == CONTROL_BTN_GET_THUMB)
147       {
148         OnGetArt();
149       }
150       else if (iControl == CONTROL_BTN_PLAY_TRAILER)
151       {
152         PlayTrailer();
153       }
154       else if (iControl == CONTROL_BTN_GET_FANART)
155       {
156         OnGetFanart();
157       }
158       else if (iControl == CONTROL_BTN_DIRECTOR)
159       {
160         CStdString strDirector = StringUtils::Join(m_movieItem->GetVideoInfoTag()->m_director, g_advancedSettings.m_videoItemSeparator);
161         OnSearch(strDirector);
162       }
163       else if (iControl == CONTROL_LIST)
164       {
165         int iAction = message.GetParam1();
166         if (ACTION_SELECT_ITEM == iAction || ACTION_MOUSE_LEFT_CLICK == iAction)
167         {
168           CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl);
169           OnMessage(msg);
170           int iItem = msg.GetParam1();
171           if (iItem < 0 || iItem >= m_castList->Size())
172             break;
173           CStdString strItem = m_castList->Get(iItem)->GetLabel();
174           CStdString strFind = StringUtils::Format(" %s ",g_localizeStrings.Get(20347).c_str());
175           size_t iPos = strItem.find(strFind);
176           if (iPos == std::string::npos)
177             iPos = strItem.size();
178           CStdString tmp = strItem.substr(0, iPos);
179           OnSearch(tmp);
180         }
181       }
182     }
183     break;
184   case GUI_MSG_NOTIFY_ALL:
185     {
186       if (IsActive() && message.GetParam1() == GUI_MSG_UPDATE_ITEM && message.GetItem())
187       {
188         CFileItemPtr item = boost::static_pointer_cast<CFileItem>(message.GetItem());
189         if (item && m_movieItem->GetPath().Equals(item->GetPath()))
190         { // Just copy over the stream details and the thumb if we don't already have one
191           if (!m_movieItem->HasArt("thumb"))
192             m_movieItem->SetArt("thumb", item->GetArt("thumb"));
193           m_movieItem->GetVideoInfoTag()->m_streamDetails = item->GetVideoInfoTag()->m_streamDetails;
194         }
195         return true;
196       }
197     }
198   }
199
200   return CGUIDialog::OnMessage(message);
201 }
202
203 void CGUIDialogVideoInfo::OnInitWindow()
204 {
205   m_bRefresh = false;
206   m_bRefreshAll = true;
207   m_hasUpdatedThumb = false;
208   m_bViewReview = true;
209
210   CVideoDatabase database;
211   ADDON::ScraperPtr scraper;
212
213   if(database.Open())
214   {
215     scraper = database.GetScraperForPath(m_movieItem->GetVideoInfoTag()->GetPath());
216     database.Close();
217   }
218
219   CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_REFRESH, (CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser) && !StringUtils::StartsWithNoCase(m_movieItem->GetVideoInfoTag()->m_strIMDBNumber, "xx") && scraper);
220   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"));
221
222   VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)m_movieItem->GetVideoContentType();
223   if (type == VIDEODB_CONTENT_TVSHOWS || type == VIDEODB_CONTENT_MOVIES)
224     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"));
225   else
226     CONTROL_DISABLE(CONTROL_BTN_GET_FANART);
227
228   Update();
229
230   CGUIDialog::OnInitWindow();
231 }
232
233 bool CGUIDialogVideoInfo::OnAction(const CAction &action)
234 {
235   if (action.GetID() == ACTION_SHOW_INFO)
236   {
237     Close();
238     return true;
239   }
240   return CGUIDialog::OnAction(action);
241 }
242
243 void CGUIDialogVideoInfo::SetMovie(const CFileItem *item)
244 {
245   *m_movieItem = *item;
246   // setup cast list + determine type.  We need to do this here as it makes
247   // sure that content type (among other things) is set correctly for the
248   // old fixed id labels that we have floating around (they may be using
249   // content type to determine visibility, so we'll set the wrong label)
250   ClearCastList();
251   VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)m_movieItem->GetVideoContentType();
252   if (type == VIDEODB_CONTENT_MUSICVIDEOS)
253   { // music video
254     CMusicDatabase database;
255     database.Open();
256     const std::vector<std::string> &artists = m_movieItem->GetVideoInfoTag()->m_artist;
257     for (std::vector<std::string>::const_iterator it = artists.begin(); it != artists.end(); ++it)
258     {
259       int idArtist = database.GetArtistByName(*it);
260       CStdString thumb = database.GetArtForItem(idArtist, "artist", "thumb");
261       CFileItemPtr item(new CFileItem(*it));
262       if (!thumb.empty())
263         item->SetArt("thumb", thumb);
264       item->SetIconImage("DefaultArtist.png");
265       m_castList->Add(item);
266     }
267     m_castList->SetContent("musicvideos");
268   }
269   else
270   { // movie/show/episode
271     for (CVideoInfoTag::iCast it = m_movieItem->GetVideoInfoTag()->m_cast.begin(); it != m_movieItem->GetVideoInfoTag()->m_cast.end(); ++it)
272     {
273       CStdString character;
274       if (it->strRole.empty())
275         character = it->strName;
276       else
277         character = StringUtils::Format("%s %s %s", it->strName.c_str(), g_localizeStrings.Get(20347).c_str(), it->strRole.c_str());
278       CFileItemPtr item(new CFileItem(it->strName));
279       if (!it->thumb.empty())
280         item->SetArt("thumb", it->thumb);
281       else if (CSettings::Get().GetBool("videolibrary.actorthumbs"))
282       { // backward compatibility
283         CStdString thumb = CScraperUrl::GetThumbURL(it->thumbUrl.GetFirstThumb());
284         if (!thumb.empty())
285         {
286           item->SetArt("thumb", thumb);
287           CTextureCache::Get().BackgroundCacheImage(thumb);
288         }
289       }
290       item->SetIconImage("DefaultActor.png");
291       item->SetLabel(character);
292       m_castList->Add(item);
293     }
294     // determine type:
295     if (type == VIDEODB_CONTENT_TVSHOWS)
296     {
297       m_castList->SetContent("tvshows");
298       // special case stuff for shows (not currently retrieved from the library in filemode (ref: GetTvShowInfo vs GetTVShowsByWhere)
299       m_movieItem->m_dateTime = m_movieItem->GetVideoInfoTag()->m_premiered;
300       if(m_movieItem->GetVideoInfoTag()->m_iYear == 0 && m_movieItem->m_dateTime.IsValid())
301         m_movieItem->GetVideoInfoTag()->m_iYear = m_movieItem->m_dateTime.GetYear();
302       m_movieItem->SetProperty("totalepisodes", m_movieItem->GetVideoInfoTag()->m_iEpisode);
303       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
304       m_movieItem->SetProperty("watchedepisodes", m_movieItem->GetVideoInfoTag()->m_playCount);
305       m_movieItem->SetProperty("unwatchedepisodes", m_movieItem->GetVideoInfoTag()->m_iEpisode - m_movieItem->GetVideoInfoTag()->m_playCount);
306       m_movieItem->GetVideoInfoTag()->m_playCount = (m_movieItem->GetVideoInfoTag()->m_iEpisode == m_movieItem->GetVideoInfoTag()->m_playCount) ? 1 : 0;
307     }
308     else if (type == VIDEODB_CONTENT_EPISODES)
309     {
310       m_castList->SetContent("episodes");
311       // special case stuff for episodes (not currently retrieved from the library in filemode (ref: GetEpisodeInfo vs GetEpisodesByWhere)
312       m_movieItem->m_dateTime = m_movieItem->GetVideoInfoTag()->m_firstAired;
313       if(m_movieItem->GetVideoInfoTag()->m_iYear == 0 && m_movieItem->m_dateTime.IsValid())
314         m_movieItem->GetVideoInfoTag()->m_iYear = m_movieItem->m_dateTime.GetYear();
315       // retrieve the season thumb.
316       // TODO: should we use the thumbloader for this?
317       CVideoDatabase db;
318       if (db.Open())
319       {
320         if (m_movieItem->GetVideoInfoTag()->m_iSeason > -1)
321         {
322           int seasonID = m_movieItem->GetVideoInfoTag()->m_iIdSeason;
323           if (seasonID < 0)
324             seasonID = db.GetSeasonId(m_movieItem->GetVideoInfoTag()->m_iIdShow,
325                                       m_movieItem->GetVideoInfoTag()->m_iSeason);
326           CGUIListItem::ArtMap thumbs;
327           if (db.GetArtForItem(seasonID, "season", thumbs))
328           {
329             for (CGUIListItem::ArtMap::iterator i = thumbs.begin(); i != thumbs.end(); i++)
330               m_movieItem->SetArt("season." + i->first, i->second);
331           }
332         }
333         db.Close();
334       }
335     }
336     else if (type == VIDEODB_CONTENT_MOVIES)
337     {
338       m_castList->SetContent("movies");
339
340       // local trailers should always override non-local, so check 
341       // for a local one if the registered trailer is online
342       if (m_movieItem->GetVideoInfoTag()->m_strTrailer.empty() ||
343           URIUtils::IsInternetStream(m_movieItem->GetVideoInfoTag()->m_strTrailer))
344       {
345         CStdString localTrailer = m_movieItem->FindTrailer();
346         if (!localTrailer.empty())
347         {
348           m_movieItem->GetVideoInfoTag()->m_strTrailer = localTrailer;
349           CVideoDatabase database;
350           if(database.Open())
351           {
352             database.SetSingleValue(VIDEODB_CONTENT_MOVIES, VIDEODB_ID_TRAILER,
353                                     m_movieItem->GetVideoInfoTag()->m_iDbId,
354                                     m_movieItem->GetVideoInfoTag()->m_strTrailer);
355             database.Close();
356             CUtil::DeleteVideoDatabaseDirectoryCache();
357           }
358         }
359       }
360     }
361   }
362   CVideoThumbLoader loader;
363   loader.LoadItem(m_movieItem.get());
364 }
365
366 void CGUIDialogVideoInfo::Update()
367 {
368   // setup plot text area
369   CStdString strTmp = m_movieItem->GetVideoInfoTag()->m_strPlot;
370   if (!(!m_movieItem->GetVideoInfoTag()->m_strShowTitle.empty() && m_movieItem->GetVideoInfoTag()->m_iSeason == 0)) // dont apply to tvshows
371     if (m_movieItem->GetVideoInfoTag()->m_playCount == 0 && !CSettings::Get().GetBool("videolibrary.showunwatchedplots"))
372       strTmp = g_localizeStrings.Get(20370);
373
374   StringUtils::Trim(strTmp);
375   SetLabel(CONTROL_TEXTAREA, strTmp);
376
377   CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST, 0, 0, m_castList);
378   OnMessage(msg);
379
380   if (GetControl(CONTROL_BTN_TRACKS)) // if no CONTROL_BTN_TRACKS found - allow skinner full visibility control over CONTROL_TEXTAREA and CONTROL_LIST
381   {
382     if (m_bViewReview)
383     {
384       if (!m_movieItem->GetVideoInfoTag()->m_artist.empty())
385       {
386         SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 133);
387       }
388       else
389       {
390         SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 206);
391       }
392
393       SET_CONTROL_HIDDEN(CONTROL_LIST);
394       SET_CONTROL_VISIBLE(CONTROL_TEXTAREA);
395     }
396     else
397     {
398       SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 207);
399
400       SET_CONTROL_HIDDEN(CONTROL_TEXTAREA);
401       SET_CONTROL_VISIBLE(CONTROL_LIST);
402     }
403   }
404
405   // Check for resumability
406   if (m_movieItem->GetVideoInfoTag()->m_resumePoint.timeInSeconds > 0.0)
407     CONTROL_ENABLE(CONTROL_BTN_RESUME);
408   else
409     CONTROL_DISABLE(CONTROL_BTN_RESUME);
410
411   CONTROL_ENABLE(CONTROL_BTN_PLAY);
412
413   // update the thumbnail
414   const CGUIControl* pControl = GetControl(CONTROL_IMAGE);
415   if (pControl)
416   {
417     CGUIImage* pImageControl = (CGUIImage*)pControl;
418     pImageControl->FreeResources();
419     pImageControl->SetFileName(m_movieItem->GetArt("thumb"));
420   }
421   // tell our GUI to completely reload all controls (as some of them
422   // are likely to have had this image in use so will need refreshing)
423   if (m_hasUpdatedThumb)
424   {
425     CGUIMessage reload(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
426     g_windowManager.SendMessage(reload);
427   }
428 }
429
430 bool CGUIDialogVideoInfo::NeedRefresh() const
431 {
432   return m_bRefresh;
433 }
434
435 bool CGUIDialogVideoInfo::RefreshAll() const
436 {
437   return m_bRefreshAll;
438 }
439
440 /// \brief Search the current directory for a string got from the virtual keyboard
441 void CGUIDialogVideoInfo::OnSearch(CStdString& strSearch)
442 {
443   CGUIDialogProgress *progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
444   if (progress)
445   {
446     progress->SetHeading(194);
447     progress->SetLine(0, strSearch);
448     progress->SetLine(1, "");
449     progress->SetLine(2, "");
450     progress->StartModal();
451     progress->Progress();
452   }
453   CFileItemList items;
454   DoSearch(strSearch, items);
455
456   if (progress)
457     progress->Close();
458
459   if (items.Size())
460   {
461     CGUIDialogSelect* pDlgSelect = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
462     pDlgSelect->Reset();
463     pDlgSelect->SetHeading(283);
464
465     for (int i = 0; i < (int)items.Size(); i++)
466     {
467       CFileItemPtr pItem = items[i];
468       pDlgSelect->Add(pItem->GetLabel());
469     }
470
471     pDlgSelect->DoModal();
472
473     int iItem = pDlgSelect->GetSelectedLabel();
474     if (iItem < 0)
475       return;
476
477     CFileItem* pSelItem = new CFileItem(*items[iItem]);
478
479     OnSearchItemFound(pSelItem);
480
481     delete pSelItem;
482   }
483   else
484   {
485     CGUIDialogOK::ShowAndGetInput(194, 284, 0, 0);
486   }
487 }
488
489 /// \brief Make the actual search for the OnSearch function.
490 /// \param strSearch The search string
491 /// \param items Items Found
492 void CGUIDialogVideoInfo::DoSearch(CStdString& strSearch, CFileItemList& items)
493 {
494   CVideoDatabase db;
495   if (!db.Open())
496     return;
497
498   CFileItemList movies;
499   db.GetMoviesByActor(strSearch, movies);
500   for (int i = 0; i < movies.Size(); ++i)
501   {
502     CStdString label = movies[i]->GetVideoInfoTag()->m_strTitle;
503     if (movies[i]->GetVideoInfoTag()->m_iYear > 0)
504       label += StringUtils::Format(" (%i)", movies[i]->GetVideoInfoTag()->m_iYear);
505     movies[i]->SetLabel(label);
506   }
507   CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20338) + "] ", items);
508
509   db.GetTvShowsByActor(strSearch, movies);
510   for (int i = 0; i < movies.Size(); ++i)
511   {
512     CStdString label = movies[i]->GetVideoInfoTag()->m_strShowTitle;
513     if (movies[i]->GetVideoInfoTag()->m_iYear > 0)
514       label += StringUtils::Format(" (%i)", movies[i]->GetVideoInfoTag()->m_iYear);
515     movies[i]->SetLabel(label);
516   }
517   CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20364) + "] ", items);
518
519   db.GetEpisodesByActor(strSearch, movies);
520   for (int i = 0; i < movies.Size(); ++i)
521   {
522     CStdString label = movies[i]->GetVideoInfoTag()->m_strTitle + " (" +  movies[i]->GetVideoInfoTag()->m_strShowTitle + ")";
523     movies[i]->SetLabel(label);
524   }
525   CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20359) + "] ", items);
526
527   db.GetMusicVideosByArtist(strSearch, movies);
528   for (int i = 0; i < movies.Size(); ++i)
529   {
530     CStdString label = StringUtils::Join(movies[i]->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator) + " - " + movies[i]->GetVideoInfoTag()->m_strTitle;
531     if (movies[i]->GetVideoInfoTag()->m_iYear > 0)
532       label += StringUtils::Format(" (%i)", movies[i]->GetVideoInfoTag()->m_iYear);
533     movies[i]->SetLabel(label);
534   }
535   CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20391) + "] ", items);
536   db.Close();
537 }
538
539 /// \brief React on the selected search item
540 /// \param pItem Search result item
541 void CGUIDialogVideoInfo::OnSearchItemFound(const CFileItem* pItem)
542 {
543   VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)pItem->GetVideoContentType();
544
545   CVideoDatabase db;
546   if (!db.Open())
547     return;
548
549   CVideoInfoTag movieDetails;
550   if (type == VIDEODB_CONTENT_MOVIES)
551     db.GetMovieInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
552   if (type == VIDEODB_CONTENT_EPISODES)
553     db.GetEpisodeInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
554   if (type == VIDEODB_CONTENT_TVSHOWS)
555     db.GetTvShowInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
556   if (type == VIDEODB_CONTENT_MUSICVIDEOS)
557     db.GetMusicVideoInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
558   db.Close();
559
560   CFileItem item(*pItem);
561   *item.GetVideoInfoTag() = movieDetails;
562   SetMovie(&item);
563   // refresh our window entirely
564   Close();
565   DoModal();
566 }
567
568 void CGUIDialogVideoInfo::ClearCastList()
569 {
570   CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_LIST);
571   OnMessage(msg);
572   m_castList->Clear();
573 }
574
575 void CGUIDialogVideoInfo::Play(bool resume)
576 {
577   if (!m_movieItem->GetVideoInfoTag()->m_strEpisodeGuide.empty())
578   {
579     CStdString strPath = StringUtils::Format("videodb://tvshows/titles/%i/",m_movieItem->GetVideoInfoTag()->m_iDbId);
580     Close();
581     g_windowManager.ActivateWindow(WINDOW_VIDEO_NAV,strPath);
582     return;
583   }
584
585   CFileItem movie(*m_movieItem->GetVideoInfoTag());
586   if (m_movieItem->GetVideoInfoTag()->m_strFileNameAndPath.empty())
587     movie.SetPath(m_movieItem->GetPath());
588   CGUIWindowVideoNav* pWindow = (CGUIWindowVideoNav*)g_windowManager.GetWindow(WINDOW_VIDEO_NAV);
589   if (pWindow)
590   {
591     // close our dialog
592     Close(true);
593     if (resume)
594       movie.m_lStartOffset = STARTOFFSET_RESUME;
595     else if (!CGUIWindowVideoBase::ShowResumeMenu(movie)) 
596     {
597       // The Resume dialog was closed without any choice
598       DoModal();
599       return;
600     }
601     pWindow->PlayMovie(&movie);
602   }
603 }
604
605 string CGUIDialogVideoInfo::ChooseArtType(const CFileItem &videoItem, map<string, string> &currentArt)
606 {
607   // prompt for choice
608   CGUIDialogSelect *dialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
609   if (!dialog || !videoItem.HasVideoInfoTag())
610     return "";
611
612   CFileItemList items;
613   dialog->SetHeading(13511);
614   dialog->Reset();
615   dialog->SetUseDetails(true);
616   dialog->EnableButton(true, 13516);
617
618   CVideoDatabase db;
619   db.Open();
620
621   vector<string> artTypes = CVideoThumbLoader::GetArtTypes(videoItem.GetVideoInfoTag()->m_type);
622
623   // add in any stored art for this item that is non-empty.
624   db.GetArtForItem(videoItem.GetVideoInfoTag()->m_iDbId, videoItem.GetVideoInfoTag()->m_type, currentArt);
625   for (CGUIListItem::ArtMap::iterator i = currentArt.begin(); i != currentArt.end(); ++i)
626   {
627     if (!i->second.empty() && find(artTypes.begin(), artTypes.end(), i->first) == artTypes.end())
628       artTypes.push_back(i->first);
629   }
630
631   // add any art types that exist for other media items of the same type
632   vector<string> dbArtTypes;
633   db.GetArtTypes(videoItem.GetVideoInfoTag()->m_type, dbArtTypes);
634   for (vector<string>::const_iterator it = dbArtTypes.begin(); it != dbArtTypes.end(); it++)
635   {
636     if (find(artTypes.begin(), artTypes.end(), *it) == artTypes.end())
637       artTypes.push_back(*it);
638   }
639
640   for (vector<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
641   {
642     string type = *i;
643     CFileItemPtr item(new CFileItem(type, "false"));
644     item->SetLabel(type);
645     if (videoItem.HasArt(type))
646       item->SetArt("thumb", videoItem.GetArt(type));
647     items.Add(item);
648   }
649
650   dialog->SetItems(&items);
651   dialog->DoModal();
652
653   if (dialog->IsButtonPressed())
654   {
655     // Get the new artwork name
656     CStdString strArtworkName;
657     if (!CGUIKeyboardFactory::ShowAndGetInput(strArtworkName, g_localizeStrings.Get(13516), false))
658       return "";
659
660     return strArtworkName;
661   }
662
663   return dialog->GetSelectedItem()->GetLabel();
664 }
665
666 void CGUIDialogVideoInfo::OnGetArt()
667 {
668   map<string, string> currentArt;
669   string type = ChooseArtType(*m_movieItem, currentArt);
670   if (type.empty())
671     return; // cancelled
672
673   // TODO: this can be removed once these are unified.
674   if (type == "fanart")
675     OnGetFanart();
676   else
677   {
678     CFileItemList items;
679
680     // Current thumb
681     if (m_movieItem->HasArt(type))
682     {
683       CFileItemPtr item(new CFileItem("thumb://Current", false));
684       item->SetArt("thumb", m_movieItem->GetArt(type));
685       item->SetLabel(g_localizeStrings.Get(13512));
686       items.Add(item);
687     }
688     else if ((type == "poster" || type == "banner") && currentArt.find("thumb") != currentArt.end())
689     { // add the 'thumb' type in
690       CFileItemPtr item(new CFileItem("thumb://Thumb", false));
691       item->SetArt("thumb", currentArt["thumb"]);
692       item->SetLabel(g_localizeStrings.Get(13512));
693       items.Add(item);
694     }
695
696     // Grab the thumbnails from the web
697     vector<CStdString> thumbs;
698     int season = (m_movieItem->GetVideoInfoTag()->m_type == "season") ? m_movieItem->GetVideoInfoTag()->m_iSeason : -1;
699     m_movieItem->GetVideoInfoTag()->m_strPictureURL.GetThumbURLs(thumbs, type, season);
700
701     for (unsigned int i = 0; i < thumbs.size(); ++i)
702     {
703       CStdString strItemPath = StringUtils::Format("thumb://Remote%i", i);
704       CFileItemPtr item(new CFileItem(strItemPath, false));
705       item->SetArt("thumb", thumbs[i]);
706       item->SetIconImage("DefaultPicture.png");
707       item->SetLabel(g_localizeStrings.Get(13513));
708
709       // TODO: Do we need to clear the cached image?
710       //    CTextureCache::Get().ClearCachedImage(thumb);
711       items.Add(item);
712     }
713
714     CStdString localThumb = CVideoThumbLoader::GetLocalArt(*m_movieItem, type);
715     if (!localThumb.empty())
716     {
717       CFileItemPtr item(new CFileItem("thumb://Local", false));
718       item->SetArt("thumb", localThumb);
719       item->SetLabel(g_localizeStrings.Get(13514));
720       items.Add(item);
721     }
722     else
723     { // no local thumb exists, so we are just using the IMDb thumb or cached thumb
724       // which is probably the IMDb thumb.  These could be wrong, so allow the user
725       // to delete the incorrect thumb
726       CFileItemPtr item(new CFileItem("thumb://None", false));
727       item->SetIconImage("DefaultVideo.png");
728       item->SetLabel(g_localizeStrings.Get(13515));
729       items.Add(item);
730     }
731
732     CStdString result;
733     VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
734     AddItemPathToFileBrowserSources(sources, *m_movieItem);
735     g_mediaManager.GetLocalDrives(sources);
736     if (CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result) &&
737         result != "thumb://Current") // user didn't choose the one they have
738     {
739       CStdString newThumb;
740       if (StringUtils::StartsWith(result, "thumb://Remote"))
741       {
742         int number = atoi(result.substr(14).c_str());
743         newThumb = thumbs[number];
744       }
745       else if (result == "thumb://Thumb")
746         newThumb = currentArt["thumb"];
747       else if (result == "thumb://Local")
748         newThumb = localThumb;
749       else if (CFile::Exists(result))
750         newThumb = result;
751       else // none
752         newThumb.clear();
753
754       // update thumb in the database
755       CVideoDatabase db;
756       if (db.Open())
757       {
758         db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, type, newThumb);
759         db.Close();
760       }
761       CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
762       m_movieItem->SetArt(type, newThumb);
763       if (m_movieItem->HasProperty("set_folder_thumb"))
764       { // have a folder thumb to set as well
765         VIDEO::CVideoInfoScanner::ApplyThumbToFolder(m_movieItem->GetProperty("set_folder_thumb").asString(), newThumb);
766       }
767       m_hasUpdatedThumb = true;
768     }
769   }
770
771   // Update our screen
772   Update();
773
774   // re-open the art selection dialog as we come back from
775   // the image selection dialog
776   OnGetArt();
777 }
778
779 // Allow user to select a Fanart
780 void CGUIDialogVideoInfo::OnGetFanart()
781 {
782   CFileItemList items;
783
784   CFileItem item(*m_movieItem->GetVideoInfoTag());
785   if (item.HasArt("fanart"))
786   {
787     CFileItemPtr itemCurrent(new CFileItem("fanart://Current",false));
788     itemCurrent->SetArt("thumb", item.GetArt("fanart"));
789     itemCurrent->SetLabel(g_localizeStrings.Get(20440));
790     items.Add(itemCurrent);
791   }
792
793   // ensure the fanart is unpacked
794   m_movieItem->GetVideoInfoTag()->m_fanart.Unpack();
795
796   // Grab the thumbnails from the web
797   for (unsigned int i = 0; i < m_movieItem->GetVideoInfoTag()->m_fanart.GetNumFanarts(); i++)
798   {
799     CStdString strItemPath = StringUtils::Format("fanart://Remote%i",i);
800     CFileItemPtr item(new CFileItem(strItemPath, false));
801     CStdString thumb = m_movieItem->GetVideoInfoTag()->m_fanart.GetPreviewURL(i);
802     item->SetArt("thumb", CTextureUtils::GetWrappedThumbURL(thumb));
803     item->SetIconImage("DefaultPicture.png");
804     item->SetLabel(g_localizeStrings.Get(20441));
805
806     // TODO: Do we need to clear the cached image?
807 //    CTextureCache::Get().ClearCachedImage(thumb);
808     items.Add(item);
809   }
810
811   CStdString strLocal = item.GetLocalFanart();
812   if (!strLocal.empty())
813   {
814     CFileItemPtr itemLocal(new CFileItem("fanart://Local",false));
815     itemLocal->SetArt("thumb", strLocal);
816     itemLocal->SetLabel(g_localizeStrings.Get(20438));
817
818     // TODO: Do we need to clear the cached image?
819     CTextureCache::Get().ClearCachedImage(strLocal);
820     items.Add(itemLocal);
821   }
822   else
823   {
824     CFileItemPtr itemNone(new CFileItem("fanart://None", false));
825     itemNone->SetIconImage("DefaultVideo.png");
826     itemNone->SetLabel(g_localizeStrings.Get(20439));
827     items.Add(itemNone);
828   }
829
830   CStdString result;
831   VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
832   AddItemPathToFileBrowserSources(sources, item);
833   g_mediaManager.GetLocalDrives(sources);
834   bool flip=false;
835   if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) || result.Equals("fanart://Current"))
836     return;   // user cancelled
837
838   if (result.Equals("fanart://Local"))
839     result = strLocal;
840
841   if (StringUtils::StartsWith(result, "fanart://Remote"))
842   {
843     int iFanart = atoi(result.substr(15).c_str());
844     // set new primary fanart, and update our database accordingly
845     m_movieItem->GetVideoInfoTag()->m_fanart.SetPrimaryFanart(iFanart);
846     CVideoDatabase db;
847     if (db.Open())
848     {
849       db.UpdateFanart(*m_movieItem, (VIDEODB_CONTENT_TYPE)m_movieItem->GetVideoContentType());
850       db.Close();
851     }
852     result = m_movieItem->GetVideoInfoTag()->m_fanart.GetImageURL();
853   }
854   else if (result.Equals("fanart://None") || !CFile::Exists(result))
855     result.clear();
856
857   // set the fanart image
858   if (flip && !result.empty())
859     result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
860   CVideoDatabase db;
861   if (db.Open())
862   {
863     db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, "fanart", result);
864     db.Close();
865   }
866
867   CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
868   m_movieItem->SetArt("fanart", result);
869   m_hasUpdatedThumb = true;
870
871   // Update our screen
872   Update();
873 }
874
875 void CGUIDialogVideoInfo::PlayTrailer()
876 {
877   CFileItem item;
878   item.SetPath(m_movieItem->GetVideoInfoTag()->m_strTrailer);
879   *item.GetVideoInfoTag() = *m_movieItem->GetVideoInfoTag();
880   item.GetVideoInfoTag()->m_streamDetails.Reset();
881   item.GetVideoInfoTag()->m_strTitle = StringUtils::Format("%s (%s)",
882                                                            m_movieItem->GetVideoInfoTag()->m_strTitle.c_str(),
883                                                            g_localizeStrings.Get(20410).c_str());
884   CVideoThumbLoader::SetArt(item, m_movieItem->GetArt());
885   item.GetVideoInfoTag()->m_iDbId = -1;
886   item.GetVideoInfoTag()->m_iFileId = -1;
887
888   // Close the dialog.
889   Close(true);
890
891   if (item.IsPlayList())
892     CApplicationMessenger::Get().MediaPlay(item);
893   else
894     CApplicationMessenger::Get().PlayFile(item);
895 }
896
897 void CGUIDialogVideoInfo::SetLabel(int iControl, const CStdString &strLabel)
898 {
899   if (strLabel.empty())
900   {
901     SET_CONTROL_LABEL(iControl, 416);  // "Not available"
902   }
903   else
904   {
905     SET_CONTROL_LABEL(iControl, strLabel);
906   }
907 }
908
909 std::string CGUIDialogVideoInfo::GetThumbnail() const
910 {
911   return m_movieItem->GetArt("thumb");
912 }
913
914 void CGUIDialogVideoInfo::AddItemPathToFileBrowserSources(VECSOURCES &sources, const CFileItem &item)
915 {
916   if (!item.HasVideoInfoTag())
917     return;
918
919   CStdString itemDir = item.GetVideoInfoTag()->m_basePath;
920
921   //season
922   if (itemDir.empty())
923     itemDir = item.GetVideoInfoTag()->GetPath();
924
925   CFileItem itemTmp(itemDir, false);
926   if (itemTmp.IsVideo())
927     itemDir = URIUtils::GetParentPath(itemDir);
928
929   if (!itemDir.empty() && CDirectory::Exists(itemDir))
930   {
931     CMediaSource itemSource;
932     itemSource.strName = g_localizeStrings.Get(36041);
933     itemSource.strPath = itemDir;
934     sources.push_back(itemSource);
935   }
936 }
937
938 int CGUIDialogVideoInfo::ManageVideoItem(const CFileItemPtr &item)
939 {
940   if (item == NULL || !item->IsVideoDb() || !item->HasVideoInfoTag() || item->GetVideoInfoTag()->m_iDbId < 0)
941     return -1;
942
943   CVideoDatabase database;
944   if (!database.Open())
945     return -1;
946
947   const std::string &type = item->GetVideoInfoTag()->m_type;
948   int dbId = item->GetVideoInfoTag()->m_iDbId;
949
950   CContextButtons buttons;
951   buttons.Add(CONTEXT_BUTTON_EDIT, 16105);
952
953   if (type == "movie" || type == "tvshow")
954     buttons.Add(CONTEXT_BUTTON_EDIT_SORTTITLE, 16107);
955
956   if (item->m_bIsFolder)
957   {
958     // Have both options for folders since we don't know whether all childs are watched/unwatched
959     buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
960     buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103);   //Mark as Watched
961   }
962   else
963   {
964     if (item->GetOverlayImage().Equals("OverlayWatched.png"))
965       buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
966     else
967       buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103);   //Mark as Watched
968   }
969
970   if (type == "movie")
971   {
972     // only show link/unlink if there are tvshows available
973     if (database.HasContent(VIDEODB_CONTENT_TVSHOWS))
974     {
975       buttons.Add(CONTEXT_BUTTON_LINK_MOVIE, 20384);
976       if (database.IsLinkedToTvshow(dbId))
977         buttons.Add(CONTEXT_BUTTON_UNLINK_MOVIE, 20385);
978     }
979
980     // set or change movie set the movie belongs to
981     buttons.Add(CONTEXT_BUTTON_SET_MOVIESET, 20465);
982   }
983
984   if (type == "episode" &&
985       item->GetVideoInfoTag()->m_iBookmarkId > 0)
986     buttons.Add(CONTEXT_BUTTON_UNLINK_BOOKMARK, 20405);
987
988   if (!item->m_bIsFolder || type == VIDEODB_CONTENT_TVSHOWS)
989     buttons.Add(CONTEXT_BUTTON_DELETE, 646);
990
991   bool result = false;
992   int button = CGUIDialogContextMenu::ShowAndGetChoice(buttons);
993   if (button >= 0)
994   {
995     switch ((CONTEXT_BUTTON)button)
996     {
997       case CONTEXT_BUTTON_EDIT:
998         result = UpdateVideoItemTitle(item);
999         break;
1000
1001       case CONTEXT_BUTTON_EDIT_SORTTITLE:
1002         result = UpdateVideoItemSortTitle(item);
1003         break;
1004
1005       case CONTEXT_BUTTON_MARK_WATCHED:
1006         result = MarkWatched(item, true);
1007         break;
1008
1009       case CONTEXT_BUTTON_MARK_UNWATCHED:
1010         result = MarkWatched(item, false);
1011         break;
1012
1013       case CONTEXT_BUTTON_LINK_MOVIE:
1014         result = LinkMovieToTvShow(item, false, database);
1015         break;
1016
1017       case CONTEXT_BUTTON_UNLINK_MOVIE:
1018         result = LinkMovieToTvShow(item, true, database);
1019         break;
1020
1021       case CONTEXT_BUTTON_SET_MOVIESET:
1022       {
1023         CFileItemPtr selectedSet;
1024         if (GetSetForMovie(item.get(), selectedSet))
1025           result = SetMovieSet(item.get(), selectedSet.get());
1026         break;
1027       }
1028
1029       case CONTEXT_BUTTON_UNLINK_BOOKMARK:
1030         database.DeleteBookMarkForEpisode(*item->GetVideoInfoTag());
1031         result = true;
1032         break;
1033
1034       case CONTEXT_BUTTON_DELETE:
1035         result = DeleteVideoItem(item);
1036         break;
1037
1038       default:
1039         result = false;
1040         break;
1041     }
1042   }
1043
1044   database.Close();
1045
1046   if (result)
1047     return button;
1048
1049   return -1;
1050 }
1051
1052 //Add change a title's name
1053 bool CGUIDialogVideoInfo::UpdateVideoItemTitle(const CFileItemPtr &pItem)
1054 {
1055   if (!pItem->HasVideoInfoTag())
1056     return false;
1057
1058   // dont allow update while scanning
1059   if (g_application.IsVideoScanning())
1060   {
1061     CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1062     return false;
1063   }
1064
1065   CVideoDatabase database;
1066   if (!database.Open())
1067     return false;
1068
1069   int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
1070   CVideoInfoTag detail;
1071   VIDEODB_CONTENT_TYPE iType = (VIDEODB_CONTENT_TYPE)pItem->GetVideoContentType();
1072   if (iType == VIDEODB_CONTENT_MOVIES)
1073     database.GetMovieInfo("", detail, iDbId);
1074   else if (iType == VIDEODB_CONTENT_MOVIE_SETS)
1075     database.GetSetInfo(iDbId, detail);
1076   else if (iType == VIDEODB_CONTENT_EPISODES)
1077     database.GetEpisodeInfo(pItem->GetPath(), detail, iDbId);
1078   else if (iType == VIDEODB_CONTENT_TVSHOWS)
1079     database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId);
1080   else if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
1081     database.GetMusicVideoInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId);
1082
1083   // get the new title
1084   if (!CGUIKeyboardFactory::ShowAndGetInput(detail.m_strTitle, g_localizeStrings.Get(16105), false))
1085     return false;
1086
1087   database.UpdateMovieTitle(iDbId, detail.m_strTitle, iType);
1088   return true;
1089 }
1090
1091 bool CGUIDialogVideoInfo::MarkWatched(const CFileItemPtr &item, bool bMark)
1092 {
1093   if (!CProfilesManager::Get().GetCurrentProfile().canWriteDatabases())
1094     return false;
1095
1096   // dont allow update while scanning
1097   if (g_application.IsVideoScanning())
1098   {
1099     CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1100     return false;
1101   }
1102
1103   CVideoDatabase database;
1104   if (!database.Open())
1105     return false;
1106
1107   CFileItemList items;
1108   if (item->m_bIsFolder)
1109   {
1110     CStdString strPath = item->GetPath();
1111     CDirectory::GetDirectory(strPath, items);
1112   }
1113   else
1114     items.Add(item);
1115
1116   for (int i = 0; i < items.Size(); ++i)
1117   {
1118     CFileItemPtr pTmpItem = items[i];
1119     if (pTmpItem->m_bIsFolder)
1120     {
1121       MarkWatched(pTmpItem, bMark);
1122       continue;
1123     }
1124
1125     if (pTmpItem->HasVideoInfoTag() &&
1126        ((bMark && pTmpItem->GetVideoInfoTag()->m_playCount) ||
1127         (!bMark && !pTmpItem->GetVideoInfoTag()->m_playCount)))
1128       continue;
1129
1130 #ifdef HAS_UPNP
1131     if (!URIUtils::IsUPnP(item->GetPath()) || !UPNP::CUPnP::MarkWatched(*pTmpItem, bMark))
1132 #endif
1133     {
1134       // Clear resume bookmark
1135       if (bMark)
1136         database.ClearBookMarksOfFile(pTmpItem->GetPath(), CBookmark::RESUME);
1137
1138       database.SetPlayCount(*pTmpItem, bMark ? 1 : 0);
1139     }
1140   }
1141
1142   database.Close();
1143
1144   return true;
1145 }
1146
1147 bool CGUIDialogVideoInfo::CanDeleteVideoItem(const CFileItemPtr &item)
1148 {
1149   if (item == NULL || !item->HasVideoInfoTag())
1150     return false;
1151
1152   CQueryParams params;
1153   CVideoDatabaseDirectory::GetQueryParams(item->GetPath(), params);
1154
1155   return params.GetMovieId()   != -1 ||
1156          params.GetEpisodeId() != -1 ||
1157          params.GetMVideoId()  != -1 ||
1158          (params.GetTvShowId() != -1 && params.GetSeason() <= -1 &&
1159           !CVideoDatabaseDirectory::IsAllItem(item->GetPath()));
1160 }
1161
1162 bool CGUIDialogVideoInfo::DeleteVideoItemFromDatabase(const CFileItemPtr &item, bool unavailable /* = false */)
1163 {
1164   if (!item->HasVideoInfoTag() || !CanDeleteVideoItem(item))
1165     return false;
1166
1167   // dont allow update while scanning
1168   if (g_application.IsVideoScanning())
1169   {
1170     CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1171     return false;
1172   }
1173
1174   CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
1175   if (pDialog == NULL)
1176     return false;
1177   
1178   int heading = -1;
1179   VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)item->GetVideoContentType();
1180   switch (type)
1181   {
1182     case VIDEODB_CONTENT_MOVIES:
1183       heading = 432;
1184       break;
1185     case VIDEODB_CONTENT_EPISODES:
1186       heading = 20362;
1187       break;
1188     case VIDEODB_CONTENT_TVSHOWS:
1189       heading = 20363;
1190       break;
1191     case VIDEODB_CONTENT_MUSICVIDEOS:
1192       heading = 20392;
1193       break;
1194
1195     default:
1196       return false;
1197   }
1198
1199   pDialog->SetHeading(heading);
1200
1201   if (unavailable)
1202   {
1203     pDialog->SetLine(0, g_localizeStrings.Get(662));
1204     pDialog->SetLine(1, g_localizeStrings.Get(663));
1205   }
1206   else
1207   {
1208     pDialog->SetLine(0, StringUtils::Format(g_localizeStrings.Get(433), item->GetLabel().c_str()));
1209     pDialog->SetLine(1, "");
1210   }
1211   pDialog->SetLine(2, "");
1212   pDialog->DoModal();
1213
1214   if (!pDialog->IsConfirmed())
1215     return false;
1216
1217   CStdString path;
1218   CVideoDatabase database;
1219   database.Open();
1220
1221   database.GetFilePathById(item->GetVideoInfoTag()->m_iDbId, path, type);
1222   if (path.empty())
1223     return false;
1224
1225   switch (type)
1226   {
1227     case VIDEODB_CONTENT_MOVIES:
1228       database.DeleteMovie(path);
1229       break;
1230     case VIDEODB_CONTENT_EPISODES:
1231       database.DeleteEpisode(path, item->GetVideoInfoTag()->m_iDbId);
1232       break;
1233     case VIDEODB_CONTENT_TVSHOWS:
1234       database.DeleteTvShow(path);
1235       break;
1236     case VIDEODB_CONTENT_MUSICVIDEOS:
1237       database.DeleteMusicVideo(path);
1238       break;
1239   
1240     default:
1241       return false;
1242   }
1243
1244   if (type == VIDEODB_CONTENT_TVSHOWS)
1245     database.SetPathHash(path,"");
1246   else
1247     database.SetPathHash(URIUtils::GetDirectory(path), "");
1248
1249   return true;
1250 }
1251
1252 bool CGUIDialogVideoInfo::DeleteVideoItem(const CFileItemPtr &item, bool unavailable /* = false */)
1253 {
1254   // delete the video item from the database
1255   if (!DeleteVideoItemFromDatabase(item, unavailable))
1256     return false;
1257
1258   bool result = true;
1259   // check if the user is allowed to delete the actual file as well
1260   if ((CProfilesManager::Get().GetCurrentProfile().getLockMode() == LOCK_MODE_EVERYONE ||
1261        !CProfilesManager::Get().GetCurrentProfile().filesLocked() ||
1262        g_passwordManager.IsMasterLockUnlocked(true)) &&
1263       CSettings::Get().GetBool("filelists.allowfiledeletion"))
1264   {
1265     CStdString strDeletePath = item->GetVideoInfoTag()->GetPath();
1266
1267     if (URIUtils::GetFileName(strDeletePath).Equals("VIDEO_TS.IFO"))
1268     {
1269       strDeletePath = URIUtils::GetDirectory(strDeletePath);
1270       if (StringUtils::EndsWithNoCase(strDeletePath, "video_ts/"))
1271       {
1272         URIUtils::RemoveSlashAtEnd(strDeletePath);
1273         strDeletePath = URIUtils::GetDirectory(strDeletePath);
1274       }
1275     }
1276     if (URIUtils::HasSlashAtEnd(strDeletePath))
1277       item->m_bIsFolder = true;
1278
1279     // check if the file/directory can be deleted
1280     if (CUtil::SupportsWriteFileOperations(strDeletePath))
1281     {
1282       item->SetPath(strDeletePath);
1283
1284       // HACK: stacked files need to be treated as folders in order to be deleted
1285       if (item->IsStack())
1286         item->m_bIsFolder = true;
1287       result = CFileUtils::DeleteItem(item);
1288     }
1289   }
1290
1291   CUtil::DeleteVideoDatabaseDirectoryCache();
1292
1293   return result;
1294 }
1295
1296 bool CGUIDialogVideoInfo::GetMoviesForSet(const CFileItem *setItem, CFileItemList &originalMovies, CFileItemList &selectedMovies)
1297 {
1298   CVideoDatabase videodb;
1299   if (!videodb.Open())
1300     return false;
1301
1302   CStdString strHeading = StringUtils::Format(g_localizeStrings.Get(20457));
1303   CStdString baseDir = StringUtils::Format("videodb://movies/sets/%d", setItem->GetVideoInfoTag()->m_iDbId);
1304
1305   if (!CDirectory::GetDirectory(baseDir, originalMovies) || originalMovies.Size() <= 0) // keep a copy of the original members of the set
1306     return false;
1307
1308   CFileItemList listItems;
1309   if (!videodb.GetSortedVideos(MediaTypeMovie, "videodb://movies", SortDescription(), listItems) || listItems.Size() <= 0)
1310     return false;
1311
1312   CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1313   if (dialog == NULL)
1314     return false;
1315
1316   listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1317
1318   dialog->Reset();
1319   dialog->SetMultiSelection(true);
1320   dialog->SetHeading(strHeading);
1321   dialog->SetItems(&listItems);
1322   vector<int> selectedIndices;
1323   for (int i = 0; i < originalMovies.Size(); i++)
1324   {
1325     for (int listIndex = 0; listIndex < listItems.Size(); listIndex++)
1326     {
1327       if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == originalMovies[i]->GetVideoInfoTag()->m_iDbId)
1328       {
1329         selectedIndices.push_back(listIndex);
1330         break;
1331       }
1332     }
1333   }
1334   dialog->SetSelected(selectedIndices);
1335   dialog->EnableButton(true, 186);
1336   dialog->DoModal();
1337
1338   if (dialog->IsConfirmed())
1339   {
1340     selectedMovies.Copy(dialog->GetSelectedItems());
1341     return (selectedMovies.Size() > 0);
1342   }
1343   else
1344     return false;
1345 }
1346
1347 bool CGUIDialogVideoInfo::GetSetForMovie(const CFileItem *movieItem, CFileItemPtr &selectedSet)
1348 {
1349   CVideoDatabase videodb;
1350   if (!videodb.Open())
1351     return false;
1352
1353   CFileItemList listItems;
1354   CStdString baseDir = "videodb://movies/sets/";
1355   if (!CDirectory::GetDirectory(baseDir, listItems) || listItems.Size() <= 0)
1356     return false;
1357   listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1358
1359   int currentSetId = 0;
1360   CStdString currentSetLabel;
1361
1362   if (movieItem->GetVideoInfoTag()->m_iSetId > currentSetId)
1363   {
1364     currentSetId = movieItem->GetVideoInfoTag()->m_iSetId;
1365     currentSetLabel = videodb.GetSetById(currentSetId);
1366   }
1367
1368   if (currentSetId > 0)
1369   {
1370     // add clear item
1371     CStdString strClear = StringUtils::Format(g_localizeStrings.Get(20467), currentSetLabel.c_str());
1372     CFileItemPtr clearItem(new CFileItem(strClear));
1373     clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
1374     listItems.AddFront(clearItem, 0);
1375     // add keep current set item
1376     CStdString strKeep = StringUtils::Format(g_localizeStrings.Get(20469), currentSetLabel.c_str());
1377     CFileItemPtr keepItem(new CFileItem(strKeep));
1378     keepItem->GetVideoInfoTag()->m_iDbId = currentSetId;
1379     listItems.AddFront(keepItem, 1);
1380   }
1381
1382   CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1383   if (dialog == NULL)
1384     return false;
1385
1386   CStdString strHeading = StringUtils::Format(g_localizeStrings.Get(20466));
1387   dialog->Reset();
1388   dialog->SetHeading(strHeading);
1389   dialog->SetItems(&listItems);
1390   if (currentSetId >= 0)
1391   {
1392     for (int listIndex = 0; listIndex < listItems.Size(); listIndex++) 
1393     {
1394       if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == currentSetId)
1395       {
1396         dialog->SetSelected(listIndex);
1397         break;
1398       }
1399     }
1400   }
1401   dialog->EnableButton(true, 20468); // new set via button
1402   dialog->DoModal();
1403
1404   if (dialog->IsButtonPressed())
1405   { // creating new set
1406     CStdString newSetTitle;
1407     if (!CGUIKeyboardFactory::ShowAndGetInput(newSetTitle, g_localizeStrings.Get(20468), false))
1408       return false;
1409     int idSet = videodb.AddSet(newSetTitle);
1410     map<string, string> movieArt, setArt;
1411     if (!videodb.GetArtForItem(idSet, "set", setArt))
1412     {
1413       videodb.GetArtForItem(movieItem->GetVideoInfoTag()->m_iDbId, "movie", movieArt);
1414       videodb.SetArtForItem(idSet, "set", movieArt);
1415     }
1416     CFileItemPtr newSet(new CFileItem(newSetTitle));
1417     newSet->GetVideoInfoTag()->m_iDbId = idSet;
1418     selectedSet = newSet;
1419     return true;
1420   }
1421   else if (dialog->IsConfirmed())
1422   {
1423     selectedSet = dialog->GetSelectedItem();
1424     return (selectedSet != NULL);
1425   }
1426   else
1427     return false;
1428 }
1429
1430 bool CGUIDialogVideoInfo::SetMovieSet(const CFileItem *movieItem, const CFileItem *selectedSet)
1431 {
1432   CVideoDatabase videodb;
1433   if (!videodb.Open())
1434     return false;
1435
1436   videodb.SetMovieSet(movieItem->GetVideoInfoTag()->m_iDbId, selectedSet->GetVideoInfoTag()->m_iDbId);
1437   return true;
1438 }
1439
1440 bool CGUIDialogVideoInfo::UpdateVideoItemSortTitle(const CFileItemPtr &pItem)
1441 {
1442   // dont allow update while scanning
1443   if (g_application.IsVideoScanning())
1444   {
1445     CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1446     return false;
1447   }
1448
1449   CVideoDatabase database;
1450   if (!database.Open())
1451     return false;
1452
1453   int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
1454   CVideoInfoTag detail;
1455   VIDEODB_CONTENT_TYPE iType = (VIDEODB_CONTENT_TYPE)pItem->GetVideoContentType();
1456   if (iType == VIDEODB_CONTENT_MOVIES)
1457     database.GetMovieInfo("", detail, iDbId);
1458   else if (iType == VIDEODB_CONTENT_TVSHOWS)
1459     database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId);
1460
1461   CStdString currentTitle;
1462   if (detail.m_strSortTitle.empty())
1463     currentTitle = detail.m_strTitle;
1464   else
1465     currentTitle = detail.m_strSortTitle;
1466   
1467   // get the new sort title
1468   if (!CGUIKeyboardFactory::ShowAndGetInput(currentTitle, g_localizeStrings.Get(16107), false))
1469     return false;
1470
1471   return database.UpdateVideoSortTitle(iDbId, currentTitle, iType);
1472 }
1473
1474 bool CGUIDialogVideoInfo::LinkMovieToTvShow(const CFileItemPtr &item, bool bRemove, CVideoDatabase &database)
1475 {
1476   int dbId = item->GetVideoInfoTag()->m_iDbId;
1477
1478   CFileItemList list;
1479   if (bRemove)
1480   {
1481     vector<int> ids;
1482     if (!database.GetLinksToTvShow(dbId, ids))
1483       return false;
1484
1485     for (unsigned int i = 0; i < ids.size(); ++i)
1486     {
1487       CVideoInfoTag tag;
1488       database.GetTvShowInfo("", tag, ids[i]);
1489       CFileItemPtr show(new CFileItem(tag));
1490       list.Add(show);
1491     }
1492   }
1493   else
1494   {
1495     database.GetTvShowsNav("videodb://tvshows/titles", list);
1496
1497     // remove already linked shows
1498     vector<int> ids;
1499     if (!database.GetLinksToTvShow(dbId, ids))
1500       return false;
1501
1502     for (int i = 0; i < list.Size(); )
1503     {
1504       size_t j;
1505       for (j = 0; j < ids.size(); ++j)
1506       {
1507         if (list[i]->GetVideoInfoTag()->m_iDbId == ids[j])
1508           break;
1509       }
1510       if (j == ids.size())
1511         i++;
1512       else
1513         list.Remove(i);
1514     }
1515   }
1516
1517   int iSelectedLabel = 0;
1518   if (list.Size() > 1)
1519   {
1520     list.Sort(SortByLabel, SortOrderAscending, CSettings::Get().GetBool("filelists.ignorethewhensorting") ? SortAttributeIgnoreArticle : SortAttributeNone);
1521     CGUIDialogSelect* pDialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1522     pDialog->Reset();
1523     pDialog->SetItems(&list);
1524     pDialog->SetHeading(20356);
1525     pDialog->DoModal();
1526     iSelectedLabel = pDialog->GetSelectedLabel();
1527   }
1528
1529   if (iSelectedLabel > -1)
1530     return database.LinkMovieToTvshow(dbId, list[iSelectedLabel]->GetVideoInfoTag()->m_iDbId, bRemove);
1531
1532   return false;
1533 }