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.
9 #include "GUIDialogVideoInfo.h"
11 #include "ContextMenuManager.h"
13 #include "GUIPassword.h"
14 #include "GUIUserMessages.h"
15 #include "ServiceBroker.h"
16 #include "TextureCache.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"
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)
98 bool CGUIDialogVideoInfo::OnMessage(CGUIMessage
& message
)
100 switch ( message
.GetMessage() )
102 case GUI_MSG_WINDOW_DEINIT
:
106 if (m_startUserrating
!= m_movieItem
->GetVideoInfoTag()->m_iUserRating
)
111 m_hasUpdatedUserrating
= true;
112 db
.SetVideoUserRating(m_movieItem
->GetVideoInfoTag()->m_iDbId
, m_movieItem
->GetVideoInfoTag()->m_iUserRating
, m_movieItem
->GetVideoInfoTag()->m_type
);
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;
133 db
.SetPathHash(m_movieItem
->GetVideoInfoTag()->m_strPath
,"");
138 m_bRefreshAll
= false;
147 else if (iControl
== CONTROL_BTN_TRACKS
)
149 m_bViewReview
= !m_bViewReview
;
152 else if (iControl
== CONTROL_BTN_PLAY
)
156 else if (iControl
== CONTROL_BTN_USERRATING
)
160 else if (iControl
== CONTROL_BTN_RESUME
)
164 else if (iControl
== CONTROL_BTN_GET_THUMB
)
168 else if (iControl
== CONTROL_BTN_PLAY_TRAILER
)
172 else if (iControl
== CONTROL_BTN_GET_FANART
)
176 else if (iControl
== CONTROL_BTN_DIRECTOR
)
178 auto directors
= m_movieItem
->GetVideoInfoTag()->m_director
;
179 if (directors
.size() == 0)
181 if (directors
.size() == 1)
182 OnSearch(directors
[0]);
185 auto pDlgSelect
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogSelect
>(WINDOW_DIALOG_SELECT
);
189 pDlgSelect
->SetHeading(CVariant
{22080});
190 for (const auto &director
: directors
)
191 pDlgSelect
->Add(director
);
194 int iItem
= pDlgSelect
->GetSelectedItem();
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
);
208 int iItem
= msg
.GetParam1();
209 if (iItem
< 0 || iItem
>= m_castList
->Size())
211 std::string strItem
= m_castList
->Get(iItem
)->GetLabel();
212 if (m_movieItem
->GetVideoInfoTag()->m_type
== MediaTypeVideoCollection
)
214 SetMovie(m_castList
->Get(iItem
).get());
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
;
240 return CGUIDialog::OnMessage(message
);
243 void CGUIDialogVideoInfo::OnInitWindow()
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
));
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"));
274 CONTROL_DISABLE(CONTROL_BTN_GET_FANART
);
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);
289 else if (action
.GetID() == ACTION_DECREASE_RATING
)
291 SetUserrating(userrating
- 1);
294 else if (action
.GetID() == ACTION_SHOW_INFO
)
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
;
323 // When the scraper throws an error, the video tag can be null here
324 if (!item
->HasVideoInfoTag())
327 MediaType type
= item
->GetVideoInfoTag()->m_type
;
329 m_startUserrating
= m_movieItem
->GetVideoInfoTag()->m_iUserRating
;
331 if (type
== MediaTypeMusicVideo
)
333 CMusicDatabase database
;
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
));
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());
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
;
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());
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
);
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());
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
;
431 database
.SetSingleValue(VideoDbContentType::MOVIES
, VIDEODB_ID_TRAILER
,
432 m_movieItem
->GetVideoInfoTag()->m_iDbId
,
433 m_movieItem
->GetVideoInfoTag()->m_strTrailer
);
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
);
469 if (GetControl(CONTROL_BTN_TRACKS
)) // if no CONTROL_BTN_TRACKS found - allow skinner full visibility control over CONTROL_TEXTAREA and CONTROL_LIST
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);
483 SET_CONTROL_LABEL(CONTROL_BTN_TRACKS
, 206);
486 SET_CONTROL_HIDDEN(CONTROL_LIST
);
487 SET_CONTROL_VISIBLE(CONTROL_TEXTAREA
);
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
);
502 CONTROL_DISABLE(CONTROL_BTN_RESUME
);
504 CONTROL_ENABLE(CONTROL_BTN_PLAY
);
506 // update the thumbnail
507 CGUIControl
* pControl
= GetControl(CONTROL_IMAGE
);
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
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
);
538 progress
->SetHeading(CVariant
{194});
539 progress
->SetLine(0, CVariant
{strSearch
});
540 progress
->SetLine(1, CVariant
{""});
541 progress
->SetLine(2, CVariant
{""});
543 progress
->Progress();
546 DoSearch(strSearch
, items
);
553 CGUIDialogSelect
* pDlgSelect
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogSelect
>(WINDOW_DIALOG_SELECT
);
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);
573 int iItem
= pDlgSelect
->GetSelectedItem();
577 OnSearchItemFound(items
[iItem
].get());
582 HELPERS::ShowOKDialogText(CVariant
{194}, CVariant
{284});
586 void CGUIDialogVideoInfo::DoSearch(std::string
& strSearch
, CFileItemList
& items
) const
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
);
632 // Search for music albums by artist with name matching search string
633 CMusicDatabase music_database
;
634 if (!music_database
.Open())
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();
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
);
668 if (type
== VideoDbContentType::MUSICALBUMS
)
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
;
678 // refresh our window entirely
683 void CGUIDialogVideoInfo::ClearCastList()
685 CGUIMessage
msg(GUI_MSG_LABEL_RESET
, GetID(), CONTROL_LIST
);
690 void CGUIDialogVideoInfo::Play(bool resume
)
692 if (m_movieItem
->GetVideoInfoTag()->m_type
== MediaTypeTvShow
)
695 if (m_movieItem
->IsPlugin())
697 strPath
= m_movieItem
->GetPath();
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
);
707 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_VIDEO_NAV
,strPath
);
711 strPath
= StringUtils::Format("videodb://tvshows/titles/{}/",
712 m_movieItem
->GetVideoInfoTag()->m_iDbId
);
714 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_VIDEO_NAV
,strPath
);
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
);
725 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_VIDEO_NAV
, strPath
);
729 CGUIWindowVideoNav
* pWindow
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIWindowVideoNav
>(WINDOW_VIDEO_NAV
);
735 m_movieItem
->SetStartOffset(STARTOFFSET_RESUME
);
736 else if (!CGUIWindowVideoBase::ShowResumeMenu(*m_movieItem
))
738 // The Resume dialog was closed without any choice
742 m_movieItem
->SetProperty("playlist_type_hint", PLAYLIST::TYPE_VIDEO
);
744 pWindow
->PlayMovie(m_movieItem
.get());
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
,
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
,
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
,
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
)
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
);
814 std::string
CGUIDialogVideoInfo::ChooseArtType(const CFileItem
&videoItem
)
817 CGUIDialogSelect
*dialog
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogSelect
>(WINDOW_DIALOG_SELECT
);
818 if (!dialog
|| !videoItem
.HasVideoInfoTag())
822 dialog
->SetHeading(CVariant
{13511});
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));
842 item
->SetLabel(type
);
843 item
->SetProperty("type", type
);
844 if (videoItem
.HasArt(type
))
845 item
->SetArt("thumb", videoItem
.GetArt(type
));
849 dialog
->SetItems(items
);
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))
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
);
872 std::string type
= ChooseArtType(*m_movieItem
);
876 //! @todo this can be removed once these are unified.
877 if (type
== "fanart")
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));
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));
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);
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));
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);
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));
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));
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
))
983 // update thumb in the database
987 db
.SetArtForItem(m_movieItem
->GetVideoInfoTag()->m_iDbId
, m_movieItem
->GetVideoInfoTag()->m_type
, type
, newThumb
);
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
1003 // re-open the art selection dialog as we come back from
1004 // the image selection dialog
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);
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));
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"))
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);
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
);
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
);
1084 VECSOURCES
sources(*CMediaSourceSettings::GetInstance().GetSources("video"));
1085 AddItemPathToFileBrowserSources(sources
, item
);
1086 CServiceBroker::GetMediaManager().GetLocalDrives(sources
);
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"))
1095 if (StringUtils::EqualsNoCase(result
, "fanart://Embedded"))
1097 unsigned int current
= m_movieItem
->GetVideoInfoTag()->m_fanart
.GetNumFanarts();
1099 for (size_t i
= 0; i
< current
; ++i
)
1100 if (URIUtils::IsProtocol(m_movieItem
->GetVideoInfoTag()->m_fanart
.GetImageURL(), "image"))
1104 m_movieItem
->GetVideoInfoTag()->m_fanart
.AddFanart(embeddedArt
, "", "");
1108 m_movieItem
->GetVideoInfoTag()->m_fanart
.SetPrimaryFanart(found
);
1113 db
.UpdateFanart(*m_movieItem
, m_movieItem
->GetVideoContentType());
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
);
1127 db
.UpdateFanart(*m_movieItem
, m_movieItem
->GetVideoContentType());
1130 result
= m_movieItem
->GetVideoInfoTag()->m_fanart
.GetImageURL();
1132 else if (StringUtils::EqualsNoCase(result
, "fanart://None") || !CFileUtils::Exists(result
))
1135 // set the fanart image
1136 if (flip
&& !result
.empty())
1137 result
= CTextureUtils::GetWrappedImageURL(result
, "", "flipped");
1141 db
.SetArtForItem(m_movieItem
->GetVideoInfoTag()->m_iDbId
, m_movieItem
->GetVideoInfoTag()->m_type
, "fanart", result
);
1145 CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
1146 m_movieItem
->SetArt("fanart", result
);
1147 m_hasUpdatedThumb
= true;
1149 // Update our screen
1153 void CGUIDialogVideoInfo::OnSetUserrating() const
1155 CGUIDialogSelect
*dialog
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogSelect
>(WINDOW_DIALOG_SELECT
);
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
);
1167 int iItem
= dialog
->GetSelectedItem();
1171 SetUserrating(iItem
);
1175 void CGUIDialogVideoInfo::PlayTrailer()
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"
1190 SET_CONTROL_LABEL(iControl
, strLabel
);
1194 std::string
CGUIDialogVideoInfo::GetThumbnail() const
1196 return m_movieItem
->GetArt("thumb");
1201 std::string
GetItemPathForBrowserSource(const CFileItem
& item
)
1203 if (!item
.HasVideoInfoTag())
1206 std::string itemDir
= item
.GetVideoInfoTag()->m_basePath
;
1208 if (itemDir
.empty())
1209 itemDir
= item
.GetVideoInfoTag()->GetPath();
1211 CFileItem
itemTmp(itemDir
, false);
1212 if (itemTmp
.IsVideo())
1213 itemDir
= URIUtils::GetParentPath(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
);
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)
1243 CVideoDatabase database
;
1244 if (!database
.Open())
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);
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);
1286 if (item
->m_bIsFolder
&& type
== MediaTypeSeason
)
1287 buttons
.Add(CONTEXT_BUTTON_SET_SEASON_ART
, 13511);
1290 if (item
->m_bIsFolder
&& type
== "tag")
1292 CVideoDbUrl videoUrl
;
1293 if (videoUrl
.FromString(item
->GetPath()))
1295 const std::string
&mediaType
= videoUrl
.GetItemType();
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
);
1318 switch (static_cast<CONTEXT_BUTTON
>(button
))
1320 case CONTEXT_BUTTON_EDIT
:
1321 result
= UpdateVideoItemTitle(item
);
1324 case CONTEXT_BUTTON_EDIT_SORTTITLE
:
1325 result
= UpdateVideoItemSortTitle(item
);
1328 case CONTEXT_BUTTON_LINK_MOVIE
:
1329 result
= LinkMovieToTvShow(item
, false, database
);
1332 case CONTEXT_BUTTON_UNLINK_MOVIE
:
1333 result
= LinkMovieToTvShow(item
, true, database
);
1336 case CONTEXT_BUTTON_SET_MOVIESET
:
1338 CFileItemPtr selectedSet
;
1339 if (GetSetForMovie(item
.get(), selectedSet
))
1340 result
= SetMovieSet(item
.get(), selectedSet
.get());
1344 case CONTEXT_BUTTON_UNLINK_BOOKMARK
:
1345 database
.DeleteBookMarkForEpisode(*item
->GetVideoInfoTag());
1349 case CONTEXT_BUTTON_DELETE
:
1350 result
= DeleteVideoItem(item
);
1353 case CONTEXT_BUTTON_SET_MOVIESET_ART
:
1354 case CONTEXT_BUTTON_SET_SEASON_ART
:
1355 result
= ManageVideoItemArtwork(item
, type
);
1358 case CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS
:
1359 result
= ManageMovieSets(item
);
1362 case CONTEXT_BUTTON_TAGS_ADD_ITEMS
:
1363 result
= AddItemsToTag(item
);
1366 case CONTEXT_BUTTON_TAGS_REMOVE_ITEMS
:
1367 result
= RemoveItemsFromTag(item
);
1371 if (button
>= addonItemOffset
)
1372 result
= CONTEXTMENU::LoopFrom(*addonItems
[button
- addonItemOffset
], item
);
1385 //Add change a title's name
1386 bool CGUIDialogVideoInfo::UpdateVideoItemTitle(const std::shared_ptr
<CFileItem
>& pItem
)
1388 if (pItem
== nullptr || !pItem
->HasVideoInfoTag())
1391 // dont allow update while scanning
1392 if (CVideoLibraryQueue::GetInstance().IsScanningLibrary())
1394 HELPERS::ShowOKDialogText(CVariant
{257}, CVariant
{14057});
1398 CVideoDatabase database
;
1399 if (!database
.Open())
1402 int iDbId
= pItem
->GetVideoInfoTag()->m_iDbId
;
1403 MediaType mediaType
= pItem
->GetVideoInfoTag()->m_type
;
1405 CVideoInfoTag detail
;
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))
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
);
1450 detail
.m_strTitle
= title
;
1451 VideoDbContentType iType
= pItem
->GetVideoContentType();
1452 database
.UpdateMovieTitle(iDbId
, detail
.m_strTitle
, iType
);
1458 bool CGUIDialogVideoInfo::CanDeleteVideoItem(const std::shared_ptr
<CFileItem
>& item
)
1460 if (item
== nullptr || !item
->HasVideoInfoTag())
1463 if (item
->GetVideoInfoTag()->m_type
== "tag")
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
))
1484 // dont allow update while scanning
1485 if (CVideoLibraryQueue::GetInstance().IsScanningLibrary())
1487 HELPERS::ShowOKDialogText(CVariant
{257}, CVariant
{14057});
1491 CGUIDialogYesNo
* pDialog
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogYesNo
>(WINDOW_DIALOG_YES_NO
);
1492 if (pDialog
== nullptr)
1496 VideoDbContentType type
= item
->GetVideoContentType();
1497 const std::string
& subtype
= item
->GetVideoInfoTag()->m_type
;
1498 if (subtype
!= "tag")
1502 case VideoDbContentType::MOVIES
:
1505 case VideoDbContentType::EPISODES
:
1508 case VideoDbContentType::TVSHOWS
:
1511 case VideoDbContentType::MUSICVIDEOS
:
1514 case VideoDbContentType::MOVIE_SETS
:
1526 pDialog
->SetHeading(CVariant
{heading
});
1530 pDialog
->SetLine(0, CVariant
{g_localizeStrings
.Get(662)});
1531 pDialog
->SetLine(1, CVariant
{g_localizeStrings
.Get(663)});
1536 CVariant
{StringUtils::Format(g_localizeStrings
.Get(433), item
->GetLabel())});
1537 pDialog
->SetLine(1, CVariant
{""});
1539 pDialog
->SetLine(2, CVariant
{""});
1542 if (!pDialog
->IsConfirmed())
1545 CVideoDatabase database
;
1548 if (item
->GetVideoInfoTag()->m_iDbId
< 0)
1551 if (subtype
== "tag")
1553 database
.DeleteTag(item
->GetVideoInfoTag()->m_iDbId
, type
);
1559 case VideoDbContentType::MOVIES
:
1560 database
.DeleteMovie(item
->GetVideoInfoTag()->m_iDbId
);
1562 case VideoDbContentType::EPISODES
:
1563 database
.DeleteEpisode(item
->GetVideoInfoTag()->m_iDbId
);
1565 case VideoDbContentType::TVSHOWS
:
1566 database
.DeleteTvShow(item
->GetVideoInfoTag()->m_iDbId
);
1568 case VideoDbContentType::MUSICVIDEOS
:
1569 database
.DeleteMusicVideo(item
->GetVideoInfoTag()->m_iDbId
);
1571 case VideoDbContentType::MOVIE_SETS
:
1572 database
.DeleteSet(item
->GetVideoInfoTag()->m_iDbId
);
1580 bool CGUIDialogVideoInfo::DeleteVideoItem(const std::shared_ptr
<CFileItem
>& item
,
1581 bool unavailable
/* = false */)
1583 if (item
== nullptr)
1586 // delete the video item from the database
1587 if (!DeleteVideoItemFromDatabase(item
, unavailable
))
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();
1632 bool CGUIDialogVideoInfo::ManageMovieSets(const std::shared_ptr
<CFileItem
>& item
)
1634 if (item
== nullptr)
1637 CFileItemList originalItems
;
1638 CFileItemList selectedItems
;
1640 if (!GetMoviesForSet(item
.get(), originalItems
, selectedItems
) ||
1641 selectedItems
.Size() == 0) // need at least one item selected
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())
1678 CVideoDatabase videodb
;
1679 if (!videodb
.Open())
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
1689 CFileItemList listItems
;
1690 if (!videodb
.GetSortedVideos(MediaTypeMovie
, "videodb://movies", SortDescription(), listItems
) || listItems
.Size() <= 0)
1693 CGUIDialogSelect
*dialog
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogSelect
>(WINDOW_DIALOG_SELECT
);
1694 if (dialog
== nullptr)
1697 listItems
.Sort(SortByLabel
, SortOrderAscending
, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING
) ? SortAttributeIgnoreArticle
: SortAttributeNone
);
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
);
1715 dialog
->SetSelected(selectedIndices
);
1716 dialog
->EnableButton(true, 186);
1719 if (dialog
->IsConfirmed())
1721 for (int i
: dialog
->GetSelectedItems())
1722 selectedMovies
.Add(listItems
.Get(i
));
1723 return (selectedMovies
.Size() > 0);
1729 bool CGUIDialogVideoInfo::GetSetForMovie(const CFileItem
* movieItem
,
1730 std::shared_ptr
<CFileItem
>& selectedSet
)
1732 if (movieItem
== nullptr || !movieItem
->HasVideoInfoTag())
1735 CVideoDatabase videodb
;
1736 if (!videodb
.Open())
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
))
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
);
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)
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
);
1801 dialog
->EnableButton(true, 20468); // new set via button
1804 if (dialog
->IsButtonPressed())
1805 { // creating new set
1806 std::string newSetTitle
;
1807 if (!CGUIKeyboardFactory::ShowAndGetInput(newSetTitle
, CVariant
{g_localizeStrings
.Get(20468)}, 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
;
1821 else if (dialog
->IsConfirmed())
1823 selectedSet
= dialog
->GetSelectedFileItem();
1824 return (selectedSet
!= nullptr);
1830 bool CGUIDialogVideoInfo::SetMovieSet(const CFileItem
*movieItem
, const CFileItem
*selectedSet
)
1832 if (movieItem
== nullptr || !movieItem
->HasVideoInfoTag() ||
1833 selectedSet
== nullptr || !selectedSet
->HasVideoInfoTag())
1836 CVideoDatabase videodb
;
1837 if (!videodb
.Open())
1840 videodb
.SetMovieSet(movieItem
->GetVideoInfoTag()->m_iDbId
, selectedSet
->GetVideoInfoTag()->m_iDbId
);
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())
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
))
1877 CVideoDatabase::Filter filter
;
1881 videoUrl
.AddOption("tagid", idTag
);
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)
1890 CGUIDialogSelect
*dialog
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogSelect
>(WINDOW_DIALOG_SELECT
);
1891 if (dialog
== nullptr)
1894 listItems
.Sort(SortByLabel
, SortOrderAscending
, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING
) ? SortAttributeIgnoreArticle
: SortAttributeNone
);
1897 dialog
->SetMultiSelection(true);
1898 dialog
->SetHeading(CVariant
{strHeading
});
1899 dialog
->SetItems(listItems
);
1900 dialog
->EnableButton(true, 186);
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())
1913 CVideoDbUrl videoUrl
;
1914 if (!videoUrl
.FromString(tagItem
->GetPath()))
1917 CVideoDatabase videodb
;
1918 if (!videodb
.Open())
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
))
1930 for (int index
= 0; index
< items
.Size(); index
++)
1932 if (!items
[index
]->HasVideoInfoTag() || items
[index
]->GetVideoInfoTag()->m_iDbId
<= 0)
1935 videodb
.AddTagToItem(items
[index
]->GetVideoInfoTag()->m_iDbId
, tagItem
->GetVideoInfoTag()->m_iDbId
, mediaType
);
1941 bool CGUIDialogVideoInfo::RemoveItemsFromTag(const std::shared_ptr
<CFileItem
>& tagItem
)
1943 if (tagItem
== nullptr || !tagItem
->HasVideoInfoTag())
1946 CVideoDbUrl videoUrl
;
1947 if (!videoUrl
.FromString(tagItem
->GetPath()))
1950 CVideoDatabase videodb
;
1951 if (!videodb
.Open())
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))
1963 for (int index
= 0; index
< items
.Size(); index
++)
1965 if (!items
[index
]->HasVideoInfoTag() || items
[index
]->GetVideoInfoTag()->m_iDbId
<= 0)
1968 videodb
.RemoveTagFromItem(items
[index
]->GetVideoInfoTag()->m_iDbId
, tagItem
->GetVideoInfoTag()->m_iDbId
, mediaType
);
1976 std::string
FindLocalMovieSetArtworkFile(const CFileItemPtr
& item
, const std::string
& artType
)
1978 std::string infoFolder
= VIDEO::CVideoInfoScanner::GetMovieSetInfoFolder(item
->GetLabel());
1979 if (infoFolder
.empty())
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();
1997 bool CGUIDialogVideoInfo::ManageVideoItemArtwork(const std::shared_ptr
<CFileItem
>& item
,
1998 const MediaType
& type
)
2000 if (item
== nullptr || !item
->HasVideoInfoTag() || type
.empty())
2003 CVideoDatabase videodb
;
2004 if (!videodb
.Open())
2007 // Grab the thumbnails from the web
2008 CFileItemList items
;
2009 CFileItemPtr
noneitem(new CFileItem("thumb://None", false));
2010 std::string currentThumb
;
2012 std::string artistPath
;
2013 std::string artistOldPath
;
2014 std::string artType
= "thumb";
2015 if (type
== MediaTypeArtist
)
2017 CMusicDatabase musicdb
;
2020 idArtist
= musicdb
.GetArtistByName(item
->GetLabel()); // Fails when name not unique
2023 // Get artist paths - possible locations for thumb - while music db open
2024 musicdb
.GetOldArtistPath(idArtist
, artistOldPath
); // Old artist path, local to music files
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
);
2039 artType
= ChooseArtType(*item
);
2040 if (artType
.empty())
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));
2059 noneitem
->SetArt("icon", "DefaultFolder.png");
2060 noneitem
->SetLabel(g_localizeStrings
.Get(13515));
2063 std::vector
<std::string
> thumbs
;
2064 if (type
!= MediaTypeArtist
)
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);
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));
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
);
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
);
2135 noneitem
->SetArt("icon", "DefaultVideo.png");
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
);
2157 CFileItemPtr
pItem(new CFileItem(strThumb
, false));
2158 pItem
->SetLabel(g_localizeStrings
.Get(13514));
2159 pItem
->SetArt("thumb", strThumb
);
2164 noneitem
->SetArt("icon", "DefaultArtist.png");
2168 items
.Add(noneitem
);
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));
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
2193 if (result
== "thumb://None")
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
||
2204 type
== MediaTypeSeason
||
2205 (type
== MediaTypeArtist
&& idArtist
< 0))
2206 videodb
.SetArtForItem(item
->GetVideoInfoTag()->m_iDbId
, item
->GetVideoInfoTag()->m_type
, artType
, result
);
2209 CMusicDatabase musicdb
;
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
);
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);
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});
2246 CVideoDatabase database
;
2247 if (!database
.Open())
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
;
2262 currentTitle
= detail
.m_strSortTitle
;
2264 // get the new sort title
2265 if (!CGUIKeyboardFactory::ShowAndGetInput(currentTitle
, CVariant
{g_localizeStrings
.Get(16107)}, false))
2268 return database
.UpdateVideoSortTitle(iDbId
, currentTitle
, iType
);
2271 bool CGUIDialogVideoInfo::LinkMovieToTvShow(const std::shared_ptr
<CFileItem
>& item
,
2273 CVideoDatabase
& database
)
2275 int dbId
= item
->GetVideoInfoTag()->m_iDbId
;
2280 std::vector
<int> ids
;
2281 if (!database
.GetLinksToTvShow(dbId
, ids
))
2284 for (unsigned int i
= 0; i
< ids
.size(); ++i
)
2287 database
.GetTvShowInfo("", tag
, ids
[i
], 0 , VideoDbDetailsNone
);
2288 CFileItemPtr
show(new CFileItem(tag
));
2294 database
.GetTvShowsNav("videodb://tvshows/titles", list
);
2296 // remove already linked shows
2297 std::vector
<int> ids
;
2298 if (!database
.GetLinksToTvShow(dbId
, ids
))
2301 for (int i
= 0; i
< list
.Size(); )
2304 for (j
= 0; j
< ids
.size(); ++j
)
2306 if (list
[i
]->GetVideoInfoTag()->m_iDbId
== ids
[j
])
2309 if (j
== ids
.size())
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
);
2324 pDialog
->SetItems(list
);
2325 pDialog
->SetHeading(CVariant
{20356});
2327 iSelectedLabel
= pDialog
->GetSelectedItem();
2331 if (iSelectedLabel
> -1 && iSelectedLabel
< list
.Size())
2332 return database
.LinkMovieToTvshow(dbId
, list
[iSelectedLabel
]->GetVideoInfoTag()->m_iDbId
, bRemove
);
2337 bool CGUIDialogVideoInfo::OnGetFanart(const std::shared_ptr
<CFileItem
>& videoItem
)
2339 if (videoItem
== nullptr || !videoItem
->HasVideoInfoTag())
2343 CVideoDatabase videodb
;
2344 if (!videodb
.Open())
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
);
2369 VECSOURCES
sources(*CMediaSourceSettings::GetInstance().GetSources("video"));
2370 CServiceBroker::GetMediaManager().GetLocalDrives(sources
);
2371 AddItemPathToFileBrowserSources(sources
, item
);
2373 if (!CGUIDialogFileBrowser::ShowAndGetImage(items
, sources
, g_localizeStrings
.Get(20437), result
, &flip
, 20445) ||
2374 StringUtils::EqualsNoCase(result
, "fanart://Current"))
2377 else if (StringUtils::EqualsNoCase(result
, "fanart://None") || !CFileUtils::Exists(result
))
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();
2390 void CGUIDialogVideoInfo::ShowFor(const CFileItem
& item
)
2392 auto window
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIWindowVideoNav
>(WINDOW_VIDEO_NAV
);
2395 ADDON::ScraperPtr info
;
2396 window
->OnItemInfo(item
, info
);