[cosmetic] AddVideoAsset function cleanup
[xbmc.git] / xbmc / video / dialogs / GUIDialogVideoInfo.cpp
blobdadb84b87b0332c7baa38f0e1a2cb651734f68ef
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 "FileItemList.h"
14 #include "GUIPassword.h"
15 #include "GUIUserMessages.h"
16 #include "ServiceBroker.h"
17 #include "TextureCache.h"
18 #include "Util.h"
19 #include "dialogs/GUIDialogBusy.h"
20 #include "dialogs/GUIDialogFileBrowser.h"
21 #include "dialogs/GUIDialogProgress.h"
22 #include "dialogs/GUIDialogSelect.h"
23 #include "dialogs/GUIDialogYesNo.h"
24 #include "filesystem/Directory.h"
25 #include "filesystem/VideoDatabaseDirectory.h"
26 #include "filesystem/VideoDatabaseDirectory/QueryParams.h"
27 #include "guilib/GUIComponent.h"
28 #include "guilib/GUIImage.h"
29 #include "guilib/GUIKeyboardFactory.h"
30 #include "guilib/GUIWindow.h"
31 #include "guilib/GUIWindowManager.h"
32 #include "guilib/LocalizeStrings.h"
33 #include "imagefiles/ImageFileURL.h"
34 #include "input/actions/Action.h"
35 #include "input/actions/ActionIDs.h"
36 #include "messaging/helpers/DialogOKHelper.h"
37 #include "music/MusicDatabase.h"
38 #include "music/dialogs/GUIDialogMusicInfo.h"
39 #include "playlists/PlayListTypes.h"
40 #include "profiles/ProfileManager.h"
41 #include "settings/AdvancedSettings.h"
42 #include "settings/MediaSourceSettings.h"
43 #include "settings/SettingUtils.h"
44 #include "settings/Settings.h"
45 #include "settings/SettingsComponent.h"
46 #include "settings/lib/Setting.h"
47 #include "storage/MediaManager.h"
48 #include "threads/IRunnable.h"
49 #include "utils/ContentUtils.h"
50 #include "utils/FileUtils.h"
51 #include "utils/SortUtils.h"
52 #include "utils/StringUtils.h"
53 #include "utils/URIUtils.h"
54 #include "utils/Variant.h"
55 #include "video/VideoDbUrl.h"
56 #include "video/VideoFileItemClassify.h"
57 #include "video/VideoInfoScanner.h"
58 #include "video/VideoInfoTag.h"
59 #include "video/VideoItemArtworkHandler.h"
60 #include "video/VideoLibraryQueue.h"
61 #include "video/VideoThumbLoader.h"
62 #include "video/VideoUtils.h"
63 #include "video/dialogs/GUIDialogVideoManagerExtras.h"
64 #include "video/dialogs/GUIDialogVideoManagerVersions.h"
65 #include "video/guilib/VideoGUIUtils.h"
66 #include "video/guilib/VideoPlayActionProcessor.h"
67 #include "video/guilib/VideoVersionHelper.h"
68 #include "video/windows/GUIWindowVideoNav.h"
70 #include <algorithm>
71 #include <iterator>
72 #include <string>
73 #include <unordered_map>
75 using namespace XFILE::VIDEODATABASEDIRECTORY;
76 using namespace XFILE;
77 using namespace KODI;
78 using namespace KODI::MESSAGING;
80 #define CONTROL_IMAGE 3
81 #define CONTROL_TEXTAREA 4
82 #define CONTROL_BTN_TRACKS 5
83 #define CONTROL_BTN_REFRESH 6
84 #define CONTROL_BTN_USERRATING 7
85 #define CONTROL_BTN_PLAY 8
86 #define CONTROL_BTN_RESUME 9
87 #define CONTROL_BTN_GET_THUMB 10
88 #define CONTROL_BTN_PLAY_TRAILER 11
89 #define CONTROL_BTN_GET_FANART 12
90 #define CONTROL_BTN_DIRECTOR 13
91 #define CONTROL_BTN_MANAGE_VIDEO_VERSIONS 14
92 #define CONTROL_BTN_MANAGE_VIDEO_EXTRAS 15
94 #define CONTROL_LIST 50
96 // predicate used by sorting and set_difference
97 bool compFileItemsByDbId(const CFileItemPtr& lhs, const CFileItemPtr& rhs)
99 return lhs->HasVideoInfoTag() && rhs->HasVideoInfoTag() && lhs->GetVideoInfoTag()->m_iDbId < rhs->GetVideoInfoTag()->m_iDbId;
102 CGUIDialogVideoInfo::CGUIDialogVideoInfo(void)
103 : CGUIDialog(WINDOW_DIALOG_VIDEO_INFO, "DialogVideoInfo.xml"),
104 m_movieItem(new CFileItem),
105 m_castList(new CFileItemList)
107 m_loadType = KEEP_IN_MEMORY;
110 CGUIDialogVideoInfo::~CGUIDialogVideoInfo(void)
112 delete m_castList;
115 bool CGUIDialogVideoInfo::OnMessage(CGUIMessage& message)
117 switch ( message.GetMessage() )
119 case GUI_MSG_WINDOW_DEINIT:
121 ClearCastList();
123 if (m_startUserrating != m_movieItem->GetVideoInfoTag()->m_iUserRating)
125 CVideoDatabase db;
126 if (db.Open())
128 m_hasUpdatedUserrating = true;
129 db.SetVideoUserRating(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_iUserRating, m_movieItem->GetVideoInfoTag()->m_type);
130 db.Close();
134 break;
136 case GUI_MSG_CLICKED:
138 int iControl = message.GetSenderId();
139 if (iControl == CONTROL_BTN_REFRESH)
141 if (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeTvShow)
143 bool bCanceled=false;
144 if (CGUIDialogYesNo::ShowAndGetInput(CVariant{20377}, CVariant{20378}, bCanceled, CVariant{ "" }, CVariant{ "" }, CGUIDialogYesNo::NO_TIMEOUT))
146 m_bRefreshAll = true;
147 CVideoDatabase db;
148 if (db.Open())
150 db.SetPathHash(m_movieItem->GetVideoInfoTag()->m_strPath,"");
151 db.Close();
154 else
155 m_bRefreshAll = false;
157 if (bCanceled)
158 return false;
160 m_bRefresh = true;
161 Close();
162 return true;
164 else if (iControl == CONTROL_BTN_TRACKS)
166 m_bViewReview = !m_bViewReview;
167 Update();
169 else if (iControl == CONTROL_BTN_MANAGE_VIDEO_VERSIONS)
171 m_hasUpdatedItems = OnManageVideoVersions();
173 else if (iControl == CONTROL_BTN_MANAGE_VIDEO_EXTRAS)
175 m_hasUpdatedItems = OnManageVideoExtras();
177 else if (iControl == CONTROL_BTN_PLAY)
179 Play();
181 else if (iControl == CONTROL_BTN_USERRATING)
183 OnSetUserrating();
185 else if (iControl == CONTROL_BTN_RESUME)
187 Play(true);
189 else if (iControl == CONTROL_BTN_GET_THUMB)
191 OnGetArt();
193 else if (iControl == CONTROL_BTN_PLAY_TRAILER)
195 PlayTrailer();
197 else if (iControl == CONTROL_BTN_GET_FANART)
199 OnGetFanart();
201 else if (iControl == CONTROL_BTN_DIRECTOR)
203 auto directors = m_movieItem->GetVideoInfoTag()->m_director;
204 if (directors.size() == 0)
205 return true;
206 if (directors.size() == 1)
207 OnSearch(directors[0]);
208 else
210 auto pDlgSelect = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
211 if (pDlgSelect)
213 pDlgSelect->Reset();
214 pDlgSelect->SetHeading(CVariant{22080});
215 for (const auto &director: directors)
216 pDlgSelect->Add(director);
217 pDlgSelect->Open();
219 int iItem = pDlgSelect->GetSelectedItem();
220 if (iItem < 0)
221 return true;
222 OnSearch(directors[iItem]);
226 else if (iControl == CONTROL_LIST)
228 int iAction = message.GetParam1();
229 if (ACTION_SELECT_ITEM == iAction || ACTION_MOUSE_LEFT_CLICK == iAction)
231 CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl);
232 OnMessage(msg);
233 int iItem = msg.GetParam1();
234 if (iItem < 0 || iItem >= m_castList->Size())
235 break;
236 std::string strItem = m_castList->Get(iItem)->GetLabel();
237 if (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeVideoCollection)
239 SetMovie(m_castList->Get(iItem).get());
240 Close();
241 Open();
243 else
244 OnSearch(strItem);
248 break;
249 case GUI_MSG_NOTIFY_ALL:
251 if (IsActive() && message.GetParam1() == GUI_MSG_UPDATE_ITEM && message.GetItem())
253 CFileItemPtr item = std::static_pointer_cast<CFileItem>(message.GetItem());
254 if (item && m_movieItem->IsPath(item->GetPath()))
255 { // Just copy over the stream details and the thumb if we don't already have one
256 if (!m_movieItem->HasArt("thumb"))
257 m_movieItem->SetArt("thumb", item->GetArt("thumb"));
258 m_movieItem->GetVideoInfoTag()->m_streamDetails = item->GetVideoInfoTag()->m_streamDetails;
260 return true;
265 return CGUIDialog::OnMessage(message);
268 void CGUIDialogVideoInfo::OnInitWindow()
270 m_bRefresh = false;
271 m_bRefreshAll = true;
272 m_hasUpdatedThumb = false;
273 m_hasUpdatedUserrating = false;
274 m_bViewReview = true;
276 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
278 const std::string uniqueId = m_movieItem->GetProperty("xxuniqueid").asString();
279 if (uniqueId.empty() || !StringUtils::StartsWithNoCase(uniqueId.c_str(), "xx"))
280 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_REFRESH,
281 (profileManager->GetCurrentProfile().canWriteDatabases() ||
282 g_passwordManager.bMasterUser));
283 else
284 CONTROL_DISABLE(CONTROL_BTN_REFRESH);
286 // @todo add support to edit video asset art. Until then edit art through Versions Manager.
287 if (!VIDEO::IsVideoAssetFile(*m_movieItem))
288 CONTROL_ENABLE_ON_CONDITION(
289 CONTROL_BTN_GET_THUMB,
290 (profileManager->GetCurrentProfile().canWriteDatabases() ||
291 g_passwordManager.bMasterUser) &&
292 !StringUtils::StartsWithNoCase(m_movieItem->GetVideoInfoTag()->GetUniqueID().c_str(),
293 "plugin"));
294 else
295 CONTROL_DISABLE(CONTROL_BTN_GET_THUMB);
297 // Disable video user rating button for plugins and sets as they don't have tables to save this
298 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_USERRATING, !m_movieItem->IsPlugin() && m_movieItem->GetVideoInfoTag()->m_type != MediaTypeVideoCollection);
300 VideoDbContentType type = m_movieItem->GetVideoContentType();
301 if (type == VideoDbContentType::TVSHOWS || type == VideoDbContentType::MOVIES)
302 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_GET_FANART, (profileManager->
303 GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser) &&
304 !StringUtils::StartsWithNoCase(m_movieItem->GetVideoInfoTag()->
305 GetUniqueID().c_str(), "plugin"));
306 else
307 CONTROL_DISABLE(CONTROL_BTN_GET_FANART);
309 Update();
311 CGUIDialog::OnInitWindow();
314 bool CGUIDialogVideoInfo::OnAction(const CAction &action)
316 int userrating = m_movieItem->GetVideoInfoTag()->m_iUserRating;
317 if (action.GetID() == ACTION_INCREASE_RATING)
319 SetUserrating(userrating + 1);
320 return true;
322 else if (action.GetID() == ACTION_DECREASE_RATING)
324 SetUserrating(userrating - 1);
325 return true;
327 else if (action.GetID() == ACTION_SHOW_INFO)
329 Close();
330 return true;
332 return CGUIDialog::OnAction(action);
335 void CGUIDialogVideoInfo::SetUserrating(int userrating) const
337 userrating = std::max(userrating, 0);
338 userrating = std::min(userrating, 10);
339 if (userrating != m_movieItem->GetVideoInfoTag()->m_iUserRating)
341 m_movieItem->GetVideoInfoTag()->SetUserrating(userrating);
343 // send a message to all windows to tell them to update the fileitem (eg playlistplayer, media windows)
344 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_movieItem);
345 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
349 void CGUIDialogVideoInfo::SetMovie(const CFileItem *item)
351 *m_movieItem = *item;
353 // setup cast list
354 ClearCastList();
356 // When the scraper throws an error, the video tag can be null here
357 if (!item->HasVideoInfoTag())
358 return;
360 MediaType type = item->GetVideoInfoTag()->m_type;
362 m_startUserrating = m_movieItem->GetVideoInfoTag()->m_iUserRating;
364 if (type == MediaTypeMusicVideo)
365 { // music video
366 CMusicDatabase database;
367 database.Open();
368 const std::vector<std::string> &artists = m_movieItem->GetVideoInfoTag()->m_artist;
369 for (std::vector<std::string>::const_iterator it = artists.begin(); it != artists.end(); ++it)
371 int idArtist = database.GetArtistByName(*it);
372 std::string thumb = database.GetArtForItem(idArtist, MediaTypeArtist, "thumb");
373 CFileItemPtr item(new CFileItem(*it));
374 if (!thumb.empty())
375 item->SetArt("thumb", thumb);
376 item->SetArt("icon", "DefaultArtist.png");
377 item->SetLabel2(g_localizeStrings.Get(29904));
378 m_castList->Add(item);
380 // get performers in the music video (added as actors)
381 for (CVideoInfoTag::iCast it = m_movieItem->GetVideoInfoTag()->m_cast.begin();
382 it != m_movieItem->GetVideoInfoTag()->m_cast.end(); ++it)
384 // Check to see if we have already added this performer as the artist and skip adding if so
385 auto haveArtist = std::find(std::begin(artists), std::end(artists), it->strName);
386 if (haveArtist == artists.end()) // artist or performer not already in the list
388 CFileItemPtr item(new CFileItem(it->strName));
389 if (!it->thumb.empty())
390 item->SetArt("thumb", it->thumb);
391 else if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
392 CSettings::SETTING_VIDEOLIBRARY_ACTORTHUMBS))
393 { // backward compatibility
394 std::string thumb = CScraperUrl::GetThumbUrl(it->thumbUrl.GetFirstUrlByType());
395 if (!thumb.empty())
397 item->SetArt("thumb", thumb);
398 CServiceBroker::GetTextureCache()->BackgroundCacheImage(thumb);
401 item->SetArt("icon", "DefaultActor.png");
402 item->SetLabel(it->strName);
403 item->SetLabel2(it->strRole);
404 m_castList->Add(item);
408 else if (type == MediaTypeVideoCollection)
410 CVideoDatabase database;
411 database.Open();
412 database.GetMoviesNav(m_movieItem->GetPath(), *m_castList, -1, -1, -1, -1, -1, -1,
413 m_movieItem->GetVideoInfoTag()->m_set.id, -1,
414 SortDescription(), VideoDbDetailsAll);
415 m_castList->Sort(SortBySortTitle, SortOrderDescending);
416 CVideoThumbLoader loader;
417 for (auto& item : *m_castList)
418 loader.LoadItem(item.get());
420 else
421 { // movie/show/season/episode
422 for (CVideoInfoTag::iCast it = m_movieItem->GetVideoInfoTag()->m_cast.begin(); it != m_movieItem->GetVideoInfoTag()->m_cast.end(); ++it)
424 CFileItemPtr item(new CFileItem(it->strName));
425 if (!it->thumb.empty())
426 item->SetArt("thumb", it->thumb);
427 else
429 const std::shared_ptr<CSettings> settings =
430 CServiceBroker::GetSettingsComponent()->GetSettings();
431 if (settings->GetInt(CSettings::SETTING_VIDEOLIBRARY_ARTWORK_LEVEL) !=
432 CSettings::VIDEOLIBRARY_ARTWORK_LEVEL_NONE &&
433 settings->GetBool(CSettings::SETTING_VIDEOLIBRARY_ACTORTHUMBS))
434 { // backward compatibility
435 std::string thumb = CScraperUrl::GetThumbUrl(it->thumbUrl.GetFirstUrlByType());
436 if (!thumb.empty())
438 item->SetArt("thumb", thumb);
439 CServiceBroker::GetTextureCache()->BackgroundCacheImage(thumb);
443 item->SetArt("icon", "DefaultActor.png");
444 item->SetLabel(it->strName);
445 item->SetLabel2(it->strRole);
446 m_castList->Add(item);
450 if (type == MediaTypeMovie)
452 // local trailers should always override non-local, so check
453 // for a local one if the registered trailer is online
454 if (m_movieItem->GetVideoInfoTag()->m_strTrailer.empty() ||
455 URIUtils::IsInternetStream(m_movieItem->GetVideoInfoTag()->m_strTrailer))
457 std::string localTrailer = VIDEO::UTILS::FindTrailer(*m_movieItem);
458 if (!localTrailer.empty())
460 m_movieItem->GetVideoInfoTag()->m_strTrailer = localTrailer;
461 CVideoDatabase database;
462 if (database.Open())
464 database.SetSingleValue(VideoDbContentType::MOVIES, VIDEODB_ID_TRAILER,
465 m_movieItem->GetVideoInfoTag()->m_iDbId,
466 m_movieItem->GetVideoInfoTag()->m_strTrailer);
467 database.Close();
468 CUtil::DeleteVideoDatabaseDirectoryCache();
474 m_castList->SetContent(CMediaTypes::ToPlural(type));
476 CVideoThumbLoader loader;
477 loader.LoadItem(m_movieItem.get());
480 void CGUIDialogVideoInfo::Update()
482 // setup plot text area
483 std::shared_ptr<CSettingList> setting(std::dynamic_pointer_cast<CSettingList>(
484 CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(CSettings::SETTING_VIDEOLIBRARY_SHOWUNWATCHEDPLOTS)));
485 std::string strTmp = m_movieItem->GetVideoInfoTag()->m_strPlot;
486 if (m_movieItem->GetVideoInfoTag()->m_type != MediaTypeTvShow)
487 if (m_movieItem->GetVideoInfoTag()->GetPlayCount() == 0 && setting &&
488 ((m_movieItem->GetVideoInfoTag()->m_type == MediaTypeMovie &&
489 !CSettingUtils::FindIntInList(setting,
490 CSettings::VIDEOLIBRARY_PLOTS_SHOW_UNWATCHED_MOVIES)) ||
491 (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeEpisode &&
492 !CSettingUtils::FindIntInList(
493 setting, CSettings::VIDEOLIBRARY_PLOTS_SHOW_UNWATCHED_TVSHOWEPISODES))))
494 strTmp = g_localizeStrings.Get(20370);
496 StringUtils::Trim(strTmp);
497 SetLabel(CONTROL_TEXTAREA, strTmp);
499 CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST, 0, 0, m_castList);
500 OnMessage(msg);
502 if (GetControl(CONTROL_BTN_TRACKS)) // if no CONTROL_BTN_TRACKS found - allow skinner full visibility control over CONTROL_TEXTAREA and CONTROL_LIST
504 if (m_bViewReview)
506 if (!m_movieItem->GetVideoInfoTag()->m_artist.empty())
508 SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 133);
510 else if (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeVideoCollection)
512 SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 20342);
514 else if (m_movieItem->GetVideoInfoTag()->m_type == MediaTypeSeason)
516 SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 20360); // Episodes
518 else
520 SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 206);
523 SET_CONTROL_HIDDEN(CONTROL_LIST);
524 SET_CONTROL_VISIBLE(CONTROL_TEXTAREA);
526 else
528 SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 207);
530 SET_CONTROL_HIDDEN(CONTROL_TEXTAREA);
531 SET_CONTROL_VISIBLE(CONTROL_LIST);
535 // Check for resumability
536 if (m_movieItem->GetVideoInfoTag()->GetResumePoint().IsPartWay())
537 CONTROL_ENABLE(CONTROL_BTN_RESUME);
538 else
539 CONTROL_DISABLE(CONTROL_BTN_RESUME);
541 CONTROL_ENABLE(CONTROL_BTN_PLAY);
543 // update the thumbnail
544 CGUIControl* pControl = GetControl(CONTROL_IMAGE);
545 if (pControl)
547 CGUIImage* pImageControl = static_cast<CGUIImage*>(pControl);
548 pImageControl->FreeResources();
549 pImageControl->SetFileName(m_movieItem->GetArt("thumb"));
551 // tell our GUI to completely reload all controls (as some of them
552 // are likely to have had this image in use so will need refreshing)
553 if (m_hasUpdatedThumb)
555 CGUIMessage reload(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
556 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(reload);
560 bool CGUIDialogVideoInfo::NeedRefresh() const
562 return m_bRefresh;
565 bool CGUIDialogVideoInfo::RefreshAll() const
567 return m_bRefreshAll;
570 void CGUIDialogVideoInfo::OnSearch(std::string& strSearch)
572 CGUIDialogProgress *progress = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS);
573 if (progress)
575 progress->SetHeading(CVariant{194});
576 progress->SetLine(0, CVariant{strSearch});
577 progress->SetLine(1, CVariant{""});
578 progress->SetLine(2, CVariant{""});
579 progress->Open();
580 progress->Progress();
582 CFileItemList items;
583 DoSearch(strSearch, items);
585 if (progress)
586 progress->Close();
588 if (items.Size())
590 CGUIDialogSelect* pDlgSelect = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
591 if (pDlgSelect)
593 pDlgSelect->Reset();
594 pDlgSelect->SetHeading(CVariant{283});
596 CVideoThumbLoader loader;
597 for (int i = 0; i < items.Size(); i++)
599 if (items[i]->HasVideoInfoTag() &&
600 items[i]->GetVideoInfoTag()->GetPlayCount() > 0)
601 items[i]->SetLabel2(g_localizeStrings.Get(16102));
603 loader.LoadItem(items[i].get());
604 pDlgSelect->Add(*items[i]);
607 pDlgSelect->SetUseDetails(true);
608 pDlgSelect->Open();
610 int iItem = pDlgSelect->GetSelectedItem();
611 if (iItem < 0)
612 return;
614 OnSearchItemFound(items[iItem].get());
617 else
619 HELPERS::ShowOKDialogText(CVariant{194}, CVariant{284});
623 void CGUIDialogVideoInfo::DoSearch(std::string& strSearch, CFileItemList& items) const
625 CVideoDatabase db;
626 if (!db.Open())
627 return;
629 CFileItemList movies;
630 db.GetMoviesByActor(strSearch, movies);
631 for (int i = 0; i < movies.Size(); ++i)
633 std::string label = movies[i]->GetVideoInfoTag()->m_strTitle;
634 if (movies[i]->GetVideoInfoTag()->HasYear())
635 label += StringUtils::Format(" ({})", movies[i]->GetVideoInfoTag()->GetYear());
636 movies[i]->SetLabel(label);
638 CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20338) + "] ", items);
640 db.GetTvShowsByActor(strSearch, movies);
641 for (int i = 0; i < movies.Size(); ++i)
643 std::string label = movies[i]->GetVideoInfoTag()->m_strShowTitle;
644 if (movies[i]->GetVideoInfoTag()->HasYear())
645 label += StringUtils::Format(" ({})", movies[i]->GetVideoInfoTag()->GetYear());
646 movies[i]->SetLabel(label);
648 CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20364) + "] ", items);
650 db.GetEpisodesByActor(strSearch, movies);
651 for (int i = 0; i < movies.Size(); ++i)
653 std::string label = movies[i]->GetVideoInfoTag()->m_strTitle + " (" + movies[i]->GetVideoInfoTag()->m_strShowTitle + ")";
654 movies[i]->SetLabel(label);
656 CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20359) + "] ", items);
658 db.GetMusicVideosByArtist(strSearch, movies);
659 for (int i = 0; i < movies.Size(); ++i)
661 std::string label = StringUtils::Join(movies[i]->GetVideoInfoTag()->m_artist, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator) + " - " + movies[i]->GetVideoInfoTag()->m_strTitle;
662 if (movies[i]->GetVideoInfoTag()->HasYear())
663 label += StringUtils::Format(" ({})", movies[i]->GetVideoInfoTag()->GetYear());
664 movies[i]->SetLabel(label);
666 CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20391) + "] ", items);
667 db.Close();
669 // Search for music albums by artist with name matching search string
670 CMusicDatabase music_database;
671 if (!music_database.Open())
672 return;
674 if (music_database.SearchAlbumsByArtistName(strSearch, movies))
676 for (int i = 0; i < movies.Size(); ++i)
678 // Set type so that video thumbloader handles album art
679 movies[i]->GetVideoInfoTag()->m_type = MediaTypeAlbum;
681 CGUIWindowVideoBase::AppendAndClearSearchItems(
682 movies, "[" + g_localizeStrings.Get(36918) + "] ", items);
684 music_database.Close();
687 void CGUIDialogVideoInfo::OnSearchItemFound(const CFileItem* pItem)
689 VideoDbContentType type = pItem->GetVideoContentType();
691 CVideoDatabase db;
692 if (!db.Open())
693 return;
695 CVideoInfoTag movieDetails;
696 if (type == VideoDbContentType::MOVIES)
697 db.GetMovieInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId,
698 pItem->GetVideoInfoTag()->GetAssetInfo().GetId());
699 if (type == VideoDbContentType::EPISODES)
700 db.GetEpisodeInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
701 if (type == VideoDbContentType::TVSHOWS)
703 bool hasInfo = false;
704 const CVideoInfoTag* videoTag = pItem->GetVideoInfoTag();
705 if (videoTag->m_type == MediaTypeSeason && videoTag->m_iSeason != -1)
706 hasInfo = db.GetSeasonInfo(videoTag->m_iIdSeason, movieDetails);
707 if (!hasInfo)
708 db.GetTvShowInfo(pItem->GetPath(), movieDetails, videoTag->m_iDbId);
710 if (type == VideoDbContentType::MUSICVIDEOS)
711 db.GetMusicVideoInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
712 db.Close();
713 if (type == VideoDbContentType::MUSICALBUMS)
715 Close();
716 CGUIDialogMusicInfo::ShowFor(const_cast<CFileItem*>(pItem));
717 return; // No video info to refresh so just close the window and go back to the fileitem list
720 CFileItem item(*pItem);
721 *item.GetVideoInfoTag() = movieDetails;
722 SetMovie(&item);
723 // refresh our window entirely
724 Close();
725 Open();
728 void CGUIDialogVideoInfo::ClearCastList()
730 CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_LIST);
731 OnMessage(msg);
732 m_castList->Clear();
735 namespace
737 class CVideoPlayActionProcessor : public VIDEO::GUILIB::CVideoPlayActionProcessorBase
739 public:
740 explicit CVideoPlayActionProcessor(const std::shared_ptr<CFileItem>& item)
741 : CVideoPlayActionProcessorBase(item)
745 protected:
746 bool OnResumeSelected() override
748 m_item->SetStartOffset(STARTOFFSET_RESUME);
749 Play();
750 return true;
753 bool OnPlaySelected() override
755 Play();
756 return true;
759 private:
760 void Play()
762 auto item{m_item};
763 if (item->m_bIsFolder && item->HasVideoVersions())
765 //! @todo get rid of "videos with versions as folder" hack!
766 item = std::make_shared<CFileItem>(*item);
767 item->m_bIsFolder = false;
770 item->SetProperty("playlist_type_hint", static_cast<int>(PLAYLIST::Id::TYPE_VIDEO));
771 const ContentUtils::PlayMode mode{item->GetProperty("CheckAutoPlayNextItem").asBoolean()
772 ? ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM
773 : ContentUtils::PlayMode::PLAY_ONLY_THIS};
774 VIDEO::UTILS::PlayItem(item, "", mode);
777 } // unnamed namespace
779 void CGUIDialogVideoInfo::Play(bool resume)
781 std::string strPath;
783 const CVideoInfoTag* videoTag = m_movieItem->GetVideoInfoTag();
784 if (videoTag->m_type == MediaTypeTvShow || videoTag->m_type == MediaTypeSeason)
786 if (m_movieItem->IsPlugin())
788 strPath = m_movieItem->GetPath();
789 Close();
790 if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VIDEO_NAV)
792 CGUIMessage message(GUI_MSG_NOTIFY_ALL, CServiceBroker::GetGUI()->
793 GetWindowManager().GetActiveWindow(), 0, GUI_MSG_UPDATE, 0);
794 message.SetStringParam(strPath);
795 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
796 return;
799 else if (videoTag->m_type == MediaTypeTvShow)
800 strPath = StringUtils::Format("videodb://tvshows/titles/{}/", videoTag->m_iDbId);
801 else // season
802 strPath = StringUtils::Format("videodb://tvshows/titles/{}/{}/", videoTag->m_iIdShow,
803 videoTag->m_iSeason);
805 else if (videoTag->m_type == MediaTypeVideoCollection)
807 strPath = StringUtils::Format("videodb://movies/sets/{}/?setid={}", videoTag->m_iDbId,
808 videoTag->m_iDbId);
811 if (!strPath.empty())
813 Close();
814 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_VIDEO_NAV, strPath);
815 return;
818 // close our dialog
819 Close(true);
821 // play the current video version, even if multiple versions are available
822 m_movieItem->SetProperty("has_resolved_video_asset", true);
824 if (resume)
826 CVideoPlayActionProcessor proc{m_movieItem};
827 proc.ProcessAction(VIDEO::GUILIB::ACTION_RESUME);
829 else
831 if (GetControl(CONTROL_BTN_RESUME))
833 // if dialog has a resume button, play button has always the purpose to start from beginning
834 CVideoPlayActionProcessor proc{m_movieItem};
835 proc.ProcessAction(VIDEO::GUILIB::ACTION_PLAY_FROM_BEGINNING);
837 else
839 // play button acts according to default play action setting
840 CVideoPlayActionProcessor proc{m_movieItem};
841 proc.ProcessDefaultAction();
842 if (proc.UserCancelled())
844 // The Resume dialog was closed without any choice
845 SetMovie(m_movieItem.get()); // restore cast list, which was cleared on dialog close
846 Open();
851 m_movieItem->ClearProperty("has_resolved_video_asset");
854 namespace
856 // Add art types required in Kodi and configured by the user
857 void AddHardCodedAndExtendedArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTag& tag)
859 for (const auto& artType : CVideoThumbLoader::GetArtTypes(tag.m_type))
861 if (std::find(artTypes.cbegin(), artTypes.cend(), artType) == artTypes.cend())
862 artTypes.emplace_back(artType);
866 // Add art types currently assigned to the media item
867 void AddCurrentArtTypes(std::vector<std::string>& artTypes,
868 const CVideoInfoTag& tag,
869 CVideoDatabase& db)
871 std::map<std::string, std::string> currentArt;
873 if (tag.GetAssetInfo().GetId() >= 0)
874 db.GetArtForAsset(tag.m_iFileId, ArtFallbackOptions::NONE, currentArt);
875 else
876 db.GetArtForItem(tag.m_iDbId, tag.m_type, currentArt);
878 for (const auto& art : currentArt)
880 if (!art.second.empty() &&
881 std::find(artTypes.cbegin(), artTypes.cend(), art.first) == artTypes.cend())
882 artTypes.emplace_back(art.first);
886 // Add art types that exist for other media items of the same type
887 void AddMediaTypeArtTypes(std::vector<std::string>& artTypes,
888 const CVideoInfoTag& tag,
889 CVideoDatabase& db)
891 std::vector<std::string> dbArtTypes;
892 db.GetArtTypes(tag.m_type, dbArtTypes);
893 for (const auto& artType : dbArtTypes)
895 if (std::find(artTypes.cbegin(), artTypes.cend(), artType) == artTypes.cend())
896 artTypes.emplace_back(artType);
900 // Add art types from available but unassigned artwork for this media item
901 void AddAvailableArtTypes(std::vector<std::string>& artTypes,
902 const CVideoInfoTag& tag,
903 CVideoDatabase& db)
905 for (const auto& artType : db.GetAvailableArtTypesForItem(tag.m_iDbId, tag.m_type))
907 if (std::find(artTypes.cbegin(), artTypes.cend(), artType) == artTypes.cend())
908 artTypes.emplace_back(artType);
912 std::vector<std::string> GetArtTypesList(const CVideoInfoTag& tag)
914 CVideoDatabase db;
915 db.Open();
917 std::vector<std::string> artTypes;
919 AddHardCodedAndExtendedArtTypes(artTypes, tag);
920 AddCurrentArtTypes(artTypes, tag, db);
921 AddMediaTypeArtTypes(artTypes, tag, db);
922 AddAvailableArtTypes(artTypes, tag, db);
924 db.Close();
925 return artTypes;
928 class CArtTypeChooser
930 public:
931 CArtTypeChooser() = delete;
932 explicit CArtTypeChooser(const std::shared_ptr<CFileItem>& item) : m_item(item) {}
934 bool ChooseArtType();
935 const std::string& GetArtType() const { return m_artType; }
936 void UpdateArtType(const std::string& type, const std::string& art) const;
938 private:
939 std::shared_ptr<CFileItem> m_item;
940 CFileItemList m_items;
941 int m_selectedItem{0};
942 std::string m_artType;
945 void CArtTypeChooser::UpdateArtType(const std::string& type, const std::string& art) const
947 m_item->SetArt(type, art);
948 if (!m_items.IsEmpty())
949 for (auto& item : m_items)
950 if (item->GetProperty("type") == type)
951 item->SetArt("thumb", art);
954 bool CArtTypeChooser::ChooseArtType()
956 CGUIDialogSelect* dialog =
957 CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
958 WINDOW_DIALOG_SELECT);
959 if (!dialog || !m_item->HasVideoInfoTag())
960 return false;
962 dialog->SetHeading(CVariant{13511});
963 dialog->Reset();
964 dialog->SetUseDetails(true);
965 dialog->EnableButton(true, 13516); // Enable "Add art type" button
967 if (m_items.IsEmpty())
969 const std::vector<std::string> availableArtTypes = GetArtTypesList(*m_item->GetVideoInfoTag());
971 // maps art types to resource ids
972 static const std::unordered_map<std::string, int> name2idMap = {
973 {"banner", 20020},
974 {"fanart", 20445},
975 {"poster", 20021},
976 {"thumb", 21371},
979 for (const auto& type : availableArtTypes)
981 const auto item = std::make_shared<CFileItem>(type, false);
982 item->SetProperty("type", type);
983 if (m_item->HasArt(type))
984 item->SetArt("thumb", m_item->GetArt(type));
986 const auto it = name2idMap.find(type);
987 item->SetLabel(it == name2idMap.cend() ? type : g_localizeStrings.Get((*it).second));
989 m_items.Add(item);
993 dialog->SetItems(m_items);
994 dialog->SetSelected(m_selectedItem);
995 dialog->Open();
996 m_selectedItem = dialog->GetSelectedItem();
998 if (dialog->IsButtonPressed())
1000 // "Add art type" button pressed. Get the new artwork name.
1001 std::string artworkName;
1002 if (!CGUIKeyboardFactory::ShowAndGetInput(artworkName, CVariant{g_localizeStrings.Get(13516)},
1003 false))
1004 return false;
1006 m_artType = artworkName;
1008 else
1010 // A type was selected from the list of available art types.
1011 m_artType = dialog->GetSelectedFileItem()->GetProperty("type").asString();
1014 return !m_artType.empty();
1016 } // unnamed namespace
1018 void CGUIDialogVideoInfo::OnGetArt()
1020 if (ChooseAndManageVideoItemArtwork(m_movieItem))
1022 m_hasUpdatedThumb = true;
1024 // Update our screen
1025 Update();
1029 void CGUIDialogVideoInfo::OnGetFanart()
1031 if (ManageVideoItemArtwork(m_movieItem, m_movieItem->GetVideoInfoTag()->m_type, "fanart"))
1033 m_hasUpdatedThumb = true;
1035 // Update our screen
1036 Update();
1040 void CGUIDialogVideoInfo::OnSetUserrating() const
1042 CGUIDialogSelect *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
1043 if (dialog)
1045 dialog->SetHeading(CVariant{ 38023 });
1046 dialog->Add(g_localizeStrings.Get(38022));
1047 for (int i = 1; i <= 10; i++)
1048 dialog->Add(StringUtils::Format("{}: {}", g_localizeStrings.Get(563), i));
1050 dialog->SetSelected(m_movieItem->GetVideoInfoTag()->m_iUserRating);
1052 dialog->Open();
1054 int iItem = dialog->GetSelectedItem();
1055 if (iItem < 0)
1056 return;
1058 SetUserrating(iItem);
1062 void CGUIDialogVideoInfo::PlayTrailer()
1064 Close(true);
1065 CGUIMessage msg(GUI_MSG_PLAY_TRAILER, 0, 0, 0, 0, m_movieItem);
1066 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
1069 void CGUIDialogVideoInfo::SetLabel(int iControl, const std::string &strLabel)
1071 if (strLabel.empty())
1073 SET_CONTROL_LABEL(iControl, 416); // "Not available"
1075 else
1077 SET_CONTROL_LABEL(iControl, strLabel);
1081 std::string CGUIDialogVideoInfo::GetThumbnail() const
1083 return m_movieItem->GetArt("thumb");
1086 int CGUIDialogVideoInfo::ManageVideoItem(const std::shared_ptr<CFileItem>& item)
1088 if (item == nullptr || !VIDEO::IsVideoDb(*item) || !item->HasVideoInfoTag() ||
1089 item->GetVideoInfoTag()->m_iDbId < 0)
1090 return -1;
1092 CVideoDatabase database;
1093 if (!database.Open())
1094 return -1;
1096 const std::string &type = item->GetVideoInfoTag()->m_type;
1097 int dbId = item->GetVideoInfoTag()->m_iDbId;
1099 CContextButtons buttons;
1100 if ((type == MediaTypeMovie && !VIDEO::IsVideoAssetFile(*item)) ||
1101 type == MediaTypeVideoCollection || type == MediaTypeTvShow || type == MediaTypeEpisode ||
1102 (type == MediaTypeSeason &&
1103 item->GetVideoInfoTag()->m_iSeason > 0) || // seasons without "all seasons" and "specials"
1104 type == MediaTypeMusicVideo)
1105 buttons.Add(CONTEXT_BUTTON_EDIT, 16105);
1107 if ((type == MediaTypeMovie && !VIDEO::IsVideoAssetFile(*item)) || type == MediaTypeTvShow ||
1108 type == MediaTypeSeason)
1109 buttons.Add(CONTEXT_BUTTON_EDIT_SORTTITLE, 16107);
1111 if (type == MediaTypeMovie && !VIDEO::IsVideoAssetFile(*item))
1113 // only show link/unlink if there are tvshows available
1114 if (database.HasContent(VideoDbContentType::TVSHOWS))
1116 buttons.Add(CONTEXT_BUTTON_LINK_MOVIE, 20384);
1117 if (database.IsLinkedToTvshow(dbId))
1118 buttons.Add(CONTEXT_BUTTON_UNLINK_MOVIE, 20385);
1121 // set or change movie set the movie belongs to
1122 buttons.Add(CONTEXT_BUTTON_SET_MOVIESET, 20465);
1125 if (type == MediaTypeMovie)
1127 // manage video versions
1128 buttons.Add(CONTEXT_BUTTON_MANAGE_VIDEOVERSIONS, 40001); // Manage versions
1131 if (type == MediaTypeEpisode &&
1132 item->GetVideoInfoTag()->m_iBookmarkId > 0)
1133 buttons.Add(CONTEXT_BUTTON_UNLINK_BOOKMARK, 20405);
1135 if (type == MediaTypeVideoCollection ||
1136 (type == MediaTypeMovie && !VIDEO::IsVideoAssetFile(*item)) || type == MediaTypeTvShow ||
1137 type == MediaTypeSeason || type == MediaTypeEpisode)
1138 buttons.Add(CONTEXT_BUTTON_SET_ART, 13511);
1140 // movie sets
1141 if (item->m_bIsFolder && type == MediaTypeVideoCollection)
1143 buttons.Add(CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS, 20465);
1146 // tags
1147 if (item->m_bIsFolder && type == "tag")
1149 CVideoDbUrl videoUrl;
1150 if (videoUrl.FromString(item->GetPath()))
1152 const std::string &mediaType = videoUrl.GetItemType();
1154 buttons.Add(
1155 CONTEXT_BUTTON_TAGS_ADD_ITEMS,
1156 StringUtils::Format(g_localizeStrings.Get(20460), GetLocalizedVideoType(mediaType)));
1157 buttons.Add(CONTEXT_BUTTON_TAGS_REMOVE_ITEMS, StringUtils::Format(g_localizeStrings.Get(20461).c_str(), GetLocalizedVideoType(mediaType).c_str()));
1161 if (type != MediaTypeSeason && !VIDEO::IsVideoAssetFile(*item))
1163 // Remove from library
1164 buttons.Add(CONTEXT_BUTTON_DELETE, 646);
1167 //temporary workaround until the context menu ids are removed
1168 const int addonItemOffset = 10000;
1170 auto addonItems = CServiceBroker::GetContextMenuManager().GetAddonItems(*item, CContextMenuManager::MANAGE);
1171 for (size_t i = 0; i < addonItems.size(); ++i)
1172 buttons.Add(addonItemOffset + i, addonItems[i]->GetLabel(*item));
1174 bool result = false;
1175 int button = CGUIDialogContextMenu::ShowAndGetChoice(buttons);
1176 if (button >= 0)
1178 switch (static_cast<CONTEXT_BUTTON>(button))
1180 case CONTEXT_BUTTON_EDIT:
1181 result = UpdateVideoItemTitle(item);
1182 break;
1184 case CONTEXT_BUTTON_EDIT_SORTTITLE:
1185 result = UpdateVideoItemSortTitle(item);
1186 break;
1188 case CONTEXT_BUTTON_LINK_MOVIE:
1189 result = LinkMovieToTvShow(item, false, database);
1190 break;
1192 case CONTEXT_BUTTON_UNLINK_MOVIE:
1193 result = LinkMovieToTvShow(item, true, database);
1194 break;
1196 case CONTEXT_BUTTON_SET_MOVIESET:
1198 CFileItemPtr selectedSet;
1199 if (GetSetForMovie(item.get(), selectedSet))
1200 result = SetMovieSet(item.get(), selectedSet.get());
1201 break;
1204 case CONTEXT_BUTTON_MANAGE_VIDEOVERSIONS:
1205 ManageVideoVersions(item);
1206 result = true;
1207 break;
1209 case CONTEXT_BUTTON_UNLINK_BOOKMARK:
1210 database.DeleteBookMarkForEpisode(*item->GetVideoInfoTag());
1211 result = true;
1212 break;
1214 case CONTEXT_BUTTON_DELETE:
1215 result = DeleteVideoItem(item);
1216 break;
1218 case CONTEXT_BUTTON_SET_ART:
1219 result = ChooseAndManageVideoItemArtwork(item);
1220 break;
1222 case CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS:
1223 result = ManageMovieSets(item);
1224 break;
1226 case CONTEXT_BUTTON_TAGS_ADD_ITEMS:
1227 result = AddItemsToTag(item);
1228 break;
1230 case CONTEXT_BUTTON_TAGS_REMOVE_ITEMS:
1231 result = RemoveItemsFromTag(item);
1232 break;
1234 default:
1235 if (button >= addonItemOffset)
1236 result = CONTEXTMENU::LoopFrom(*addonItems[button - addonItemOffset], item);
1237 break;
1241 database.Close();
1243 if (result)
1244 return button;
1246 return -1;
1249 //Add change a title's name
1250 bool CGUIDialogVideoInfo::UpdateVideoItemTitle(const std::shared_ptr<CFileItem>& pItem)
1252 if (pItem == nullptr || !pItem->HasVideoInfoTag())
1253 return false;
1255 // dont allow update while scanning
1256 if (CVideoLibraryQueue::GetInstance().IsScanningLibrary())
1258 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{14057});
1259 return false;
1262 CVideoDatabase database;
1263 if (!database.Open())
1264 return false;
1266 int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
1267 MediaType mediaType = pItem->GetVideoInfoTag()->m_type;
1269 CVideoInfoTag detail;
1270 std::string title;
1271 if (mediaType == MediaTypeMovie)
1273 database.GetMovieInfo("", detail, iDbId, pItem->GetVideoInfoTag()->GetAssetInfo().GetId(),
1274 VideoDbDetailsNone);
1275 title = detail.m_strTitle;
1277 else if (mediaType == MediaTypeVideoCollection)
1279 database.GetSetInfo(iDbId, detail);
1280 title = detail.m_strTitle;
1282 else if (mediaType == MediaTypeEpisode)
1284 database.GetEpisodeInfo(pItem->GetPath(), detail, iDbId, VideoDbDetailsNone);
1285 title = detail.m_strTitle;
1287 else if (mediaType == MediaTypeSeason)
1289 database.GetSeasonInfo(iDbId, detail);
1290 title = detail.m_strSortTitle.empty() ? detail.m_strTitle : detail.m_strSortTitle;
1292 else if (mediaType == MediaTypeTvShow)
1294 database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId, 0, VideoDbDetailsNone);
1295 title = detail.m_strTitle;
1297 else if (mediaType == MediaTypeMusicVideo)
1299 database.GetMusicVideoInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId, VideoDbDetailsNone);
1300 title = detail.m_strTitle;
1303 // get the new title
1304 if (!CGUIKeyboardFactory::ShowAndGetInput(title, CVariant{ g_localizeStrings.Get(16105) }, false))
1305 return false;
1307 if (mediaType == MediaTypeSeason)
1309 detail.m_strSortTitle = title;
1310 std::map<std::string, std::string> artwork;
1311 database.SetDetailsForSeason(detail, artwork, detail.m_iIdShow, detail.m_iDbId);
1313 else
1315 detail.m_strTitle = title;
1316 VideoDbContentType iType = pItem->GetVideoContentType();
1317 database.UpdateMovieTitle(iDbId, detail.m_strTitle, iType);
1320 return true;
1323 bool CGUIDialogVideoInfo::CanDeleteVideoItem(const std::shared_ptr<CFileItem>& item)
1325 if (item == nullptr || !item->HasVideoInfoTag())
1326 return false;
1328 if (item->GetVideoInfoTag()->m_type == "tag")
1329 return true;
1331 CQueryParams params;
1332 CVideoDatabaseDirectory::GetQueryParams(item->GetPath(), params);
1334 return params.GetMovieId() != -1 || params.GetEpisodeId() != -1 || params.GetMVideoId() != -1 ||
1335 params.GetSetId() != -1 || params.GetVideoVersionId() != -1 ||
1336 (params.GetTvShowId() != -1 && params.GetSeason() <= -1 &&
1337 !CVideoDatabaseDirectory::IsAllItem(item->GetPath()));
1340 bool CGUIDialogVideoInfo::DeleteVideoItemFromDatabase(const std::shared_ptr<CFileItem>& item,
1341 bool unavailable /* = false */)
1343 if (item == nullptr || !item->HasVideoInfoTag() ||
1344 !CanDeleteVideoItem(item))
1345 return false;
1347 // dont allow update while scanning
1348 if (CVideoLibraryQueue::GetInstance().IsScanningLibrary())
1350 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{14057});
1351 return false;
1354 CGUIDialogYesNo* pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogYesNo>(WINDOW_DIALOG_YES_NO);
1355 if (pDialog == nullptr)
1356 return false;
1358 int heading = -1;
1359 VideoDbContentType type = item->GetVideoContentType();
1360 const std::string& subtype = item->GetVideoInfoTag()->m_type;
1361 if (subtype != "tag")
1363 switch (type)
1365 case VideoDbContentType::MOVIES:
1366 heading = 432;
1367 break;
1368 case VideoDbContentType::EPISODES:
1369 heading = 20362;
1370 break;
1371 case VideoDbContentType::TVSHOWS:
1372 heading = 20363;
1373 break;
1374 case VideoDbContentType::MUSICVIDEOS:
1375 heading = 20392;
1376 break;
1377 case VideoDbContentType::MOVIE_SETS:
1378 heading = 646;
1379 break;
1380 default:
1381 return false;
1384 else
1386 heading = 10058;
1389 pDialog->SetHeading(CVariant{heading});
1391 if (unavailable)
1393 pDialog->SetLine(0, CVariant{g_localizeStrings.Get(662)});
1394 pDialog->SetLine(1, CVariant{g_localizeStrings.Get(663)});
1396 else
1398 pDialog->SetLine(
1399 0, CVariant{StringUtils::Format(
1400 g_localizeStrings.Get(item->HasVideoVersions() ? 40021 : 433), item->GetLabel())});
1401 pDialog->SetLine(1, CVariant{""});
1403 pDialog->SetLine(2, CVariant{""});
1404 pDialog->Open();
1406 if (!pDialog->IsConfirmed())
1407 return false;
1409 CVideoDatabase database;
1410 database.Open();
1412 if (item->GetVideoInfoTag()->m_iDbId < 0)
1413 return false;
1415 if (subtype == "tag")
1417 database.DeleteTag(item->GetVideoInfoTag()->m_iDbId, type);
1418 return true;
1421 switch (type)
1423 case VideoDbContentType::MOVIES:
1424 database.DeleteMovie(item->GetVideoInfoTag()->m_iDbId);
1425 break;
1426 case VideoDbContentType::EPISODES:
1427 database.DeleteEpisode(item->GetVideoInfoTag()->m_iDbId);
1428 break;
1429 case VideoDbContentType::TVSHOWS:
1430 database.DeleteTvShow(item->GetVideoInfoTag()->m_iDbId);
1431 break;
1432 case VideoDbContentType::MUSICVIDEOS:
1433 database.DeleteMusicVideo(item->GetVideoInfoTag()->m_iDbId);
1434 break;
1435 case VideoDbContentType::MOVIE_SETS:
1436 database.DeleteSet(item->GetVideoInfoTag()->m_iDbId);
1437 break;
1438 default:
1439 return false;
1441 return true;
1444 bool CGUIDialogVideoInfo::DeleteVideoItem(const std::shared_ptr<CFileItem>& item,
1445 bool unavailable /* = false */)
1447 if (item == nullptr)
1448 return false;
1450 // delete the video item from the database
1451 if (!DeleteVideoItemFromDatabase(item, unavailable))
1452 return false;
1454 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
1456 // check if the user is allowed to delete the actual file as well
1457 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_ALLOWFILEDELETION) &&
1458 (profileManager->GetCurrentProfile().getLockMode() == LOCK_MODE_EVERYONE ||
1459 !profileManager->GetCurrentProfile().filesLocked() ||
1460 g_passwordManager.IsMasterLockUnlocked(true)))
1462 std::string strDeletePath = item->GetVideoInfoTag()->GetPath();
1464 if (StringUtils::EqualsNoCase(URIUtils::GetFileName(strDeletePath), "VIDEO_TS.IFO"))
1466 strDeletePath = URIUtils::GetDirectory(strDeletePath);
1467 if (StringUtils::EndsWithNoCase(strDeletePath, "video_ts/"))
1469 URIUtils::RemoveSlashAtEnd(strDeletePath);
1470 strDeletePath = URIUtils::GetDirectory(strDeletePath);
1473 if (URIUtils::HasSlashAtEnd(strDeletePath))
1474 item->m_bIsFolder = true;
1476 // check if the file/directory can be deleted
1477 if (CUtil::SupportsWriteFileOperations(strDeletePath))
1479 item->SetPath(strDeletePath);
1481 // HACK: stacked files need to be treated as folders in order to be deleted
1482 if (item->IsStack())
1483 item->m_bIsFolder = true;
1485 CFileUtils::DeleteItemWithConfirm(item);
1489 CUtil::DeleteVideoDatabaseDirectoryCache();
1491 return true;
1494 bool CGUIDialogVideoInfo::ManageMovieSets(const std::shared_ptr<CFileItem>& item)
1496 if (item == nullptr)
1497 return false;
1499 CFileItemList originalItems;
1500 CFileItemList selectedItems;
1502 if (!GetMoviesForSet(item.get(), originalItems, selectedItems) ||
1503 selectedItems.Size() == 0) // need at least one item selected
1504 return false;
1506 VECFILEITEMS original = originalItems.GetList();
1507 std::sort(original.begin(), original.end(), compFileItemsByDbId);
1508 VECFILEITEMS selected = selectedItems.GetList();
1509 std::sort(selected.begin(), selected.end(), compFileItemsByDbId);
1511 bool refreshNeeded = false;
1512 // update the "added" items
1513 VECFILEITEMS addedItems;
1514 set_difference(selected.begin(),selected.end(), original.begin(),original.end(), std::back_inserter(addedItems), compFileItemsByDbId);
1515 for (VECFILEITEMS::const_iterator it = addedItems.begin(); it != addedItems.end(); ++it)
1517 if (SetMovieSet(it->get(), item.get()))
1518 refreshNeeded = true;
1521 // update the "deleted" items
1522 CFileItemPtr clearItem(new CFileItem());
1523 clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
1524 VECFILEITEMS deletedItems;
1525 set_difference(original.begin(),original.end(), selected.begin(),selected.end(), std::back_inserter(deletedItems), compFileItemsByDbId);
1526 for (VECFILEITEMS::iterator it = deletedItems.begin(); it != deletedItems.end(); ++it)
1528 if (SetMovieSet(it->get(), clearItem.get()))
1529 refreshNeeded = true;
1532 return refreshNeeded;
1535 bool CGUIDialogVideoInfo::GetMoviesForSet(const CFileItem *setItem, CFileItemList &originalMovies, CFileItemList &selectedMovies)
1537 if (setItem == nullptr || !setItem->HasVideoInfoTag())
1538 return false;
1540 CVideoDatabase videodb;
1541 if (!videodb.Open())
1542 return false;
1544 std::string baseDir =
1545 StringUtils::Format("videodb://movies/sets/{}", setItem->GetVideoInfoTag()->m_iDbId);
1547 if (!CDirectory::GetDirectory(baseDir, originalMovies, "", DIR_FLAG_DEFAULTS) ||
1548 originalMovies.Size() <= 0) // keep a copy of the original members of the set
1549 return false;
1551 CFileItemList listItems;
1552 if (!videodb.GetSortedVideos(MediaTypeMovie, "videodb://movies", SortDescription(), listItems) || listItems.Size() <= 0)
1553 return false;
1555 CGUIDialogSelect *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
1556 if (dialog == nullptr)
1557 return false;
1559 listItems.Sort(SortByLabel, SortOrderAscending, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING) ? SortAttributeIgnoreArticle : SortAttributeNone);
1561 dialog->Reset();
1562 dialog->SetMultiSelection(true);
1563 dialog->SetHeading(CVariant{g_localizeStrings.Get(20457)});
1564 dialog->SetItems(listItems);
1565 std::vector<int> selectedIndices;
1566 for (int i = 0; i < originalMovies.Size(); i++)
1568 for (int listIndex = 0; listIndex < listItems.Size(); listIndex++)
1570 if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == originalMovies[i]->GetVideoInfoTag()->m_iDbId)
1572 selectedIndices.push_back(listIndex);
1573 break;
1577 dialog->SetSelected(selectedIndices);
1578 dialog->EnableButton(true, 186);
1579 dialog->Open();
1581 if (dialog->IsConfirmed())
1583 for (int i : dialog->GetSelectedItems())
1584 selectedMovies.Add(listItems.Get(i));
1585 return (selectedMovies.Size() > 0);
1587 else
1588 return false;
1591 bool CGUIDialogVideoInfo::GetSetForMovie(const CFileItem* movieItem,
1592 std::shared_ptr<CFileItem>& selectedSet)
1594 if (movieItem == nullptr || !movieItem->HasVideoInfoTag())
1595 return false;
1597 CVideoDatabase videodb;
1598 if (!videodb.Open())
1599 return false;
1601 CFileItemList listItems;
1603 // " ignoreSingleMovieSets=false " as an option in the url is needed here
1604 // to override the gui-setting "Include sets containing a single movie"
1605 // and retrieve all moviesets
1607 std::string baseDir = "videodb://movies/sets/?ignoreSingleMovieSets=false";
1609 if (!CDirectory::GetDirectory(baseDir, listItems, "", DIR_FLAG_DEFAULTS))
1610 return false;
1611 listItems.Sort(SortByLabel, SortOrderAscending, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING) ? SortAttributeIgnoreArticle : SortAttributeNone);
1613 int currentSetId = 0;
1614 std::string currentSetLabel;
1616 if (movieItem->GetVideoInfoTag()->m_set.id > currentSetId)
1618 currentSetId = movieItem->GetVideoInfoTag()->m_set.id;
1619 currentSetLabel = videodb.GetSetById(currentSetId);
1622 if (currentSetId > 0)
1624 // remove duplicate entry
1625 for (int listIndex = 0; listIndex < listItems.Size(); listIndex++)
1627 if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == currentSetId)
1629 listItems.Remove(listIndex);
1630 break;
1633 // add clear item
1634 std::string strClear = StringUtils::Format(g_localizeStrings.Get(20467), currentSetLabel);
1635 CFileItemPtr clearItem(new CFileItem(strClear));
1636 clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
1637 listItems.AddFront(clearItem, 0);
1638 // add keep current set item
1639 std::string strKeep = StringUtils::Format(g_localizeStrings.Get(20469), currentSetLabel);
1640 CFileItemPtr keepItem(new CFileItem(strKeep));
1641 keepItem->GetVideoInfoTag()->m_iDbId = currentSetId;
1642 listItems.AddFront(keepItem, 1);
1645 CGUIDialogSelect *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
1646 if (dialog == nullptr)
1647 return false;
1649 dialog->Reset();
1650 dialog->SetHeading(CVariant{g_localizeStrings.Get(20466)});
1651 dialog->SetItems(listItems);
1652 if (currentSetId >= 0)
1654 for (int listIndex = 0; listIndex < listItems.Size(); listIndex++)
1656 if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == currentSetId)
1658 dialog->SetSelected(listIndex);
1659 break;
1663 dialog->EnableButton(true, 20468); // new set via button
1664 dialog->Open();
1666 if (dialog->IsButtonPressed())
1667 { // creating new set
1668 std::string newSetTitle;
1669 if (!CGUIKeyboardFactory::ShowAndGetInput(newSetTitle, CVariant{g_localizeStrings.Get(20468)}, false))
1670 return false;
1671 int idSet = videodb.AddSet(newSetTitle);
1672 std::map<std::string, std::string> movieArt, setArt;
1673 if (!videodb.GetArtForItem(idSet, MediaTypeVideoCollection, setArt))
1675 videodb.GetArtForItem(movieItem->GetVideoInfoTag()->m_iDbId, MediaTypeMovie, movieArt);
1676 videodb.SetArtForItem(idSet, MediaTypeVideoCollection, movieArt);
1678 CFileItemPtr newSet(new CFileItem(newSetTitle));
1679 newSet->GetVideoInfoTag()->m_iDbId = idSet;
1680 selectedSet = newSet;
1681 return true;
1683 else if (dialog->IsConfirmed())
1685 selectedSet = dialog->GetSelectedFileItem();
1686 return (selectedSet != nullptr);
1688 else
1689 return false;
1692 bool CGUIDialogVideoInfo::SetMovieSet(const CFileItem *movieItem, const CFileItem *selectedSet)
1694 if (movieItem == nullptr || !movieItem->HasVideoInfoTag() ||
1695 selectedSet == nullptr || !selectedSet->HasVideoInfoTag())
1696 return false;
1698 CVideoDatabase videodb;
1699 if (!videodb.Open())
1700 return false;
1702 videodb.SetMovieSet(movieItem->GetVideoInfoTag()->m_iDbId, selectedSet->GetVideoInfoTag()->m_iDbId);
1703 return true;
1706 bool CGUIDialogVideoInfo::GetItemsForTag(const std::string &strHeading, const std::string &type, CFileItemList &items, int idTag /* = -1 */, bool showAll /* = true */)
1708 CVideoDatabase videodb;
1709 if (!videodb.Open())
1710 return false;
1712 MediaType mediaType = MediaTypeNone;
1713 std::string baseDir = "videodb://";
1714 std::string idColumn;
1715 if (type.compare(MediaTypeMovie) == 0)
1717 mediaType = MediaTypeMovie;
1718 baseDir += "movies";
1719 idColumn = "idMovie";
1721 else if (type.compare(MediaTypeTvShow) == 0)
1723 mediaType = MediaTypeTvShow;
1724 baseDir += "tvshows";
1725 idColumn = "idShow";
1727 else if (type.compare(MediaTypeMusicVideo) == 0)
1729 mediaType = MediaTypeMusicVideo;
1730 baseDir += "musicvideos";
1731 idColumn = "idMVideo";
1734 baseDir += "/titles/";
1735 CVideoDbUrl videoUrl;
1736 if (!videoUrl.FromString(baseDir))
1737 return false;
1739 CVideoDatabase::Filter filter;
1740 if (idTag > 0)
1742 if (!showAll)
1743 videoUrl.AddOption("tagid", idTag);
1744 else
1745 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());
1748 CFileItemList listItems;
1749 if (!videodb.GetSortedVideos(mediaType, videoUrl.ToString(), SortDescription(), listItems, filter) || listItems.Size() <= 0)
1750 return false;
1752 CGUIDialogSelect *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
1753 if (dialog == nullptr)
1754 return false;
1756 listItems.Sort(SortByLabel, SortOrderAscending, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING) ? SortAttributeIgnoreArticle : SortAttributeNone);
1758 dialog->Reset();
1759 dialog->SetMultiSelection(true);
1760 dialog->SetHeading(CVariant{strHeading});
1761 dialog->SetItems(listItems);
1762 dialog->EnableButton(true, 186);
1763 dialog->Open();
1765 for (int i : dialog->GetSelectedItems())
1766 items.Add(listItems.Get(i));
1767 return items.Size() > 0;
1770 bool CGUIDialogVideoInfo::AddItemsToTag(const std::shared_ptr<CFileItem>& tagItem)
1772 if (tagItem == nullptr || !tagItem->HasVideoInfoTag())
1773 return false;
1775 CVideoDbUrl videoUrl;
1776 if (!videoUrl.FromString(tagItem->GetPath()))
1777 return false;
1779 CVideoDatabase videodb;
1780 if (!videodb.Open())
1781 return true;
1783 std::string mediaType = videoUrl.GetItemType();
1784 mediaType.pop_back();
1786 CFileItemList items;
1787 std::string localizedType = GetLocalizedVideoType(mediaType);
1788 std::string strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType);
1789 if (!GetItemsForTag(strLabel, mediaType, items, tagItem->GetVideoInfoTag()->m_iDbId))
1790 return true;
1792 for (int index = 0; index < items.Size(); index++)
1794 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1795 continue;
1797 videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, tagItem->GetVideoInfoTag()->m_iDbId, mediaType);
1800 return true;
1803 bool CGUIDialogVideoInfo::RemoveItemsFromTag(const std::shared_ptr<CFileItem>& tagItem)
1805 if (tagItem == nullptr || !tagItem->HasVideoInfoTag())
1806 return false;
1808 CVideoDbUrl videoUrl;
1809 if (!videoUrl.FromString(tagItem->GetPath()))
1810 return false;
1812 CVideoDatabase videodb;
1813 if (!videodb.Open())
1814 return true;
1816 std::string mediaType = videoUrl.GetItemType();
1817 mediaType.pop_back();
1819 CFileItemList items;
1820 std::string localizedType = GetLocalizedVideoType(mediaType);
1821 std::string strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType);
1822 if (!GetItemsForTag(strLabel, mediaType, items, tagItem->GetVideoInfoTag()->m_iDbId, false))
1823 return true;
1825 for (int index = 0; index < items.Size(); index++)
1827 if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1828 continue;
1830 videodb.RemoveTagFromItem(items[index]->GetVideoInfoTag()->m_iDbId, tagItem->GetVideoInfoTag()->m_iDbId, mediaType);
1833 return true;
1836 bool CGUIDialogVideoInfo::ChooseAndManageVideoItemArtwork(const std::shared_ptr<CFileItem>& item)
1838 bool result = false;
1840 CArtTypeChooser chooser{item};
1843 if (!chooser.ChooseArtType())
1844 break;
1846 const std::string chosenArtType{chooser.GetArtType()};
1847 result = ManageVideoItemArtwork(item, item->GetVideoInfoTag()->m_type, chosenArtType);
1848 if (result)
1849 chooser.UpdateArtType(chosenArtType, item->GetArt(chosenArtType));
1851 } while (true);
1853 return result;
1856 bool CGUIDialogVideoInfo::ManageVideoItemArtwork(const std::shared_ptr<CFileItem>& item,
1857 const std::string& mediaType)
1859 // When not selecting art type, default type to "thumb".
1860 return ManageVideoItemArtwork(item, mediaType, "thumb");
1863 namespace
1865 class CAsyncGetArt : private IRunnable
1867 public:
1868 CAsyncGetArt() = delete;
1869 CAsyncGetArt(const VIDEO::IVideoItemArtworkHandler& handler) : m_handler(handler) {}
1871 bool FetchAllArt()
1873 CGUIDialogBusy::Wait(this, 100, false);
1874 return true;
1877 const std::string& GetCurrentArt() const { return m_currentArt; }
1878 const std::string& GetEmbeddedArt() const { return m_embeddedArt; }
1879 const std::vector<std::string>& GetRemoteArt() const { return m_remoteArt; }
1880 const std::string& GetLocalArt() const { return m_localArt; }
1882 private:
1883 // IRunnable implementation
1884 void Run() override
1886 m_currentArt = m_handler.GetCurrentArt();
1887 m_embeddedArt = m_handler.GetEmbeddedArt();
1888 m_remoteArt = m_handler.GetRemoteArt();
1889 m_localArt = m_handler.GetLocalArt();
1892 const VIDEO::IVideoItemArtworkHandler& m_handler;
1894 // Note: No mutex needed to protect the strings, as correct usage sequence of this class is:
1895 // T1 calls FetchAllArt
1896 // T1 blocks in CGUIDialogBusy::Wait until strings are filled by worker thread T2 in Run()
1897 // T1: calls GetFooArt, which accesses the then completely filled strings
1898 std::string m_currentArt;
1899 std::string m_embeddedArt;
1900 std::vector<std::string> m_remoteArt;
1901 std::string m_localArt;
1903 } // unnamed namespace
1905 bool CGUIDialogVideoInfo::ManageVideoItemArtwork(const std::shared_ptr<CFileItem>& item,
1906 const MediaType& mediaType,
1907 const std::string& artType)
1909 if (item == nullptr || !item->HasVideoInfoTag() || mediaType.empty() || artType.empty())
1910 return false;
1912 const std::unique_ptr<VIDEO::IVideoItemArtworkHandler> artHandler =
1913 VIDEO::IVideoItemArtworkHandlerFactory::Create(item, mediaType, artType);
1914 CAsyncGetArt asyncArtHandler{*artHandler};
1915 asyncArtHandler.FetchAllArt();
1917 CFileItemList items;
1919 const std::string currentArt = asyncArtHandler.GetCurrentArt();
1920 if (!currentArt.empty())
1922 const auto itemCurrent = std::make_shared<CFileItem>("thumb://Current", false);
1923 itemCurrent->SetArt("thumb", currentArt);
1924 itemCurrent->SetLabel(g_localizeStrings.Get(13512));
1925 items.Add(itemCurrent);
1928 const std::string embeddedArt = asyncArtHandler.GetEmbeddedArt();
1929 if (!embeddedArt.empty())
1931 const auto itemEmbedded = std::make_shared<CFileItem>("thumb://Embedded", false);
1932 itemEmbedded->SetArt("thumb", embeddedArt);
1933 itemEmbedded->SetLabel(g_localizeStrings.Get(13519));
1934 items.Add(itemEmbedded);
1937 const std::vector<std::string> remoteArt = asyncArtHandler.GetRemoteArt();
1938 for (size_t i = 0; i < remoteArt.size(); ++i)
1940 const auto itemRemote =
1941 std::make_shared<CFileItem>(StringUtils::Format("thumb://Remote{0}", i), false);
1942 itemRemote->SetArt("thumb", remoteArt[i]);
1943 itemRemote->SetArt("icon", "DefaultPicture.png");
1944 itemRemote->SetLabel(g_localizeStrings.Get(13513));
1945 items.Add(itemRemote);
1947 //! @todo Do we need to clear the cached image?
1948 // CServiceBroker::GetTextureCache()->ClearCachedImage(remoteArt[i]);
1951 const std::string localArt = asyncArtHandler.GetLocalArt();
1952 if (!localArt.empty())
1954 const auto itemLocal = std::make_shared<CFileItem>("thumb://Local", false);
1955 itemLocal->SetLabel(g_localizeStrings.Get(13514));
1956 itemLocal->SetArt("thumb", localArt);
1957 items.Add(itemLocal);
1960 const auto itemNone = std::make_shared<CFileItem>("thumb://None", false);
1961 itemNone->SetLabel(g_localizeStrings.Get(13515));
1962 itemNone->SetArt("icon", artHandler->GetDefaultIcon());
1963 items.Add(itemNone);
1965 std::string result;
1966 std::vector<CMediaSource> sources = *CMediaSourceSettings::GetInstance().GetSources("video");
1967 CServiceBroker::GetMediaManager().GetLocalDrives(sources);
1968 artHandler->AddItemPathToFileBrowserSources(sources);
1970 bool flip = false;
1971 if (!CGUIDialogFileBrowser::ShowAndGetImage(
1972 items, sources, g_localizeStrings.Get(13511) /* Choose art */, result,
1973 artHandler->SupportsFlippedArt() ? &flip : nullptr, 39123 /* Artwork */))
1974 return false; // user cancelled
1976 if (result == "thumb://Current")
1977 result = currentArt; // user chose the one they have
1979 if (result == "thumb://Local")
1980 result = localArt;
1982 if (result == "thumb://Embedded")
1983 result = artHandler->UpdateEmbeddedArt(embeddedArt);
1985 // delete the thumbnail if that's what the user wants, else overwrite with the
1986 // new thumbnail
1987 if (result == "thumb://None")
1989 result.clear();
1991 else if (StringUtils::StartsWith(result, "thumb://Remote"))
1993 const int index = std::atoi(StringUtils::Mid(result, 14).c_str());
1994 result = artHandler->UpdateRemoteArt(remoteArt, index);
1997 // flip selected image, if user wants it
1998 if (!result.empty() && flip)
2000 auto file = IMAGE_FILES::CImageFileURL::FromFile(result);
2001 file.flipped = true;
2002 result = file.ToString();
2005 // write the selected artwork to the database
2006 artHandler->PersistArt(result);
2008 item->SetArt(artType, result);
2010 if (item->HasProperty("set_folder_thumb"))
2012 // have a folder thumb to set as well
2013 VIDEO::CVideoInfoScanner::ApplyThumbToFolder(item->GetProperty("set_folder_thumb").asString(),
2014 result);
2017 CUtil::DeleteVideoDatabaseDirectoryCache();
2018 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
2019 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
2021 return true;
2024 std::string CGUIDialogVideoInfo::GetLocalizedVideoType(const std::string &strType)
2026 if (CMediaTypes::IsMediaType(strType, MediaTypeMovie))
2027 return g_localizeStrings.Get(20342);
2028 else if (CMediaTypes::IsMediaType(strType, MediaTypeTvShow))
2029 return g_localizeStrings.Get(20343);
2030 else if (CMediaTypes::IsMediaType(strType, MediaTypeEpisode))
2031 return g_localizeStrings.Get(20359);
2032 else if (CMediaTypes::IsMediaType(strType, MediaTypeMusicVideo))
2033 return g_localizeStrings.Get(20391);
2035 return "";
2038 bool CGUIDialogVideoInfo::UpdateVideoItemSortTitle(const std::shared_ptr<CFileItem>& pItem)
2040 // dont allow update while scanning
2041 if (CVideoLibraryQueue::GetInstance().IsScanningLibrary())
2043 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{14057});
2044 return false;
2047 CVideoDatabase database;
2048 if (!database.Open())
2049 return false;
2051 int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
2052 CVideoInfoTag detail;
2053 VideoDbContentType iType = pItem->GetVideoContentType();
2054 if (iType == VideoDbContentType::MOVIES)
2055 database.GetMovieInfo("", detail, iDbId, pItem->GetVideoInfoTag()->GetAssetInfo().GetId(),
2056 VideoDbDetailsNone);
2057 else if (iType == VideoDbContentType::TVSHOWS)
2058 database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId, 0, VideoDbDetailsNone);
2060 std::string currentTitle;
2061 if (detail.m_strSortTitle.empty())
2062 currentTitle = detail.m_strTitle;
2063 else
2064 currentTitle = detail.m_strSortTitle;
2066 // get the new sort title
2067 if (!CGUIKeyboardFactory::ShowAndGetInput(currentTitle, CVariant{g_localizeStrings.Get(16107)}, false))
2068 return false;
2070 return database.UpdateVideoSortTitle(iDbId, currentTitle, iType);
2073 bool CGUIDialogVideoInfo::LinkMovieToTvShow(const std::shared_ptr<CFileItem>& item,
2074 bool bRemove,
2075 CVideoDatabase& database)
2077 int dbId = item->GetVideoInfoTag()->m_iDbId;
2079 CFileItemList list;
2080 if (bRemove)
2082 std::vector<int> ids;
2083 if (!database.GetLinksToTvShow(dbId, ids))
2084 return false;
2086 for (unsigned int i = 0; i < ids.size(); ++i)
2088 CVideoInfoTag tag;
2089 database.GetTvShowInfo("", tag, ids[i], 0 , VideoDbDetailsNone);
2090 CFileItemPtr show(new CFileItem(tag));
2091 list.Add(show);
2094 else
2096 database.GetTvShowsNav("videodb://tvshows/titles", list);
2098 // remove already linked shows
2099 std::vector<int> ids;
2100 if (!database.GetLinksToTvShow(dbId, ids))
2101 return false;
2103 for (int i = 0; i < list.Size(); )
2105 size_t j;
2106 for (j = 0; j < ids.size(); ++j)
2108 if (list[i]->GetVideoInfoTag()->m_iDbId == ids[j])
2109 break;
2111 if (j == ids.size())
2112 i++;
2113 else
2114 list.Remove(i);
2118 int iSelectedLabel = 0;
2119 if (list.Size() > 1 || (!bRemove && !list.IsEmpty()))
2121 list.Sort(SortByLabel, SortOrderAscending, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING) ? SortAttributeIgnoreArticle : SortAttributeNone);
2122 CGUIDialogSelect* pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
2123 if (pDialog)
2125 pDialog->Reset();
2126 pDialog->SetItems(list);
2127 pDialog->SetHeading(CVariant{20356});
2128 pDialog->Open();
2129 iSelectedLabel = pDialog->GetSelectedItem();
2133 if (iSelectedLabel > -1 && iSelectedLabel < list.Size())
2134 return database.LinkMovieToTvshow(dbId, list[iSelectedLabel]->GetVideoInfoTag()->m_iDbId, bRemove);
2136 return false;
2139 void CGUIDialogVideoInfo::ShowFor(const CFileItem& item)
2141 auto window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowVideoNav>(WINDOW_VIDEO_NAV);
2142 if (window)
2143 window->OnItemInfo(item);
2146 bool CGUIDialogVideoInfo::OnManageVideoVersions()
2148 return CGUIDialogVideoManagerVersions::ManageVideoVersions(m_movieItem);
2151 bool CGUIDialogVideoInfo::OnManageVideoExtras()
2153 return CGUIDialogVideoManagerExtras::ManageVideoExtras(m_movieItem);
2156 void CGUIDialogVideoInfo::ManageVideoVersions(const std::shared_ptr<CFileItem>& item)
2158 CGUIDialogVideoManagerVersions::ManageVideoVersions(item);