[DialogVideoInfo] Fixed empty dialog when edit season title
[xbmc.git] / xbmc / video / dialogs / GUIDialogVideoInfo.cpp
blob240d672744edb85eb2468b6fe2d1c42435cb67bc
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "GUIDialogVideoInfo.h"
11 #include "ContextMenuManager.h"
12 #include "FileItem.h"
13 #include "GUIPassword.h"
14 #include "GUIUserMessages.h"
15 #include "ServiceBroker.h"
16 #include "TextureCache.h"
17 #include "Util.h"
18 #include "dialogs/GUIDialogFileBrowser.h"
19 #include "dialogs/GUIDialogProgress.h"
20 #include "dialogs/GUIDialogSelect.h"
21 #include "dialogs/GUIDialogYesNo.h"
22 #include "filesystem/Directory.h"
23 #include "filesystem/VideoDatabaseDirectory.h"
24 #include "filesystem/VideoDatabaseDirectory/QueryParams.h"
25 #include "guilib/GUIComponent.h"
26 #include "guilib/GUIImage.h"
27 #include "guilib/GUIKeyboardFactory.h"
28 #include "guilib/GUIWindow.h"
29 #include "guilib/GUIWindowManager.h"
30 #include "guilib/LocalizeStrings.h"
31 #include "input/Key.h"
32 #include "messaging/helpers/DialogOKHelper.h"
33 #include "music/MusicDatabase.h"
34 #include "music/dialogs/GUIDialogMusicInfo.h"
35 #include "playlists/PlayListTypes.h"
36 #include "profiles/ProfileManager.h"
37 #include "settings/AdvancedSettings.h"
38 #include "settings/MediaSourceSettings.h"
39 #include "settings/SettingUtils.h"
40 #include "settings/Settings.h"
41 #include "settings/SettingsComponent.h"
42 #include "settings/lib/Setting.h"
43 #include "storage/MediaManager.h"
44 #include "utils/FileExtensionProvider.h"
45 #include "utils/FileUtils.h"
46 #include "utils/SortUtils.h"
47 #include "utils/StringUtils.h"
48 #include "utils/URIUtils.h"
49 #include "utils/Variant.h"
50 #include "video/VideoDbUrl.h"
51 #include "video/VideoInfoScanner.h"
52 #include "video/VideoInfoTag.h"
53 #include "video/VideoLibraryQueue.h"
54 #include "video/VideoThumbLoader.h"
55 #include "video/tags/VideoTagLoaderFFmpeg.h"
56 #include "video/windows/GUIWindowVideoNav.h"
58 #include <iterator>
59 #include <string>
61 using namespace XFILE::VIDEODATABASEDIRECTORY;
62 using namespace XFILE;
63 using namespace KODI::MESSAGING;
65 #define CONTROL_IMAGE 3
66 #define CONTROL_TEXTAREA 4
67 #define CONTROL_BTN_TRACKS 5
68 #define CONTROL_BTN_REFRESH 6
69 #define CONTROL_BTN_USERRATING 7
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
77 #define CONTROL_LIST 50
79 // predicate used by sorting and set_difference
80 bool compFileItemsByDbId(const CFileItemPtr& lhs, const CFileItemPtr& rhs)
82 return lhs->HasVideoInfoTag() && rhs->HasVideoInfoTag() && lhs->GetVideoInfoTag()->m_iDbId < rhs->GetVideoInfoTag()->m_iDbId;
85 CGUIDialogVideoInfo::CGUIDialogVideoInfo(void)
86 : CGUIDialog(WINDOW_DIALOG_VIDEO_INFO, "DialogVideoInfo.xml"),
87 m_movieItem(new CFileItem),
88 m_castList(new CFileItemList)
90 m_loadType = KEEP_IN_MEMORY;
93 CGUIDialogVideoInfo::~CGUIDialogVideoInfo(void)
95 delete m_castList;
98 bool CGUIDialogVideoInfo::OnMessage(CGUIMessage& message)
100 switch ( message.GetMessage() )
102 case GUI_MSG_WINDOW_DEINIT:
104 ClearCastList();
106 if (m_startUserrating != m_movieItem->GetVideoInfoTag()->m_iUserRating)
108 CVideoDatabase db;
109 if (db.Open())
111 m_hasUpdatedUserrating = true;
112 db.SetVideoUserRating(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_iUserRating, m_movieItem->GetVideoInfoTag()->m_type);
113 db.Close();
117 break;
119 case GUI_MSG_CLICKED:
121 int iControl = message.GetSenderId();
122 if (iControl == CONTROL_BTN_REFRESH)
124 if (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeTvShow)
126 bool bCanceled=false;
127 if (CGUIDialogYesNo::ShowAndGetInput(CVariant{20377}, CVariant{20378}, bCanceled, CVariant{ "" }, CVariant{ "" }, CGUIDialogYesNo::NO_TIMEOUT))
129 m_bRefreshAll = true;
130 CVideoDatabase db;
131 if (db.Open())
133 db.SetPathHash(m_movieItem->GetVideoInfoTag()->m_strPath,"");
134 db.Close();
137 else
138 m_bRefreshAll = false;
140 if (bCanceled)
141 return false;
143 m_bRefresh = true;
144 Close();
145 return true;
147 else if (iControl == CONTROL_BTN_TRACKS)
149 m_bViewReview = !m_bViewReview;
150 Update();
152 else if (iControl == CONTROL_BTN_PLAY)
154 Play();
156 else if (iControl == CONTROL_BTN_USERRATING)
158 OnSetUserrating();
160 else if (iControl == CONTROL_BTN_RESUME)
162 Play(true);
164 else if (iControl == CONTROL_BTN_GET_THUMB)
166 OnGetArt();
168 else if (iControl == CONTROL_BTN_PLAY_TRAILER)
170 PlayTrailer();
172 else if (iControl == CONTROL_BTN_GET_FANART)
174 OnGetFanart();
176 else if (iControl == CONTROL_BTN_DIRECTOR)
178 auto directors = m_movieItem->GetVideoInfoTag()->m_director;
179 if (directors.size() == 0)
180 return true;
181 if (directors.size() == 1)
182 OnSearch(directors[0]);
183 else
185 auto pDlgSelect = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
186 if (pDlgSelect)
188 pDlgSelect->Reset();
189 pDlgSelect->SetHeading(CVariant{22080});
190 for (const auto &director: directors)
191 pDlgSelect->Add(director);
192 pDlgSelect->Open();
194 int iItem = pDlgSelect->GetSelectedItem();
195 if (iItem < 0)
196 return true;
197 OnSearch(directors[iItem]);
201 else if (iControl == CONTROL_LIST)
203 int iAction = message.GetParam1();
204 if (ACTION_SELECT_ITEM == iAction || ACTION_MOUSE_LEFT_CLICK == iAction)
206 CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl);
207 OnMessage(msg);
208 int iItem = msg.GetParam1();
209 if (iItem < 0 || iItem >= m_castList->Size())
210 break;
211 std::string strItem = m_castList->Get(iItem)->GetLabel();
212 if (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeVideoCollection)
214 SetMovie(m_castList->Get(iItem).get());
215 Close();
216 Open();
218 else
219 OnSearch(strItem);
223 break;
224 case GUI_MSG_NOTIFY_ALL:
226 if (IsActive() && message.GetParam1() == GUI_MSG_UPDATE_ITEM && message.GetItem())
228 CFileItemPtr item = std::static_pointer_cast<CFileItem>(message.GetItem());
229 if (item && m_movieItem->IsPath(item->GetPath()))
230 { // Just copy over the stream details and the thumb if we don't already have one
231 if (!m_movieItem->HasArt("thumb"))
232 m_movieItem->SetArt("thumb", item->GetArt("thumb"));
233 m_movieItem->GetVideoInfoTag()->m_streamDetails = item->GetVideoInfoTag()->m_streamDetails;
235 return true;
240 return CGUIDialog::OnMessage(message);
243 void CGUIDialogVideoInfo::OnInitWindow()
245 m_bRefresh = false;
246 m_bRefreshAll = true;
247 m_hasUpdatedThumb = false;
248 m_hasUpdatedUserrating = false;
249 m_bViewReview = true;
251 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
253 const std::string uniqueId = m_movieItem->GetProperty("xxuniqueid").asString();
254 if (uniqueId.empty() || !StringUtils::StartsWithNoCase(uniqueId.c_str(), "xx"))
255 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_REFRESH,
256 (profileManager->GetCurrentProfile().canWriteDatabases() ||
257 g_passwordManager.bMasterUser));
258 else
259 CONTROL_DISABLE(CONTROL_BTN_REFRESH);
260 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_GET_THUMB,
261 (profileManager->GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser) &&
262 !StringUtils::StartsWithNoCase(m_movieItem->GetVideoInfoTag()->
263 GetUniqueID().c_str(), "plugin"));
264 // Disable video user rating button for plugins and sets as they don't have tables to save this
265 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_USERRATING, !m_movieItem->IsPlugin() && m_movieItem->GetVideoInfoTag()->m_type != MediaTypeVideoCollection);
267 VideoDbContentType type = m_movieItem->GetVideoContentType();
268 if (type == VideoDbContentType::TVSHOWS || type == VideoDbContentType::MOVIES)
269 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_GET_FANART, (profileManager->
270 GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser) &&
271 !StringUtils::StartsWithNoCase(m_movieItem->GetVideoInfoTag()->
272 GetUniqueID().c_str(), "plugin"));
273 else
274 CONTROL_DISABLE(CONTROL_BTN_GET_FANART);
276 Update();
278 CGUIDialog::OnInitWindow();
281 bool CGUIDialogVideoInfo::OnAction(const CAction &action)
283 int userrating = m_movieItem->GetVideoInfoTag()->m_iUserRating;
284 if (action.GetID() == ACTION_INCREASE_RATING)
286 SetUserrating(userrating + 1);
287 return true;
289 else if (action.GetID() == ACTION_DECREASE_RATING)
291 SetUserrating(userrating - 1);
292 return true;
294 else if (action.GetID() == ACTION_SHOW_INFO)
296 Close();
297 return true;
299 return CGUIDialog::OnAction(action);
302 void CGUIDialogVideoInfo::SetUserrating(int userrating) const
304 userrating = std::max(userrating, 0);
305 userrating = std::min(userrating, 10);
306 if (userrating != m_movieItem->GetVideoInfoTag()->m_iUserRating)
308 m_movieItem->GetVideoInfoTag()->SetUserrating(userrating);
310 // send a message to all windows to tell them to update the fileitem (eg playlistplayer, media windows)
311 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_movieItem);
312 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
316 void CGUIDialogVideoInfo::SetMovie(const CFileItem *item)
318 *m_movieItem = *item;
320 // setup cast list
321 ClearCastList();
323 // When the scraper throws an error, the video tag can be null here
324 if (!item->HasVideoInfoTag())
325 return;
327 MediaType type = item->GetVideoInfoTag()->m_type;
329 m_startUserrating = m_movieItem->GetVideoInfoTag()->m_iUserRating;
331 if (type == MediaTypeMusicVideo)
332 { // music video
333 CMusicDatabase database;
334 database.Open();
335 const std::vector<std::string> &artists = m_movieItem->GetVideoInfoTag()->m_artist;
336 for (std::vector<std::string>::const_iterator it = artists.begin(); it != artists.end(); ++it)
338 int idArtist = database.GetArtistByName(*it);
339 std::string thumb = database.GetArtForItem(idArtist, MediaTypeArtist, "thumb");
340 CFileItemPtr item(new CFileItem(*it));
341 if (!thumb.empty())
342 item->SetArt("thumb", thumb);
343 item->SetArt("icon", "DefaultArtist.png");
344 item->SetLabel2(g_localizeStrings.Get(29904));
345 m_castList->Add(item);
347 // get performers in the music video (added as actors)
348 for (CVideoInfoTag::iCast it = m_movieItem->GetVideoInfoTag()->m_cast.begin();
349 it != m_movieItem->GetVideoInfoTag()->m_cast.end(); ++it)
351 // Check to see if we have already added this performer as the artist and skip adding if so
352 auto haveArtist = std::find(std::begin(artists), std::end(artists), it->strName);
353 if (haveArtist == artists.end()) // artist or performer not already in the list
355 CFileItemPtr item(new CFileItem(it->strName));
356 if (!it->thumb.empty())
357 item->SetArt("thumb", it->thumb);
358 else if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
359 CSettings::SETTING_VIDEOLIBRARY_ACTORTHUMBS))
360 { // backward compatibility
361 std::string thumb = CScraperUrl::GetThumbUrl(it->thumbUrl.GetFirstUrlByType());
362 if (!thumb.empty())
364 item->SetArt("thumb", thumb);
365 CServiceBroker::GetTextureCache()->BackgroundCacheImage(thumb);
368 item->SetArt("icon", "DefaultActor.png");
369 item->SetLabel(it->strName);
370 item->SetLabel2(it->strRole);
371 m_castList->Add(item);
375 else if (type == MediaTypeVideoCollection)
377 CVideoDatabase database;
378 database.Open();
379 database.GetMoviesNav(m_movieItem->GetPath(), *m_castList, -1, -1, -1, -1, -1, -1,
380 m_movieItem->GetVideoInfoTag()->m_set.id, -1,
381 SortDescription(), VideoDbDetailsAll);
382 m_castList->Sort(SortBySortTitle, SortOrderDescending);
383 CVideoThumbLoader loader;
384 for (auto& item : *m_castList)
385 loader.LoadItem(item.get());
387 else
388 { // movie/show/episode
389 for (CVideoInfoTag::iCast it = m_movieItem->GetVideoInfoTag()->m_cast.begin(); it != m_movieItem->GetVideoInfoTag()->m_cast.end(); ++it)
391 CFileItemPtr item(new CFileItem(it->strName));
392 if (!it->thumb.empty())
393 item->SetArt("thumb", it->thumb);
394 else
396 const std::shared_ptr<CSettings> settings =
397 CServiceBroker::GetSettingsComponent()->GetSettings();
398 if (settings->GetInt(CSettings::SETTING_VIDEOLIBRARY_ARTWORK_LEVEL) !=
399 CSettings::VIDEOLIBRARY_ARTWORK_LEVEL_NONE &&
400 settings->GetBool(CSettings::SETTING_VIDEOLIBRARY_ACTORTHUMBS))
401 { // backward compatibility
402 std::string thumb = CScraperUrl::GetThumbUrl(it->thumbUrl.GetFirstUrlByType());
403 if (!thumb.empty())
405 item->SetArt("thumb", thumb);
406 CServiceBroker::GetTextureCache()->BackgroundCacheImage(thumb);
410 item->SetArt("icon", "DefaultActor.png");
411 item->SetLabel(it->strName);
412 item->SetLabel2(it->strRole);
413 m_castList->Add(item);
417 if (type == MediaTypeMovie)
419 // local trailers should always override non-local, so check
420 // for a local one if the registered trailer is online
421 if (m_movieItem->GetVideoInfoTag()->m_strTrailer.empty() ||
422 URIUtils::IsInternetStream(m_movieItem->GetVideoInfoTag()->m_strTrailer))
424 std::string localTrailer = m_movieItem->FindTrailer();
425 if (!localTrailer.empty())
427 m_movieItem->GetVideoInfoTag()->m_strTrailer = localTrailer;
428 CVideoDatabase database;
429 if (database.Open())
431 database.SetSingleValue(VideoDbContentType::MOVIES, VIDEODB_ID_TRAILER,
432 m_movieItem->GetVideoInfoTag()->m_iDbId,
433 m_movieItem->GetVideoInfoTag()->m_strTrailer);
434 database.Close();
435 CUtil::DeleteVideoDatabaseDirectoryCache();
441 m_castList->SetContent(CMediaTypes::ToPlural(type));
443 CVideoThumbLoader loader;
444 loader.LoadItem(m_movieItem.get());
447 void CGUIDialogVideoInfo::Update()
449 // setup plot text area
450 std::shared_ptr<CSettingList> setting(std::dynamic_pointer_cast<CSettingList>(
451 CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(CSettings::SETTING_VIDEOLIBRARY_SHOWUNWATCHEDPLOTS)));
452 std::string strTmp = m_movieItem->GetVideoInfoTag()->m_strPlot;
453 if (m_movieItem->GetVideoInfoTag()->m_type != MediaTypeTvShow)
454 if (m_movieItem->GetVideoInfoTag()->GetPlayCount() == 0 && setting &&
455 ((m_movieItem->GetVideoInfoTag()->m_type == MediaTypeMovie &&
456 !CSettingUtils::FindIntInList(setting,
457 CSettings::VIDEOLIBRARY_PLOTS_SHOW_UNWATCHED_MOVIES)) ||
458 (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeEpisode &&
459 !CSettingUtils::FindIntInList(
460 setting, CSettings::VIDEOLIBRARY_PLOTS_SHOW_UNWATCHED_TVSHOWEPISODES))))
461 strTmp = g_localizeStrings.Get(20370);
463 StringUtils::Trim(strTmp);
464 SetLabel(CONTROL_TEXTAREA, strTmp);
466 CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST, 0, 0, m_castList);
467 OnMessage(msg);
469 if (GetControl(CONTROL_BTN_TRACKS)) // if no CONTROL_BTN_TRACKS found - allow skinner full visibility control over CONTROL_TEXTAREA and CONTROL_LIST
471 if (m_bViewReview)
473 if (!m_movieItem->GetVideoInfoTag()->m_artist.empty())
475 SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 133);
477 else if (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeVideoCollection)
479 SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 20342);
481 else
483 SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 206);
486 SET_CONTROL_HIDDEN(CONTROL_LIST);
487 SET_CONTROL_VISIBLE(CONTROL_TEXTAREA);
489 else
491 SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 207);
493 SET_CONTROL_HIDDEN(CONTROL_TEXTAREA);
494 SET_CONTROL_VISIBLE(CONTROL_LIST);
498 // Check for resumability
499 if (m_movieItem->GetVideoInfoTag()->GetResumePoint().timeInSeconds > 0.0)
500 CONTROL_ENABLE(CONTROL_BTN_RESUME);
501 else
502 CONTROL_DISABLE(CONTROL_BTN_RESUME);
504 CONTROL_ENABLE(CONTROL_BTN_PLAY);
506 // update the thumbnail
507 CGUIControl* pControl = GetControl(CONTROL_IMAGE);
508 if (pControl)
510 CGUIImage* pImageControl = static_cast<CGUIImage*>(pControl);
511 pImageControl->FreeResources();
512 pImageControl->SetFileName(m_movieItem->GetArt("thumb"));
514 // tell our GUI to completely reload all controls (as some of them
515 // are likely to have had this image in use so will need refreshing)
516 if (m_hasUpdatedThumb)
518 CGUIMessage reload(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
519 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(reload);
523 bool CGUIDialogVideoInfo::NeedRefresh() const
525 return m_bRefresh;
528 bool CGUIDialogVideoInfo::RefreshAll() const
530 return m_bRefreshAll;
533 void CGUIDialogVideoInfo::OnSearch(std::string& strSearch)
535 CGUIDialogProgress *progress = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS);
536 if (progress)
538 progress->SetHeading(CVariant{194});
539 progress->SetLine(0, CVariant{strSearch});
540 progress->SetLine(1, CVariant{""});
541 progress->SetLine(2, CVariant{""});
542 progress->Open();
543 progress->Progress();
545 CFileItemList items;
546 DoSearch(strSearch, items);
548 if (progress)
549 progress->Close();
551 if (items.Size())
553 CGUIDialogSelect* pDlgSelect = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
554 if (pDlgSelect)
556 pDlgSelect->Reset();
557 pDlgSelect->SetHeading(CVariant{283});
559 CVideoThumbLoader loader;
560 for (int i = 0; i < items.Size(); i++)
562 if (items[i]->HasVideoInfoTag() &&
563 items[i]->GetVideoInfoTag()->GetPlayCount() > 0)
564 items[i]->SetLabel2(g_localizeStrings.Get(16102));
566 loader.LoadItem(items[i].get());
567 pDlgSelect->Add(*items[i]);
570 pDlgSelect->SetUseDetails(true);
571 pDlgSelect->Open();
573 int iItem = pDlgSelect->GetSelectedItem();
574 if (iItem < 0)
575 return;
577 OnSearchItemFound(items[iItem].get());
580 else
582 HELPERS::ShowOKDialogText(CVariant{194}, CVariant{284});
586 void CGUIDialogVideoInfo::DoSearch(std::string& strSearch, CFileItemList& items) const
588 CVideoDatabase db;
589 if (!db.Open())
590 return;
592 CFileItemList movies;
593 db.GetMoviesByActor(strSearch, movies);
594 for (int i = 0; i < movies.Size(); ++i)
596 std::string label = movies[i]->GetVideoInfoTag()->m_strTitle;
597 if (movies[i]->GetVideoInfoTag()->HasYear())
598 label += StringUtils::Format(" ({})", movies[i]->GetVideoInfoTag()->GetYear());
599 movies[i]->SetLabel(label);
601 CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20338) + "] ", items);
603 db.GetTvShowsByActor(strSearch, movies);
604 for (int i = 0; i < movies.Size(); ++i)
606 std::string label = movies[i]->GetVideoInfoTag()->m_strShowTitle;
607 if (movies[i]->GetVideoInfoTag()->HasYear())
608 label += StringUtils::Format(" ({})", movies[i]->GetVideoInfoTag()->GetYear());
609 movies[i]->SetLabel(label);
611 CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20364) + "] ", items);
613 db.GetEpisodesByActor(strSearch, movies);
614 for (int i = 0; i < movies.Size(); ++i)
616 std::string label = movies[i]->GetVideoInfoTag()->m_strTitle + " (" + movies[i]->GetVideoInfoTag()->m_strShowTitle + ")";
617 movies[i]->SetLabel(label);
619 CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20359) + "] ", items);
621 db.GetMusicVideosByArtist(strSearch, movies);
622 for (int i = 0; i < movies.Size(); ++i)
624 std::string label = StringUtils::Join(movies[i]->GetVideoInfoTag()->m_artist, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator) + " - " + movies[i]->GetVideoInfoTag()->m_strTitle;
625 if (movies[i]->GetVideoInfoTag()->HasYear())
626 label += StringUtils::Format(" ({})", movies[i]->GetVideoInfoTag()->GetYear());
627 movies[i]->SetLabel(label);
629 CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20391) + "] ", items);
630 db.Close();
632 // Search for music albums by artist with name matching search string
633 CMusicDatabase music_database;
634 if (!music_database.Open())
635 return;
637 if (music_database.SearchAlbumsByArtistName(strSearch, movies))
639 for (int i = 0; i < movies.Size(); ++i)
641 // Set type so that video thumbloader handles album art
642 movies[i]->GetVideoInfoTag()->m_type = MediaTypeAlbum;
644 CGUIWindowVideoBase::AppendAndClearSearchItems(
645 movies, "[" + g_localizeStrings.Get(36918) + "] ", items);
647 music_database.Close();
650 void CGUIDialogVideoInfo::OnSearchItemFound(const CFileItem* pItem)
652 VideoDbContentType type = pItem->GetVideoContentType();
654 CVideoDatabase db;
655 if (!db.Open())
656 return;
658 CVideoInfoTag movieDetails;
659 if (type == VideoDbContentType::MOVIES)
660 db.GetMovieInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
661 if (type == VideoDbContentType::EPISODES)
662 db.GetEpisodeInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
663 if (type == VideoDbContentType::TVSHOWS)
664 db.GetTvShowInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
665 if (type == VideoDbContentType::MUSICVIDEOS)
666 db.GetMusicVideoInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
667 db.Close();
668 if (type == VideoDbContentType::MUSICALBUMS)
670 Close();
671 CGUIDialogMusicInfo::ShowFor(const_cast<CFileItem*>(pItem));
672 return; // No video info to refresh so just close the window and go back to the fileitem list
675 CFileItem item(*pItem);
676 *item.GetVideoInfoTag() = movieDetails;
677 SetMovie(&item);
678 // refresh our window entirely
679 Close();
680 Open();
683 void CGUIDialogVideoInfo::ClearCastList()
685 CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_LIST);
686 OnMessage(msg);
687 m_castList->Clear();
690 void CGUIDialogVideoInfo::Play(bool resume)
692 if (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeTvShow)
694 std::string strPath;
695 if (m_movieItem->IsPlugin())
697 strPath = m_movieItem->GetPath();
698 Close();
699 if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VIDEO_NAV)
701 CGUIMessage message(GUI_MSG_NOTIFY_ALL, CServiceBroker::GetGUI()->
702 GetWindowManager().GetActiveWindow(), 0, GUI_MSG_UPDATE, 0);
703 message.SetStringParam(strPath);
704 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
706 else
707 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_VIDEO_NAV,strPath);
709 else
711 strPath = StringUtils::Format("videodb://tvshows/titles/{}/",
712 m_movieItem->GetVideoInfoTag()->m_iDbId);
713 Close();
714 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_VIDEO_NAV,strPath);
716 return;
719 if (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeVideoCollection)
721 std::string strPath = StringUtils::Format("videodb://movies/sets/{}/?setid={}",
722 m_movieItem->GetVideoInfoTag()->m_iDbId,
723 m_movieItem->GetVideoInfoTag()->m_iDbId);
724 Close();
725 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_VIDEO_NAV, strPath);
726 return;
729 CGUIWindowVideoNav* pWindow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowVideoNav>(WINDOW_VIDEO_NAV);
730 if (pWindow)
732 // close our dialog
733 Close(true);
734 if (resume)
735 m_movieItem->SetStartOffset(STARTOFFSET_RESUME);
736 else if (!CGUIWindowVideoBase::ShowResumeMenu(*m_movieItem))
738 // The Resume dialog was closed without any choice
739 Open();
740 return;
742 m_movieItem->SetProperty("playlist_type_hint", PLAYLIST::TYPE_VIDEO);
744 pWindow->PlayMovie(m_movieItem.get());
748 namespace
750 // Add art types required in Kodi and configured by the user
751 void AddHardCodedAndExtendedArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTag& tag)
753 for (const auto& artType : CVideoThumbLoader::GetArtTypes(tag.m_type))
755 if (find(artTypes.cbegin(), artTypes.cend(), artType) == artTypes.cend())
756 artTypes.emplace_back(artType);
760 // Add art types currently assigned to the media item
761 void AddCurrentArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTag& tag,
762 CVideoDatabase& db)
764 std::map<std::string, std::string> currentArt;
765 db.GetArtForItem(tag.m_iDbId, tag.m_type, currentArt);
766 for (const auto& art : currentArt)
768 if (!art.second.empty() && find(artTypes.cbegin(), artTypes.cend(), art.first) == artTypes.cend())
769 artTypes.push_back(art.first);
773 // Add art types that exist for other media items of the same type
774 void AddMediaTypeArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTag& tag,
775 CVideoDatabase& db)
777 std::vector<std::string> dbArtTypes;
778 db.GetArtTypes(tag.m_type, dbArtTypes);
779 for (const auto& artType : dbArtTypes)
781 if (find(artTypes.cbegin(), artTypes.cend(), artType) == artTypes.cend())
782 artTypes.push_back(artType);
786 // Add art types from available but unassigned artwork for this media item
787 void AddAvailableArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTag& tag,
788 CVideoDatabase& db)
790 for (const auto& artType : db.GetAvailableArtTypesForItem(tag.m_iDbId, tag.m_type))
792 if (find(artTypes.cbegin(), artTypes.cend(), artType) == artTypes.cend())
793 artTypes.push_back(artType);
797 std::vector<std::string> GetArtTypesList(const CVideoInfoTag& tag)
799 CVideoDatabase db;
800 db.Open();
802 std::vector<std::string> artTypes;
804 AddHardCodedAndExtendedArtTypes(artTypes, tag);
805 AddCurrentArtTypes(artTypes, tag, db);
806 AddMediaTypeArtTypes(artTypes, tag, db);
807 AddAvailableArtTypes(artTypes, tag, db);
809 db.Close();
810 return artTypes;
814 std::string CGUIDialogVideoInfo::ChooseArtType(const CFileItem &videoItem)
816 // prompt for choice
817 CGUIDialogSelect *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
818 if (!dialog || !videoItem.HasVideoInfoTag())
819 return "";
821 CFileItemList items;
822 dialog->SetHeading(CVariant{13511});
823 dialog->Reset();
824 dialog->SetUseDetails(true);
825 dialog->EnableButton(true, 13516);
827 std::vector<std::string> artTypes = GetArtTypesList(*videoItem.GetVideoInfoTag());
829 for (std::vector<std::string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
831 const std::string& type = *i;
832 CFileItemPtr item(new CFileItem(type, false));
833 if (type == "banner")
834 item->SetLabel(g_localizeStrings.Get(20020));
835 else if (type == "fanart")
836 item->SetLabel(g_localizeStrings.Get(20445));
837 else if (type == "poster")
838 item->SetLabel(g_localizeStrings.Get(20021));
839 else if (type == "thumb")
840 item->SetLabel(g_localizeStrings.Get(21371));
841 else
842 item->SetLabel(type);
843 item->SetProperty("type", type);
844 if (videoItem.HasArt(type))
845 item->SetArt("thumb", videoItem.GetArt(type));
846 items.Add(item);
849 dialog->SetItems(items);
850 dialog->Open();
852 if (dialog->IsButtonPressed())
854 // Get the new artwork name
855 std::string strArtworkName;
856 if (!CGUIKeyboardFactory::ShowAndGetInput(strArtworkName, CVariant{g_localizeStrings.Get(13516)}, false))
857 return "";
859 return strArtworkName;
862 return dialog->GetSelectedFileItem()->GetProperty("type").asString();
865 void CGUIDialogVideoInfo::OnGetArt()
867 if (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeVideoCollection)
869 ManageVideoItemArtwork(m_movieItem, m_movieItem->GetVideoInfoTag()->m_type);
870 return;
872 std::string type = ChooseArtType(*m_movieItem);
873 if (type.empty())
874 return; // cancelled
876 //! @todo this can be removed once these are unified.
877 if (type == "fanart")
878 OnGetFanart();
879 else
881 CFileItemList items;
883 // Current thumb
884 if (m_movieItem->HasArt(type))
886 CFileItemPtr item(new CFileItem("thumb://Current", false));
887 item->SetArt("thumb", m_movieItem->GetArt(type));
888 item->SetArt("icon", "DefaultPicture.png");
889 item->SetLabel(g_localizeStrings.Get(13512));
890 items.Add(item);
892 else if ((type == "poster" || type == "banner") && m_movieItem->HasArt("thumb"))
893 { // add the 'thumb' type in
894 CFileItemPtr item(new CFileItem("thumb://Thumb", false));
895 item->SetArt("thumb", m_movieItem->GetArt("thumb"));
896 item->SetArt("icon", "DefaultPicture.png");
897 item->SetLabel(g_localizeStrings.Get(13512));
898 items.Add(item);
901 std::string embeddedArt;
902 if (URIUtils::HasExtension(m_movieItem->GetVideoInfoTag()->m_strFileNameAndPath, ".mkv"))
904 CFileItem item(m_movieItem->GetVideoInfoTag()->m_strFileNameAndPath, false);
905 CVideoTagLoaderFFmpeg loader(item, nullptr, false);
906 CVideoInfoTag tag;
907 loader.Load(tag, false, nullptr);
908 for (const auto& it : tag.m_coverArt)
910 if (it.m_type == type)
912 CFileItemPtr itemF(new CFileItem("thumb://Embedded", false));
913 embeddedArt = CTextureUtils::GetWrappedImageURL(item.GetPath(), "video_" + type);
914 itemF->SetArt("thumb", embeddedArt);
915 itemF->SetLabel(g_localizeStrings.Get(13519));
916 items.Add(itemF);
921 // Grab the thumbnails from the web
922 m_movieItem->GetVideoInfoTag()->m_strPictureURL.Parse();
923 std::vector<std::string> thumbs;
924 int season = (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeSeason) ? m_movieItem->GetVideoInfoTag()->m_iSeason : -1;
925 m_movieItem->GetVideoInfoTag()->m_strPictureURL.GetThumbUrls(thumbs, type, season);
927 for (unsigned int i = 0; i < thumbs.size(); ++i)
929 std::string strItemPath = StringUtils::Format("thumb://Remote{}", i);
930 CFileItemPtr item(new CFileItem(strItemPath, false));
931 item->SetArt("thumb", thumbs[i]);
932 item->SetArt("icon", "DefaultPicture.png");
933 item->SetLabel(g_localizeStrings.Get(13513));
935 //! @todo Do we need to clear the cached image?
936 // CServiceBroker::GetTextureCache()->ClearCachedImage(thumb);
937 items.Add(item);
940 std::string localThumb = CVideoThumbLoader::GetLocalArt(*m_movieItem, type);
941 if (!localThumb.empty())
943 CFileItemPtr item(new CFileItem("thumb://Local", false));
944 item->SetArt("thumb", localThumb);
945 item->SetArt("icon", "DefaultPicture.png");
946 item->SetLabel(g_localizeStrings.Get(13514));
947 items.Add(item);
949 else
950 { // no local thumb exists, so we are just using the IMDb thumb or cached thumb
951 // which is probably the IMDb thumb. These could be wrong, so allow the user
952 // to delete the incorrect thumb
953 CFileItemPtr item(new CFileItem("thumb://None", false));
954 item->SetArt("icon", "DefaultPicture.png");
955 item->SetLabel(g_localizeStrings.Get(13515));
956 items.Add(item);
959 std::string result;
960 VECSOURCES sources(*CMediaSourceSettings::GetInstance().GetSources("video"));
961 AddItemPathToFileBrowserSources(sources, *m_movieItem);
962 CServiceBroker::GetMediaManager().GetLocalDrives(sources);
963 if (CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result) &&
964 result != "thumb://Current") // user didn't choose the one they have
966 std::string newThumb;
967 if (StringUtils::StartsWith(result, "thumb://Remote"))
969 int number = atoi(result.substr(14).c_str());
970 newThumb = thumbs[number];
972 else if (result == "thumb://Thumb")
973 newThumb = m_movieItem->GetArt("thumb");
974 else if (result == "thumb://Local")
975 newThumb = localThumb;
976 else if (result == "thumb://Embedded")
977 newThumb = embeddedArt;
978 else if (CFileUtils::Exists(result))
979 newThumb = result;
980 else // none
981 newThumb.clear();
983 // update thumb in the database
984 CVideoDatabase db;
985 if (db.Open())
987 db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, type, newThumb);
988 db.Close();
990 CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
991 m_movieItem->SetArt(type, newThumb);
992 if (m_movieItem->HasProperty("set_folder_thumb"))
993 { // have a folder thumb to set as well
994 VIDEO::CVideoInfoScanner::ApplyThumbToFolder(m_movieItem->GetProperty("set_folder_thumb").asString(), newThumb);
996 m_hasUpdatedThumb = true;
1000 // Update our screen
1001 Update();
1003 // re-open the art selection dialog as we come back from
1004 // the image selection dialog
1005 OnGetArt();
1008 // Allow user to select a Fanart
1009 void CGUIDialogVideoInfo::OnGetFanart()
1011 CFileItemList items;
1013 // Ensure the fanart is unpacked
1014 m_movieItem->GetVideoInfoTag()->m_fanart.Unpack();
1016 if (m_movieItem->HasArt("fanart"))
1018 CFileItemPtr itemCurrent(new CFileItem("fanart://Current",false));
1019 itemCurrent->SetArt("thumb", m_movieItem->GetArt("fanart"));
1020 itemCurrent->SetArt("icon", "DefaultPicture.png");
1021 itemCurrent->SetLabel(g_localizeStrings.Get(20440));
1022 items.Add(itemCurrent);
1025 std::string embeddedArt;
1026 if (URIUtils::HasExtension(m_movieItem->GetVideoInfoTag()->m_strFileNameAndPath, ".mkv"))
1028 CFileItem item(m_movieItem->GetVideoInfoTag()->m_strFileNameAndPath, false);
1029 CVideoTagLoaderFFmpeg loader(item, nullptr, false);
1030 CVideoInfoTag tag;
1031 loader.Load(tag, false, nullptr);
1032 for (const auto& it : tag.m_coverArt)
1034 if (it.m_type == "fanart")
1036 CFileItemPtr itemF(new CFileItem("fanart://Embedded", false));
1037 embeddedArt = CTextureUtils::GetWrappedImageURL(item.GetPath(), "video_fanart");
1038 itemF->SetArt("thumb", embeddedArt);
1039 itemF->SetLabel(g_localizeStrings.Get(13520));
1040 items.Add(itemF);
1045 // Grab the thumbnails from the web
1046 for (unsigned int i = 0; i < m_movieItem->GetVideoInfoTag()->m_fanart.GetNumFanarts(); i++)
1048 if (URIUtils::IsProtocol(m_movieItem->GetVideoInfoTag()->m_fanart.GetPreviewURL(i), "image"))
1049 continue;
1050 std::string strItemPath = StringUtils::Format("fanart://Remote{}", i);
1051 CFileItemPtr item(new CFileItem(strItemPath, false));
1052 std::string thumb = m_movieItem->GetVideoInfoTag()->m_fanart.GetPreviewURL(i);
1053 item->SetArt("thumb", CTextureUtils::GetWrappedThumbURL(thumb));
1054 item->SetArt("icon", "DefaultPicture.png");
1055 item->SetLabel(g_localizeStrings.Get(20441));
1057 //! @todo Do we need to clear the cached image?
1058 // CServiceBroker::GetTextureCache()->ClearCachedImage(thumb);
1059 items.Add(item);
1062 CFileItem item(*m_movieItem->GetVideoInfoTag());
1063 std::string strLocal = item.GetLocalFanart();
1064 if (!strLocal.empty())
1066 CFileItemPtr itemLocal(new CFileItem("fanart://Local",false));
1067 itemLocal->SetArt("thumb", strLocal);
1068 itemLocal->SetArt("icon", "DefaultPicture.png");
1069 itemLocal->SetLabel(g_localizeStrings.Get(20438));
1071 //! @todo Do we need to clear the cached image?
1072 CServiceBroker::GetTextureCache()->ClearCachedImage(strLocal);
1073 items.Add(itemLocal);
1075 else
1077 CFileItemPtr itemNone(new CFileItem("fanart://None", false));
1078 itemNone->SetArt("icon", "DefaultPicture.png");
1079 itemNone->SetLabel(g_localizeStrings.Get(20439));
1080 items.Add(itemNone);
1083 std::string result;
1084 VECSOURCES sources(*CMediaSourceSettings::GetInstance().GetSources("video"));
1085 AddItemPathToFileBrowserSources(sources, item);
1086 CServiceBroker::GetMediaManager().GetLocalDrives(sources);
1087 bool flip=false;
1088 if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) ||
1089 StringUtils::EqualsNoCase(result, "fanart://Current"))
1090 return; // user cancelled
1092 if (StringUtils::EqualsNoCase(result, "fanart://Local"))
1093 result = strLocal;
1095 if (StringUtils::EqualsNoCase(result, "fanart://Embedded"))
1097 unsigned int current = m_movieItem->GetVideoInfoTag()->m_fanart.GetNumFanarts();
1098 int found = -1;
1099 for (size_t i = 0; i < current; ++i)
1100 if (URIUtils::IsProtocol(m_movieItem->GetVideoInfoTag()->m_fanart.GetImageURL(), "image"))
1101 found = i;
1102 if (found != -1)
1104 m_movieItem->GetVideoInfoTag()->m_fanart.AddFanart(embeddedArt, "", "");
1105 found = current;
1108 m_movieItem->GetVideoInfoTag()->m_fanart.SetPrimaryFanart(found);
1110 CVideoDatabase db;
1111 if (db.Open())
1113 db.UpdateFanart(*m_movieItem, m_movieItem->GetVideoContentType());
1114 db.Close();
1116 result = embeddedArt;
1119 if (StringUtils::StartsWith(result, "fanart://Remote"))
1121 int iFanart = atoi(result.substr(15).c_str());
1122 // set new primary fanart, and update our database accordingly
1123 m_movieItem->GetVideoInfoTag()->m_fanart.SetPrimaryFanart(iFanart);
1124 CVideoDatabase db;
1125 if (db.Open())
1127 db.UpdateFanart(*m_movieItem, m_movieItem->GetVideoContentType());
1128 db.Close();
1130 result = m_movieItem->GetVideoInfoTag()->m_fanart.GetImageURL();
1132 else if (StringUtils::EqualsNoCase(result, "fanart://None") || !CFileUtils::Exists(result))
1133 result.clear();
1135 // set the fanart image
1136 if (flip && !result.empty())
1137 result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
1138 CVideoDatabase db;
1139 if (db.Open())
1141 db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, "fanart", result);
1142 db.Close();
1145 CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
1146 m_movieItem->SetArt("fanart", result);
1147 m_hasUpdatedThumb = true;
1149 // Update our screen
1150 Update();
1153 void CGUIDialogVideoInfo::OnSetUserrating() const
1155 CGUIDialogSelect *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
1156 if (dialog)
1158 dialog->SetHeading(CVariant{ 38023 });
1159 dialog->Add(g_localizeStrings.Get(38022));
1160 for (int i = 1; i <= 10; i++)
1161 dialog->Add(StringUtils::Format("{}: {}", g_localizeStrings.Get(563), i));
1163 dialog->SetSelected(m_movieItem->GetVideoInfoTag()->m_iUserRating);
1165 dialog->Open();
1167 int iItem = dialog->GetSelectedItem();
1168 if (iItem < 0)
1169 return;
1171 SetUserrating(iItem);
1175 void CGUIDialogVideoInfo::PlayTrailer()
1177 Close(true);
1178 CGUIMessage msg(GUI_MSG_PLAY_TRAILER, 0, 0, 0, 0, m_movieItem);
1179 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
1182 void CGUIDialogVideoInfo::SetLabel(int iControl, const std::string &strLabel)
1184 if (strLabel.empty())
1186 SET_CONTROL_LABEL(iControl, 416); // "Not available"
1188 else
1190 SET_CONTROL_LABEL(iControl, strLabel);
1194 std::string CGUIDialogVideoInfo::GetThumbnail() const
1196 return m_movieItem->GetArt("thumb");
1199 namespace
1201 std::string GetItemPathForBrowserSource(const CFileItem& item)
1203 if (!item.HasVideoInfoTag())
1204 return "";
1206 std::string itemDir = item.GetVideoInfoTag()->m_basePath;
1207 //season
1208 if (itemDir.empty())
1209 itemDir = item.GetVideoInfoTag()->GetPath();
1211 CFileItem itemTmp(itemDir, false);
1212 if (itemTmp.IsVideo())
1213 itemDir = URIUtils::GetParentPath(itemDir);
1215 return itemDir;
1218 void AddItemPathStringToFileBrowserSources(VECSOURCES& sources,
1219 const std::string& itemDir, const std::string& label)
1221 if (!itemDir.empty() && CDirectory::Exists(itemDir))
1223 CMediaSource itemSource;
1224 itemSource.strName = label;
1225 itemSource.strPath = itemDir;
1226 sources.push_back(itemSource);
1229 } // namespace
1231 void CGUIDialogVideoInfo::AddItemPathToFileBrowserSources(VECSOURCES& sources,
1232 const CFileItem& item)
1234 std::string itemDir = GetItemPathForBrowserSource(item);
1235 AddItemPathStringToFileBrowserSources(sources, itemDir, g_localizeStrings.Get(36041));
1238 int CGUIDialogVideoInfo::ManageVideoItem(const std::shared_ptr<CFileItem>& item)
1240 if (item == nullptr || !item->IsVideoDb() || !item->HasVideoInfoTag() || item->GetVideoInfoTag()->m_iDbId < 0)
1241 return -1;
1243 CVideoDatabase database;
1244 if (!database.Open())
1245 return -1;
1247 const std::string &type = item->GetVideoInfoTag()->m_type;
1248 int dbId = item->GetVideoInfoTag()->m_iDbId;
1250 CContextButtons buttons;
1251 if (type == MediaTypeMovie || type == MediaTypeVideoCollection ||
1252 type == MediaTypeTvShow || type == MediaTypeEpisode ||
1253 (type == MediaTypeSeason && item->GetVideoInfoTag()->m_iSeason > 0) || // seasons without "all seasons" and "specials"
1254 type == MediaTypeMusicVideo)
1255 buttons.Add(CONTEXT_BUTTON_EDIT, 16105);
1257 if (type == MediaTypeMovie || type == MediaTypeTvShow)
1258 buttons.Add(CONTEXT_BUTTON_EDIT_SORTTITLE, 16107);
1260 if (type == MediaTypeMovie)
1262 // only show link/unlink if there are tvshows available
1263 if (database.HasContent(VideoDbContentType::TVSHOWS))
1265 buttons.Add(CONTEXT_BUTTON_LINK_MOVIE, 20384);
1266 if (database.IsLinkedToTvshow(dbId))
1267 buttons.Add(CONTEXT_BUTTON_UNLINK_MOVIE, 20385);
1270 // set or change movie set the movie belongs to
1271 buttons.Add(CONTEXT_BUTTON_SET_MOVIESET, 20465);
1274 if (type == MediaTypeEpisode &&
1275 item->GetVideoInfoTag()->m_iBookmarkId > 0)
1276 buttons.Add(CONTEXT_BUTTON_UNLINK_BOOKMARK, 20405);
1278 // movie sets
1279 if (item->m_bIsFolder && type == MediaTypeVideoCollection)
1281 buttons.Add(CONTEXT_BUTTON_SET_MOVIESET_ART, 13511);
1282 buttons.Add(CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS, 20465);
1285 // seasons
1286 if (item->m_bIsFolder && type == MediaTypeSeason)
1287 buttons.Add(CONTEXT_BUTTON_SET_SEASON_ART, 13511);
1289 // tags
1290 if (item->m_bIsFolder && type == "tag")
1292 CVideoDbUrl videoUrl;
1293 if (videoUrl.FromString(item->GetPath()))
1295 const std::string &mediaType = videoUrl.GetItemType();
1297 buttons.Add(
1298 CONTEXT_BUTTON_TAGS_ADD_ITEMS,
1299 StringUtils::Format(g_localizeStrings.Get(20460), GetLocalizedVideoType(mediaType)));
1300 buttons.Add(CONTEXT_BUTTON_TAGS_REMOVE_ITEMS, StringUtils::Format(g_localizeStrings.Get(20461).c_str(), GetLocalizedVideoType(mediaType).c_str()));
1304 if (type != MediaTypeSeason)
1305 buttons.Add(CONTEXT_BUTTON_DELETE, 646);
1307 //temporary workaround until the context menu ids are removed
1308 const int addonItemOffset = 10000;
1310 auto addonItems = CServiceBroker::GetContextMenuManager().GetAddonItems(*item, CContextMenuManager::MANAGE);
1311 for (size_t i = 0; i < addonItems.size(); ++i)
1312 buttons.Add(addonItemOffset + i, addonItems[i]->GetLabel(*item));
1314 bool result = false;
1315 int button = CGUIDialogContextMenu::ShowAndGetChoice(buttons);
1316 if (button >= 0)
1318 switch (static_cast<CONTEXT_BUTTON>(button))
1320 case CONTEXT_BUTTON_EDIT:
1321 result = UpdateVideoItemTitle(item);
1322 break;
1324 case CONTEXT_BUTTON_EDIT_SORTTITLE:
1325 result = UpdateVideoItemSortTitle(item);
1326 break;
1328 case CONTEXT_BUTTON_LINK_MOVIE:
1329 result = LinkMovieToTvShow(item, false, database);
1330 break;
1332 case CONTEXT_BUTTON_UNLINK_MOVIE:
1333 result = LinkMovieToTvShow(item, true, database);
1334 break;
1336 case CONTEXT_BUTTON_SET_MOVIESET:
1338 CFileItemPtr selectedSet;
1339 if (GetSetForMovie(item.get(), selectedSet))
1340 result = SetMovieSet(item.get(), selectedSet.get());
1341 break;
1344 case CONTEXT_BUTTON_UNLINK_BOOKMARK:
1345 database.DeleteBookMarkForEpisode(*item->GetVideoInfoTag());
1346 result = true;
1347 break;
1349 case CONTEXT_BUTTON_DELETE:
1350 result = DeleteVideoItem(item);
1351 break;
1353 case CONTEXT_BUTTON_SET_MOVIESET_ART:
1354 case CONTEXT_BUTTON_SET_SEASON_ART:
1355 result = ManageVideoItemArtwork(item, type);
1356 break;
1358 case CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS:
1359 result = ManageMovieSets(item);
1360 break;
1362 case CONTEXT_BUTTON_TAGS_ADD_ITEMS:
1363 result = AddItemsToTag(item);
1364 break;
1366 case CONTEXT_BUTTON_TAGS_REMOVE_ITEMS:
1367 result = RemoveItemsFromTag(item);
1368 break;
1370 default:
1371 if (button >= addonItemOffset)
1372 result = CONTEXTMENU::LoopFrom(*addonItems[button - addonItemOffset], item);
1373 break;
1377 database.Close();
1379 if (result)
1380 return button;
1382 return -1;
1385 //Add change a title's name
1386 bool CGUIDialogVideoInfo::UpdateVideoItemTitle(const std::shared_ptr<CFileItem>& pItem)
1388 if (pItem == nullptr || !pItem->HasVideoInfoTag())
1389 return false;
1391 // dont allow update while scanning
1392 if (CVideoLibraryQueue::GetInstance().IsScanningLibrary())
1394 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{14057});
1395 return false;
1398 CVideoDatabase database;
1399 if (!database.Open())
1400 return false;
1402 int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
1403 MediaType mediaType = pItem->GetVideoInfoTag()->m_type;
1405 CVideoInfoTag detail;
1406 std::string title;
1407 if (mediaType == MediaTypeMovie)
1409 database.GetMovieInfo("", detail, iDbId, VideoDbDetailsNone);
1410 title = detail.m_strTitle;
1412 else if (mediaType == MediaTypeVideoCollection)
1414 database.GetSetInfo(iDbId, detail);
1415 title = detail.m_strTitle;
1417 else if (mediaType == MediaTypeEpisode)
1419 database.GetEpisodeInfo(pItem->GetPath(), detail, iDbId, VideoDbDetailsNone);
1420 title = detail.m_strTitle;
1422 else if (mediaType == MediaTypeSeason)
1424 database.GetSeasonInfo(iDbId, detail);
1425 title = detail.m_strSortTitle.empty() ? detail.m_strTitle : detail.m_strSortTitle;
1427 else if (mediaType == MediaTypeTvShow)
1429 database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId, 0, VideoDbDetailsNone);
1430 title = detail.m_strTitle;
1432 else if (mediaType == MediaTypeMusicVideo)
1434 database.GetMusicVideoInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId, VideoDbDetailsNone);
1435 title = detail.m_strTitle;
1438 // get the new title
1439 if (!CGUIKeyboardFactory::ShowAndGetInput(title, CVariant{ g_localizeStrings.Get(16105) }, false))
1440 return false;
1442 if (mediaType == MediaTypeSeason)
1444 detail.m_strSortTitle = title;
1445 std::map<std::string, std::string> artwork;
1446 database.SetDetailsForSeason(detail, artwork, detail.m_iIdShow, detail.m_iDbId);
1448 else
1450 detail.m_strTitle = title;
1451 VideoDbContentType iType = pItem->GetVideoContentType();
1452 database.UpdateMovieTitle(iDbId, detail.m_strTitle, iType);
1455 return true;
1458 bool CGUIDialogVideoInfo::CanDeleteVideoItem(const std::shared_ptr<CFileItem>& item)
1460 if (item == nullptr || !item->HasVideoInfoTag())
1461 return false;
1463 if (item->GetVideoInfoTag()->m_type == "tag")
1464 return true;
1466 CQueryParams params;
1467 CVideoDatabaseDirectory::GetQueryParams(item->GetPath(), params);
1469 return params.GetMovieId() != -1 ||
1470 params.GetEpisodeId() != -1 ||
1471 params.GetMVideoId() != -1 ||
1472 params.GetSetId() != -1 ||
1473 (params.GetTvShowId() != -1 && params.GetSeason() <= -1 &&
1474 !CVideoDatabaseDirectory::IsAllItem(item->GetPath()));
1477 bool CGUIDialogVideoInfo::DeleteVideoItemFromDatabase(const std::shared_ptr<CFileItem>& item,
1478 bool unavailable /* = false */)
1480 if (item == nullptr || !item->HasVideoInfoTag() ||
1481 !CanDeleteVideoItem(item))
1482 return false;
1484 // dont allow update while scanning
1485 if (CVideoLibraryQueue::GetInstance().IsScanningLibrary())
1487 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{14057});
1488 return false;
1491 CGUIDialogYesNo* pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogYesNo>(WINDOW_DIALOG_YES_NO);
1492 if (pDialog == nullptr)
1493 return false;
1495 int heading = -1;
1496 VideoDbContentType type = item->GetVideoContentType();
1497 const std::string& subtype = item->GetVideoInfoTag()->m_type;
1498 if (subtype != "tag")
1500 switch (type)
1502 case VideoDbContentType::MOVIES:
1503 heading = 432;
1504 break;
1505 case VideoDbContentType::EPISODES:
1506 heading = 20362;
1507 break;
1508 case VideoDbContentType::TVSHOWS:
1509 heading = 20363;
1510 break;
1511 case VideoDbContentType::MUSICVIDEOS:
1512 heading = 20392;
1513 break;
1514 case VideoDbContentType::MOVIE_SETS:
1515 heading = 646;
1516 break;
1517 default:
1518 return false;
1521 else
1523 heading = 10058;
1526 pDialog->SetHeading(CVariant{heading});
1528 if (unavailable)
1530 pDialog->SetLine(0, CVariant{g_localizeStrings.Get(662)});
1531 pDialog->SetLine(1, CVariant{g_localizeStrings.Get(663)});
1533 else
1535 pDialog->SetLine(0,
1536 CVariant{StringUtils::Format(g_localizeStrings.Get(433), item->GetLabel())});
1537 pDialog->SetLine(1, CVariant{""});
1539 pDialog->SetLine(2, CVariant{""});
1540 pDialog->Open();
1542 if (!pDialog->IsConfirmed())
1543 return false;
1545 CVideoDatabase database;
1546 database.Open();
1548 if (item->GetVideoInfoTag()->m_iDbId < 0)
1549 return false;
1551 if (subtype == "tag")
1553 database.DeleteTag(item->GetVideoInfoTag()->m_iDbId, type);
1554 return true;
1557 switch (type)
1559 case VideoDbContentType::MOVIES:
1560 database.DeleteMovie(item->GetVideoInfoTag()->m_iDbId);
1561 break;
1562 case VideoDbContentType::EPISODES:
1563 database.DeleteEpisode(item->GetVideoInfoTag()->m_iDbId);
1564 break;
1565 case VideoDbContentType::TVSHOWS:
1566 database.DeleteTvShow(item->GetVideoInfoTag()->m_iDbId);
1567 break;
1568 case VideoDbContentType::MUSICVIDEOS:
1569 database.DeleteMusicVideo(item->GetVideoInfoTag()->m_iDbId);
1570 break;
1571 case VideoDbContentType::MOVIE_SETS:
1572 database.DeleteSet(item->GetVideoInfoTag()->m_iDbId);
1573 break;
1574 default:
1575 return false;
1577 return true;
1580 bool CGUIDialogVideoInfo::DeleteVideoItem(const std::shared_ptr<CFileItem>& item,
1581 bool unavailable /* = false */)
1583 if (item == nullptr)
1584 return false;
1586 // delete the video item from the database
1587 if (!DeleteVideoItemFromDatabase(item, unavailable))
1588 return false;
1590 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
1592 // check if the user is allowed to delete the actual file as well
1593 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_ALLOWFILEDELETION) &&
1594 (profileManager->GetCurrentProfile().getLockMode() == LOCK_MODE_EVERYONE ||
1595 !profileManager->GetCurrentProfile().filesLocked() ||
1596 g_passwordManager.IsMasterLockUnlocked(true)))
1598 std::string strDeletePath = item->GetVideoInfoTag()->GetPath();
1600 if (StringUtils::EqualsNoCase(URIUtils::GetFileName(strDeletePath), "VIDEO_TS.IFO"))
1602 strDeletePath = URIUtils::GetDirectory(strDeletePath);
1603 if (StringUtils::EndsWithNoCase(strDeletePath, "video_ts/"))
1605 URIUtils::RemoveSlashAtEnd(strDeletePath);
1606 strDeletePath = URIUtils::GetDirectory(strDeletePath);
1609 if (URIUtils::HasSlashAtEnd(strDeletePath))
1610 item->m_bIsFolder = true;
1612 // check if the file/directory can be deleted
1613 if (CUtil::SupportsWriteFileOperations(strDeletePath))
1615 item->SetPath(strDeletePath);
1617 // HACK: stacked files need to be treated as folders in order to be deleted
1618 if (item->IsStack())
1619 item->m_bIsFolder = true;
1621 CGUIComponent *gui = CServiceBroker::GetGUI();
1622 if (gui && gui->ConfirmDelete(item->GetPath()))
1623 CFileUtils::DeleteItem(item);
1627 CUtil::DeleteVideoDatabaseDirectoryCache();
1629 return true;
1632 bool CGUIDialogVideoInfo::ManageMovieSets(const std::shared_ptr<CFileItem>& item)
1634 if (item == nullptr)
1635 return false;
1637 CFileItemList originalItems;
1638 CFileItemList selectedItems;
1640 if (!GetMoviesForSet(item.get(), originalItems, selectedItems) ||
1641 selectedItems.Size() == 0) // need at least one item selected
1642 return false;
1644 VECFILEITEMS original = originalItems.GetList();
1645 std::sort(original.begin(), original.end(), compFileItemsByDbId);
1646 VECFILEITEMS selected = selectedItems.GetList();
1647 std::sort(selected.begin(), selected.end(), compFileItemsByDbId);
1649 bool refreshNeeded = false;
1650 // update the "added" items
1651 VECFILEITEMS addedItems;
1652 set_difference(selected.begin(),selected.end(), original.begin(),original.end(), std::back_inserter(addedItems), compFileItemsByDbId);
1653 for (VECFILEITEMS::const_iterator it = addedItems.begin(); it != addedItems.end(); ++it)
1655 if (SetMovieSet(it->get(), item.get()))
1656 refreshNeeded = true;
1659 // update the "deleted" items
1660 CFileItemPtr clearItem(new CFileItem());
1661 clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
1662 VECFILEITEMS deletedItems;
1663 set_difference(original.begin(),original.end(), selected.begin(),selected.end(), std::back_inserter(deletedItems), compFileItemsByDbId);
1664 for (VECFILEITEMS::iterator it = deletedItems.begin(); it != deletedItems.end(); ++it)
1666 if (SetMovieSet(it->get(), clearItem.get()))
1667 refreshNeeded = true;
1670 return refreshNeeded;
1673 bool CGUIDialogVideoInfo::GetMoviesForSet(const CFileItem *setItem, CFileItemList &originalMovies, CFileItemList &selectedMovies)
1675 if (setItem == nullptr || !setItem->HasVideoInfoTag())
1676 return false;
1678 CVideoDatabase videodb;
1679 if (!videodb.Open())
1680 return false;
1682 std::string baseDir =
1683 StringUtils::Format("videodb://movies/sets/{}", setItem->GetVideoInfoTag()->m_iDbId);
1685 if (!CDirectory::GetDirectory(baseDir, originalMovies, "", DIR_FLAG_DEFAULTS) ||
1686 originalMovies.Size() <= 0) // keep a copy of the original members of the set
1687 return false;
1689 CFileItemList listItems;
1690 if (!videodb.GetSortedVideos(MediaTypeMovie, "videodb://movies", SortDescription(), listItems) || listItems.Size() <= 0)
1691 return false;
1693 CGUIDialogSelect *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
1694 if (dialog == nullptr)
1695 return false;
1697 listItems.Sort(SortByLabel, SortOrderAscending, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING) ? SortAttributeIgnoreArticle : SortAttributeNone);
1699 dialog->Reset();
1700 dialog->SetMultiSelection(true);
1701 dialog->SetHeading(CVariant{g_localizeStrings.Get(20457)});
1702 dialog->SetItems(listItems);
1703 std::vector<int> selectedIndices;
1704 for (int i = 0; i < originalMovies.Size(); i++)
1706 for (int listIndex = 0; listIndex < listItems.Size(); listIndex++)
1708 if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == originalMovies[i]->GetVideoInfoTag()->m_iDbId)
1710 selectedIndices.push_back(listIndex);
1711 break;
1715 dialog->SetSelected(selectedIndices);
1716 dialog->EnableButton(true, 186);
1717 dialog->Open();
1719 if (dialog->IsConfirmed())
1721 for (int i : dialog->GetSelectedItems())
1722 selectedMovies.Add(listItems.Get(i));
1723 return (selectedMovies.Size() > 0);
1725 else
1726 return false;
1729 bool CGUIDialogVideoInfo::GetSetForMovie(const CFileItem* movieItem,
1730 std::shared_ptr<CFileItem>& selectedSet)
1732 if (movieItem == nullptr || !movieItem->HasVideoInfoTag())
1733 return false;
1735 CVideoDatabase videodb;
1736 if (!videodb.Open())
1737 return false;
1739 CFileItemList listItems;
1741 // " ignoreSingleMovieSets=false " as an option in the url is needed here
1742 // to override the gui-setting "Include sets containing a single movie"
1743 // and retrieve all moviesets
1745 std::string baseDir = "videodb://movies/sets/?ignoreSingleMovieSets=false";
1747 if (!CDirectory::GetDirectory(baseDir, listItems, "", DIR_FLAG_DEFAULTS))
1748 return false;
1749 listItems.Sort(SortByLabel, SortOrderAscending, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING) ? SortAttributeIgnoreArticle : SortAttributeNone);
1751 int currentSetId = 0;
1752 std::string currentSetLabel;
1754 if (movieItem->GetVideoInfoTag()->m_set.id > currentSetId)
1756 currentSetId = movieItem->GetVideoInfoTag()->m_set.id;
1757 currentSetLabel = videodb.GetSetById(currentSetId);
1760 if (currentSetId > 0)
1762 // remove duplicate entry
1763 for (int listIndex = 0; listIndex < listItems.Size(); listIndex++)
1765 if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == currentSetId)
1767 listItems.Remove(listIndex);
1768 break;
1771 // add clear item
1772 std::string strClear = StringUtils::Format(g_localizeStrings.Get(20467), currentSetLabel);
1773 CFileItemPtr clearItem(new CFileItem(strClear));
1774 clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
1775 listItems.AddFront(clearItem, 0);
1776 // add keep current set item
1777 std::string strKeep = StringUtils::Format(g_localizeStrings.Get(20469), currentSetLabel);
1778 CFileItemPtr keepItem(new CFileItem(strKeep));
1779 keepItem->GetVideoInfoTag()->m_iDbId = currentSetId;
1780 listItems.AddFront(keepItem, 1);
1783 CGUIDialogSelect *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
1784 if (dialog == nullptr)
1785 return false;
1787 dialog->Reset();
1788 dialog->SetHeading(CVariant{g_localizeStrings.Get(20466)});
1789 dialog->SetItems(listItems);
1790 if (currentSetId >= 0)
1792 for (int listIndex = 0; listIndex < listItems.Size(); listIndex++)
1794 if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == currentSetId)
1796 dialog->SetSelected(listIndex);
1797 break;
1801 dialog->EnableButton(true, 20468); // new set via button
1802 dialog->Open();
1804 if (dialog->IsButtonPressed())
1805 { // creating new set
1806 std::string newSetTitle;
1807 if (!CGUIKeyboardFactory::ShowAndGetInput(newSetTitle, CVariant{g_localizeStrings.Get(20468)}, false))
1808 return false;
1809 int idSet = videodb.AddSet(newSetTitle);
1810 std::map<std::string, std::string> movieArt, setArt;
1811 if (!videodb.GetArtForItem(idSet, MediaTypeVideoCollection, setArt))
1813 videodb.GetArtForItem(movieItem->GetVideoInfoTag()->m_iDbId, MediaTypeMovie, movieArt);
1814 videodb.SetArtForItem(idSet, MediaTypeVideoCollection, movieArt);
1816 CFileItemPtr newSet(new CFileItem(newSetTitle));
1817 newSet->GetVideoInfoTag()->m_iDbId = idSet;
1818 selectedSet = newSet;
1819 return true;
1821 else if (dialog->IsConfirmed())
1823 selectedSet = dialog->GetSelectedFileItem();
1824 return (selectedSet != nullptr);
1826 else
1827 return false;
1830 bool CGUIDialogVideoInfo::SetMovieSet(const CFileItem *movieItem, const CFileItem *selectedSet)
1832 if (movieItem == nullptr || !movieItem->HasVideoInfoTag() ||
1833 selectedSet == nullptr || !selectedSet->HasVideoInfoTag())
1834 return false;
1836 CVideoDatabase videodb;
1837 if (!videodb.Open())
1838 return false;
1840 videodb.SetMovieSet(movieItem->GetVideoInfoTag()->m_iDbId, selectedSet->GetVideoInfoTag()->m_iDbId);
1841 return true;
1844 bool CGUIDialogVideoInfo::GetItemsForTag(const std::string &strHeading, const std::string &type, CFileItemList &items, int idTag /* = -1 */, bool showAll /* = true */)
1846 CVideoDatabase videodb;
1847 if (!videodb.Open())
1848 return false;
1850 MediaType mediaType = MediaTypeNone;
1851 std::string baseDir = "videodb://";
1852 std::string idColumn;
1853 if (type.compare(MediaTypeMovie) == 0)
1855 mediaType = MediaTypeMovie;
1856 baseDir += "movies";
1857 idColumn = "idMovie";
1859 else if (type.compare(MediaTypeTvShow) == 0)
1861 mediaType = MediaTypeTvShow;
1862 baseDir += "tvshows";
1863 idColumn = "idShow";
1865 else if (type.compare(MediaTypeMusicVideo) == 0)
1867 mediaType = MediaTypeMusicVideo;
1868 baseDir += "musicvideos";
1869 idColumn = "idMVideo";
1872 baseDir += "/titles/";
1873 CVideoDbUrl videoUrl;
1874 if (!videoUrl.FromString(baseDir))
1875 return false;
1877 CVideoDatabase::Filter filter;
1878 if (idTag > 0)
1880 if (!showAll)
1881 videoUrl.AddOption("tagid", idTag);
1882 else
1883 filter.where = videodb.PrepareSQL("%s_view.%s NOT IN (SELECT tag_link.media_id FROM tag_link WHERE tag_link.tag_id = %d AND tag_link.media_type = '%s')", type.c_str(), idColumn.c_str(), idTag, type.c_str());
1886 CFileItemList listItems;
1887 if (!videodb.GetSortedVideos(mediaType, videoUrl.ToString(), SortDescription(), listItems, filter) || listItems.Size() <= 0)
1888 return false;
1890 CGUIDialogSelect *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
1891 if (dialog == nullptr)
1892 return false;
1894 listItems.Sort(SortByLabel, SortOrderAscending, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING) ? SortAttributeIgnoreArticle : SortAttributeNone);
1896 dialog->Reset();
1897 dialog->SetMultiSelection(true);
1898 dialog->SetHeading(CVariant{strHeading});
1899 dialog->SetItems(listItems);
1900 dialog->EnableButton(true, 186);
1901 dialog->Open();
1903 for (int i : dialog->GetSelectedItems())
1904 items.Add(listItems.Get(i));
1905 return items.Size() > 0;
1908 bool CGUIDialogVideoInfo::AddItemsToTag(const std::shared_ptr<CFileItem>& tagItem)
1910 if (tagItem == nullptr || !tagItem->HasVideoInfoTag())
1911 return false;
1913 CVideoDbUrl videoUrl;
1914 if (!videoUrl.FromString(tagItem->GetPath()))
1915 return false;
1917 CVideoDatabase videodb;
1918 if (!videodb.Open())
1919 return true;
1921 std::string mediaType = videoUrl.GetItemType();
1922 mediaType = mediaType.substr(0, mediaType.length() - 1);
1924 CFileItemList items;
1925 std::string localizedType = GetLocalizedVideoType(mediaType);
1926 std::string strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType);
1927 if (!GetItemsForTag(strLabel, mediaType, items, tagItem->GetVideoInfoTag()->m_iDbId))
1928 return true;
1930 for (int index = 0; index < items.Size(); index++)
1932 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1933 continue;
1935 videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, tagItem->GetVideoInfoTag()->m_iDbId, mediaType);
1938 return true;
1941 bool CGUIDialogVideoInfo::RemoveItemsFromTag(const std::shared_ptr<CFileItem>& tagItem)
1943 if (tagItem == nullptr || !tagItem->HasVideoInfoTag())
1944 return false;
1946 CVideoDbUrl videoUrl;
1947 if (!videoUrl.FromString(tagItem->GetPath()))
1948 return false;
1950 CVideoDatabase videodb;
1951 if (!videodb.Open())
1952 return true;
1954 std::string mediaType = videoUrl.GetItemType();
1955 mediaType = mediaType.substr(0, mediaType.length() - 1);
1957 CFileItemList items;
1958 std::string localizedType = GetLocalizedVideoType(mediaType);
1959 std::string strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType);
1960 if (!GetItemsForTag(strLabel, mediaType, items, tagItem->GetVideoInfoTag()->m_iDbId, false))
1961 return true;
1963 for (int index = 0; index < items.Size(); index++)
1965 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1966 continue;
1968 videodb.RemoveTagFromItem(items[index]->GetVideoInfoTag()->m_iDbId, tagItem->GetVideoInfoTag()->m_iDbId, mediaType);
1971 return true;
1974 namespace
1976 std::string FindLocalMovieSetArtworkFile(const CFileItemPtr& item, const std::string& artType)
1978 std::string infoFolder = VIDEO::CVideoInfoScanner::GetMovieSetInfoFolder(item->GetLabel());
1979 if (infoFolder.empty())
1980 return "";
1982 CFileItemList availableArtFiles;
1983 CDirectory::GetDirectory(infoFolder, availableArtFiles,
1984 CServiceBroker::GetFileExtensionProvider().GetPictureExtensions(),
1985 DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_READ_CACHE | DIR_FLAG_NO_FILE_INFO);
1986 for (const auto& artFile : availableArtFiles)
1988 std::string candidate = URIUtils::GetFileName(artFile->GetPath());
1989 URIUtils::RemoveExtension(candidate);
1990 if (StringUtils::EqualsNoCase(candidate, artType))
1991 return artFile->GetPath();
1993 return "";
1995 } // namespace
1997 bool CGUIDialogVideoInfo::ManageVideoItemArtwork(const std::shared_ptr<CFileItem>& item,
1998 const MediaType& type)
2000 if (item == nullptr || !item->HasVideoInfoTag() || type.empty())
2001 return false;
2003 CVideoDatabase videodb;
2004 if (!videodb.Open())
2005 return true;
2007 // Grab the thumbnails from the web
2008 CFileItemList items;
2009 CFileItemPtr noneitem(new CFileItem("thumb://None", false));
2010 std::string currentThumb;
2011 int idArtist = -1;
2012 std::string artistPath;
2013 std::string artistOldPath;
2014 std::string artType = "thumb";
2015 if (type == MediaTypeArtist)
2017 CMusicDatabase musicdb;
2018 if (musicdb.Open())
2020 idArtist = musicdb.GetArtistByName(item->GetLabel()); // Fails when name not unique
2021 if (idArtist >= 0 )
2023 // Get artist paths - possible locations for thumb - while music db open
2024 musicdb.GetOldArtistPath(idArtist, artistOldPath); // Old artist path, local to music files
2025 CArtist artist;
2026 musicdb.GetArtist(idArtist, artist); // Need name and mbid for artist folder name
2027 musicdb.GetArtistPath(artist, artistPath); // Artist path in artist info folder
2029 currentThumb = musicdb.GetArtForItem(idArtist, MediaTypeArtist, "thumb");
2030 if (currentThumb.empty())
2031 currentThumb = videodb.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType);
2035 else if (type == "actor")
2036 currentThumb = videodb.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType);
2037 else
2038 { // SEASON, SET
2039 artType = ChooseArtType(*item);
2040 if (artType.empty())
2041 return false;
2043 if (artType == "fanart" && type != MediaTypeVideoCollection)
2044 return OnGetFanart(item);
2046 if (item->HasArt(artType))
2047 currentThumb = item->GetArt(artType);
2048 else if ((artType == "poster" || artType == "banner") && item->HasArt("thumb"))
2049 currentThumb = item->GetArt("thumb");
2052 if (!currentThumb.empty())
2054 CFileItemPtr item(new CFileItem("thumb://Current", false));
2055 item->SetArt("thumb", currentThumb);
2056 item->SetLabel(g_localizeStrings.Get(13512));
2057 items.Add(item);
2059 noneitem->SetArt("icon", "DefaultFolder.png");
2060 noneitem->SetLabel(g_localizeStrings.Get(13515));
2062 bool local = false;
2063 std::vector<std::string> thumbs;
2064 if (type != MediaTypeArtist)
2066 CVideoInfoTag tag;
2067 if (type == MediaTypeSeason)
2069 videodb.GetTvShowInfo("", tag, item->GetVideoInfoTag()->m_iIdShow);
2070 tag.m_strPictureURL.Parse();
2071 tag.m_strPictureURL.GetThumbUrls(thumbs, artType, item->GetVideoInfoTag()->m_iSeason);
2073 else if (type == MediaTypeVideoCollection)
2075 CFileItemList items;
2076 std::string baseDir =
2077 StringUtils::Format("videodb://movies/sets/{}", item->GetVideoInfoTag()->m_iDbId);
2078 if (videodb.GetMoviesNav(baseDir, items))
2080 for (int i=0; i < items.Size(); i++)
2082 CVideoInfoTag* pTag = items[i]->GetVideoInfoTag();
2083 pTag->m_strPictureURL.Parse();
2084 pTag->m_strPictureURL.GetThumbUrls(thumbs, "set." + artType, -1, true);
2088 else
2090 tag = *item->GetVideoInfoTag();
2091 tag.m_strPictureURL.Parse();
2092 tag.m_strPictureURL.GetThumbUrls(thumbs, artType);
2095 for (size_t i = 0; i < thumbs.size(); i++)
2097 CFileItemPtr item(new CFileItem(StringUtils::Format("thumb://Remote{0}", i), false));
2098 item->SetArt("thumb", thumbs[i]);
2099 item->SetArt("icon", "DefaultPicture.png");
2100 item->SetLabel(g_localizeStrings.Get(13513));
2101 items.Add(item);
2103 //! @todo Do we need to clear the cached image?
2104 // CServiceBroker::GetTextureCache()->ClearCachedImage(thumbs[i]);
2107 if (type == "actor")
2109 std::string picturePath;
2110 std::string strThumb = URIUtils::AddFileToFolder(picturePath, "folder.jpg");
2111 if (CFileUtils::Exists(strThumb))
2113 CFileItemPtr pItem(new CFileItem(strThumb,false));
2114 pItem->SetLabel(g_localizeStrings.Get(13514));
2115 pItem->SetArt("thumb", strThumb);
2116 items.Add(pItem);
2117 local = true;
2119 else
2120 noneitem->SetArt("icon", "DefaultActor.png");
2123 if (type == MediaTypeVideoCollection)
2125 std::string localFile = FindLocalMovieSetArtworkFile(item, artType);
2126 if (!localFile.empty())
2128 CFileItemPtr pItem(new CFileItem(localFile, false));
2129 pItem->SetLabel(g_localizeStrings.Get(13514));
2130 pItem->SetArt("thumb", localFile);
2131 items.Add(pItem);
2132 local = true;
2134 else
2135 noneitem->SetArt("icon", "DefaultVideo.png");
2138 else
2140 std::string strThumb;
2141 bool existsThumb = false;
2142 // First look for artist thumb in the primary location
2143 if (!artistPath.empty())
2145 strThumb = URIUtils::AddFileToFolder(artistPath, "folder.jpg");
2146 existsThumb = CFileUtils::Exists(strThumb);
2148 // If not there fall back local to music files (historic location for those album artists with a unique folder)
2149 if (!existsThumb && !artistOldPath.empty())
2151 strThumb = URIUtils::AddFileToFolder(artistOldPath, "folder.jpg");
2152 existsThumb = CFileUtils::Exists(strThumb);
2155 if (existsThumb)
2157 CFileItemPtr pItem(new CFileItem(strThumb, false));
2158 pItem->SetLabel(g_localizeStrings.Get(13514));
2159 pItem->SetArt("thumb", strThumb);
2160 items.Add(pItem);
2161 local = true;
2163 else
2164 noneitem->SetArt("icon", "DefaultArtist.png");
2167 if (!local)
2168 items.Add(noneitem);
2170 std::string result;
2171 VECSOURCES sources=*CMediaSourceSettings::GetInstance().GetSources("video");
2172 CServiceBroker::GetMediaManager().GetLocalDrives(sources);
2173 if (type == MediaTypeVideoCollection)
2175 AddItemPathStringToFileBrowserSources(sources,
2176 VIDEO::CVideoInfoScanner::GetMovieSetInfoFolder(item->GetLabel()),
2177 g_localizeStrings.Get(36041));
2178 AddItemPathStringToFileBrowserSources(sources,
2179 CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
2180 CSettings::SETTING_VIDEOLIBRARY_MOVIESETSFOLDER),
2181 "* " + g_localizeStrings.Get(20226));
2183 else
2184 AddItemPathToFileBrowserSources(sources, *item);
2185 if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result))
2186 return false; // user cancelled
2188 if (result == "thumb://Current")
2189 result = currentThumb; // user chose the one they have
2191 // delete the thumbnail if that's what the user wants, else overwrite with the
2192 // new thumbnail
2193 if (result == "thumb://None")
2194 result.clear();
2195 else if (StringUtils::StartsWith(result, "thumb://Remote"))
2197 int number = atoi(StringUtils::Mid(result, 14).c_str());
2198 result = thumbs[number];
2201 // write the selected artwork to the database
2202 if (type == MediaTypeVideoCollection ||
2203 type == "actor" ||
2204 type == MediaTypeSeason ||
2205 (type == MediaTypeArtist && idArtist < 0))
2206 videodb.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType, result);
2207 else
2209 CMusicDatabase musicdb;
2210 if (musicdb.Open())
2211 musicdb.SetArtForItem(idArtist, MediaTypeArtist, artType, result);
2214 item->SetArt(artType, result);
2216 CUtil::DeleteVideoDatabaseDirectoryCache();
2217 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
2218 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
2220 return true;
2223 std::string CGUIDialogVideoInfo::GetLocalizedVideoType(const std::string &strType)
2225 if (CMediaTypes::IsMediaType(strType, MediaTypeMovie))
2226 return g_localizeStrings.Get(20342);
2227 else if (CMediaTypes::IsMediaType(strType, MediaTypeTvShow))
2228 return g_localizeStrings.Get(20343);
2229 else if (CMediaTypes::IsMediaType(strType, MediaTypeEpisode))
2230 return g_localizeStrings.Get(20359);
2231 else if (CMediaTypes::IsMediaType(strType, MediaTypeMusicVideo))
2232 return g_localizeStrings.Get(20391);
2234 return "";
2237 bool CGUIDialogVideoInfo::UpdateVideoItemSortTitle(const std::shared_ptr<CFileItem>& pItem)
2239 // dont allow update while scanning
2240 if (CVideoLibraryQueue::GetInstance().IsScanningLibrary())
2242 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{14057});
2243 return false;
2246 CVideoDatabase database;
2247 if (!database.Open())
2248 return false;
2250 int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
2251 CVideoInfoTag detail;
2252 VideoDbContentType iType = pItem->GetVideoContentType();
2253 if (iType == VideoDbContentType::MOVIES)
2254 database.GetMovieInfo("", detail, iDbId, VideoDbDetailsNone);
2255 else if (iType == VideoDbContentType::TVSHOWS)
2256 database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId, 0, VideoDbDetailsNone);
2258 std::string currentTitle;
2259 if (detail.m_strSortTitle.empty())
2260 currentTitle = detail.m_strTitle;
2261 else
2262 currentTitle = detail.m_strSortTitle;
2264 // get the new sort title
2265 if (!CGUIKeyboardFactory::ShowAndGetInput(currentTitle, CVariant{g_localizeStrings.Get(16107)}, false))
2266 return false;
2268 return database.UpdateVideoSortTitle(iDbId, currentTitle, iType);
2271 bool CGUIDialogVideoInfo::LinkMovieToTvShow(const std::shared_ptr<CFileItem>& item,
2272 bool bRemove,
2273 CVideoDatabase& database)
2275 int dbId = item->GetVideoInfoTag()->m_iDbId;
2277 CFileItemList list;
2278 if (bRemove)
2280 std::vector<int> ids;
2281 if (!database.GetLinksToTvShow(dbId, ids))
2282 return false;
2284 for (unsigned int i = 0; i < ids.size(); ++i)
2286 CVideoInfoTag tag;
2287 database.GetTvShowInfo("", tag, ids[i], 0 , VideoDbDetailsNone);
2288 CFileItemPtr show(new CFileItem(tag));
2289 list.Add(show);
2292 else
2294 database.GetTvShowsNav("videodb://tvshows/titles", list);
2296 // remove already linked shows
2297 std::vector<int> ids;
2298 if (!database.GetLinksToTvShow(dbId, ids))
2299 return false;
2301 for (int i = 0; i < list.Size(); )
2303 size_t j;
2304 for (j = 0; j < ids.size(); ++j)
2306 if (list[i]->GetVideoInfoTag()->m_iDbId == ids[j])
2307 break;
2309 if (j == ids.size())
2310 i++;
2311 else
2312 list.Remove(i);
2316 int iSelectedLabel = 0;
2317 if (list.Size() > 1 || (!bRemove && !list.IsEmpty()))
2319 list.Sort(SortByLabel, SortOrderAscending, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING) ? SortAttributeIgnoreArticle : SortAttributeNone);
2320 CGUIDialogSelect* pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
2321 if (pDialog)
2323 pDialog->Reset();
2324 pDialog->SetItems(list);
2325 pDialog->SetHeading(CVariant{20356});
2326 pDialog->Open();
2327 iSelectedLabel = pDialog->GetSelectedItem();
2331 if (iSelectedLabel > -1 && iSelectedLabel < list.Size())
2332 return database.LinkMovieToTvshow(dbId, list[iSelectedLabel]->GetVideoInfoTag()->m_iDbId, bRemove);
2334 return false;
2337 bool CGUIDialogVideoInfo::OnGetFanart(const std::shared_ptr<CFileItem>& videoItem)
2339 if (videoItem == nullptr || !videoItem->HasVideoInfoTag())
2340 return false;
2342 // update the db
2343 CVideoDatabase videodb;
2344 if (!videodb.Open())
2345 return false;
2347 CVideoThumbLoader loader;
2348 CFileItem item(*videoItem);
2349 loader.LoadItem(&item);
2351 CFileItemList items;
2352 if (item.HasArt("fanart"))
2354 CFileItemPtr itemCurrent(new CFileItem("fanart://Current", false));
2355 itemCurrent->SetArt("thumb", item.GetArt("fanart"));
2356 itemCurrent->SetLabel(g_localizeStrings.Get(20440));
2357 items.Add(itemCurrent);
2360 // add the none option
2362 CFileItemPtr itemNone(new CFileItem("fanart://None", false));
2363 itemNone->SetArt("icon", "DefaultVideo.png");
2364 itemNone->SetLabel(g_localizeStrings.Get(20439));
2365 items.Add(itemNone);
2368 std::string result;
2369 VECSOURCES sources(*CMediaSourceSettings::GetInstance().GetSources("video"));
2370 CServiceBroker::GetMediaManager().GetLocalDrives(sources);
2371 AddItemPathToFileBrowserSources(sources, item);
2372 bool flip = false;
2373 if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) ||
2374 StringUtils::EqualsNoCase(result, "fanart://Current"))
2375 return false;
2377 else if (StringUtils::EqualsNoCase(result, "fanart://None") || !CFileUtils::Exists(result))
2378 result.clear();
2379 if (!result.empty() && flip)
2380 result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
2382 videodb.SetArtForItem(item.GetVideoInfoTag()->m_iDbId, item.GetVideoInfoTag()->m_type, "fanart", result);
2384 // clear view cache and reload images
2385 CUtil::DeleteVideoDatabaseDirectoryCache();
2387 return true;
2390 void CGUIDialogVideoInfo::ShowFor(const CFileItem& item)
2392 auto window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowVideoNav>(WINDOW_VIDEO_NAV);
2393 if (window)
2395 ADDON::ScraperPtr info;
2396 window->OnItemInfo(item, info);