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 "FileItemList.h"
14 #include "GUIPassword.h"
15 #include "GUIUserMessages.h"
16 #include "ServiceBroker.h"
17 #include "TextureCache.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"
73 #include <unordered_map>
75 using namespace XFILE::VIDEODATABASEDIRECTORY
;
76 using namespace XFILE
;
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)
115 bool CGUIDialogVideoInfo::OnMessage(CGUIMessage
& message
)
117 switch ( message
.GetMessage() )
119 case GUI_MSG_WINDOW_DEINIT
:
123 if (m_startUserrating
!= m_movieItem
->GetVideoInfoTag()->m_iUserRating
)
128 m_hasUpdatedUserrating
= true;
129 db
.SetVideoUserRating(m_movieItem
->GetVideoInfoTag()->m_iDbId
, m_movieItem
->GetVideoInfoTag()->m_iUserRating
, m_movieItem
->GetVideoInfoTag()->m_type
);
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;
150 db
.SetPathHash(m_movieItem
->GetVideoInfoTag()->m_strPath
,"");
155 m_bRefreshAll
= false;
164 else if (iControl
== CONTROL_BTN_TRACKS
)
166 m_bViewReview
= !m_bViewReview
;
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
)
181 else if (iControl
== CONTROL_BTN_USERRATING
)
185 else if (iControl
== CONTROL_BTN_RESUME
)
189 else if (iControl
== CONTROL_BTN_GET_THUMB
)
193 else if (iControl
== CONTROL_BTN_PLAY_TRAILER
)
197 else if (iControl
== CONTROL_BTN_GET_FANART
)
201 else if (iControl
== CONTROL_BTN_DIRECTOR
)
203 auto directors
= m_movieItem
->GetVideoInfoTag()->m_director
;
204 if (directors
.size() == 0)
206 if (directors
.size() == 1)
207 OnSearch(directors
[0]);
210 auto pDlgSelect
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogSelect
>(WINDOW_DIALOG_SELECT
);
214 pDlgSelect
->SetHeading(CVariant
{22080});
215 for (const auto &director
: directors
)
216 pDlgSelect
->Add(director
);
219 int iItem
= pDlgSelect
->GetSelectedItem();
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
);
233 int iItem
= msg
.GetParam1();
234 if (iItem
< 0 || iItem
>= m_castList
->Size())
236 std::string strItem
= m_castList
->Get(iItem
)->GetLabel();
237 if (m_movieItem
->GetVideoInfoTag()->m_type
== MediaTypeVideoCollection
)
239 SetMovie(m_castList
->Get(iItem
).get());
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
;
265 return CGUIDialog::OnMessage(message
);
268 void CGUIDialogVideoInfo::OnInitWindow()
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
));
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(),
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"));
307 CONTROL_DISABLE(CONTROL_BTN_GET_FANART
);
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);
322 else if (action
.GetID() == ACTION_DECREASE_RATING
)
324 SetUserrating(userrating
- 1);
327 else if (action
.GetID() == ACTION_SHOW_INFO
)
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
;
356 // When the scraper throws an error, the video tag can be null here
357 if (!item
->HasVideoInfoTag())
360 MediaType type
= item
->GetVideoInfoTag()->m_type
;
362 m_startUserrating
= m_movieItem
->GetVideoInfoTag()->m_iUserRating
;
364 if (type
== MediaTypeMusicVideo
)
366 CMusicDatabase database
;
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
));
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());
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
;
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());
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
);
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());
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
;
464 database
.SetSingleValue(VideoDbContentType::MOVIES
, VIDEODB_ID_TRAILER
,
465 m_movieItem
->GetVideoInfoTag()->m_iDbId
,
466 m_movieItem
->GetVideoInfoTag()->m_strTrailer
);
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
);
502 if (GetControl(CONTROL_BTN_TRACKS
)) // if no CONTROL_BTN_TRACKS found - allow skinner full visibility control over CONTROL_TEXTAREA and CONTROL_LIST
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
520 SET_CONTROL_LABEL(CONTROL_BTN_TRACKS
, 206);
523 SET_CONTROL_HIDDEN(CONTROL_LIST
);
524 SET_CONTROL_VISIBLE(CONTROL_TEXTAREA
);
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
);
539 CONTROL_DISABLE(CONTROL_BTN_RESUME
);
541 CONTROL_ENABLE(CONTROL_BTN_PLAY
);
543 // update the thumbnail
544 CGUIControl
* pControl
= GetControl(CONTROL_IMAGE
);
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
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
);
575 progress
->SetHeading(CVariant
{194});
576 progress
->SetLine(0, CVariant
{strSearch
});
577 progress
->SetLine(1, CVariant
{""});
578 progress
->SetLine(2, CVariant
{""});
580 progress
->Progress();
583 DoSearch(strSearch
, items
);
590 CGUIDialogSelect
* pDlgSelect
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogSelect
>(WINDOW_DIALOG_SELECT
);
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);
610 int iItem
= pDlgSelect
->GetSelectedItem();
614 OnSearchItemFound(items
[iItem
].get());
619 HELPERS::ShowOKDialogText(CVariant
{194}, CVariant
{284});
623 void CGUIDialogVideoInfo::DoSearch(std::string
& strSearch
, CFileItemList
& items
) const
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
);
669 // Search for music albums by artist with name matching search string
670 CMusicDatabase music_database
;
671 if (!music_database
.Open())
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();
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
);
708 db
.GetTvShowInfo(pItem
->GetPath(), movieDetails
, videoTag
->m_iDbId
);
710 if (type
== VideoDbContentType::MUSICVIDEOS
)
711 db
.GetMusicVideoInfo(pItem
->GetPath(), movieDetails
, pItem
->GetVideoInfoTag()->m_iDbId
);
713 if (type
== VideoDbContentType::MUSICALBUMS
)
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
;
723 // refresh our window entirely
728 void CGUIDialogVideoInfo::ClearCastList()
730 CGUIMessage
msg(GUI_MSG_LABEL_RESET
, GetID(), CONTROL_LIST
);
737 class CVideoPlayActionProcessor
: public VIDEO::GUILIB::CVideoPlayActionProcessorBase
740 explicit CVideoPlayActionProcessor(const std::shared_ptr
<CFileItem
>& item
)
741 : CVideoPlayActionProcessorBase(item
)
746 bool OnResumeSelected() override
748 m_item
->SetStartOffset(STARTOFFSET_RESUME
);
753 bool OnPlaySelected() override
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
)
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();
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
);
799 else if (videoTag
->m_type
== MediaTypeTvShow
)
800 strPath
= StringUtils::Format("videodb://tvshows/titles/{}/", videoTag
->m_iDbId
);
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
,
811 if (!strPath
.empty())
814 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_VIDEO_NAV
, strPath
);
821 // play the current video version, even if multiple versions are available
822 m_movieItem
->SetProperty("has_resolved_video_asset", true);
826 CVideoPlayActionProcessor proc
{m_movieItem
};
827 proc
.ProcessAction(VIDEO::GUILIB::ACTION_RESUME
);
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
);
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
851 m_movieItem
->ClearProperty("has_resolved_video_asset");
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
,
871 std::map
<std::string
, std::string
> currentArt
;
873 if (tag
.GetAssetInfo().GetId() >= 0)
874 db
.GetArtForAsset(tag
.m_iFileId
, ArtFallbackOptions::NONE
, currentArt
);
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
,
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
,
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
)
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
);
928 class CArtTypeChooser
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;
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())
962 dialog
->SetHeading(CVariant
{13511});
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
= {
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
));
993 dialog
->SetItems(m_items
);
994 dialog
->SetSelected(m_selectedItem
);
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)},
1006 m_artType
= artworkName
;
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
1029 void CGUIDialogVideoInfo::OnGetFanart()
1031 if (ManageVideoItemArtwork(m_movieItem
, m_movieItem
->GetVideoInfoTag()->m_type
, "fanart"))
1033 m_hasUpdatedThumb
= true;
1035 // Update our screen
1040 void CGUIDialogVideoInfo::OnSetUserrating() const
1042 CGUIDialogSelect
*dialog
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogSelect
>(WINDOW_DIALOG_SELECT
);
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
);
1054 int iItem
= dialog
->GetSelectedItem();
1058 SetUserrating(iItem
);
1062 void CGUIDialogVideoInfo::PlayTrailer()
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"
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)
1092 CVideoDatabase database
;
1093 if (!database
.Open())
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);
1141 if (item
->m_bIsFolder
&& type
== MediaTypeVideoCollection
)
1143 buttons
.Add(CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS
, 20465);
1147 if (item
->m_bIsFolder
&& type
== "tag")
1149 CVideoDbUrl videoUrl
;
1150 if (videoUrl
.FromString(item
->GetPath()))
1152 const std::string
&mediaType
= videoUrl
.GetItemType();
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
);
1178 switch (static_cast<CONTEXT_BUTTON
>(button
))
1180 case CONTEXT_BUTTON_EDIT
:
1181 result
= UpdateVideoItemTitle(item
);
1184 case CONTEXT_BUTTON_EDIT_SORTTITLE
:
1185 result
= UpdateVideoItemSortTitle(item
);
1188 case CONTEXT_BUTTON_LINK_MOVIE
:
1189 result
= LinkMovieToTvShow(item
, false, database
);
1192 case CONTEXT_BUTTON_UNLINK_MOVIE
:
1193 result
= LinkMovieToTvShow(item
, true, database
);
1196 case CONTEXT_BUTTON_SET_MOVIESET
:
1198 CFileItemPtr selectedSet
;
1199 if (GetSetForMovie(item
.get(), selectedSet
))
1200 result
= SetMovieSet(item
.get(), selectedSet
.get());
1204 case CONTEXT_BUTTON_MANAGE_VIDEOVERSIONS
:
1205 ManageVideoVersions(item
);
1209 case CONTEXT_BUTTON_UNLINK_BOOKMARK
:
1210 database
.DeleteBookMarkForEpisode(*item
->GetVideoInfoTag());
1214 case CONTEXT_BUTTON_DELETE
:
1215 result
= DeleteVideoItem(item
);
1218 case CONTEXT_BUTTON_SET_ART
:
1219 result
= ChooseAndManageVideoItemArtwork(item
);
1222 case CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS
:
1223 result
= ManageMovieSets(item
);
1226 case CONTEXT_BUTTON_TAGS_ADD_ITEMS
:
1227 result
= AddItemsToTag(item
);
1230 case CONTEXT_BUTTON_TAGS_REMOVE_ITEMS
:
1231 result
= RemoveItemsFromTag(item
);
1235 if (button
>= addonItemOffset
)
1236 result
= CONTEXTMENU::LoopFrom(*addonItems
[button
- addonItemOffset
], item
);
1249 //Add change a title's name
1250 bool CGUIDialogVideoInfo::UpdateVideoItemTitle(const std::shared_ptr
<CFileItem
>& pItem
)
1252 if (pItem
== nullptr || !pItem
->HasVideoInfoTag())
1255 // dont allow update while scanning
1256 if (CVideoLibraryQueue::GetInstance().IsScanningLibrary())
1258 HELPERS::ShowOKDialogText(CVariant
{257}, CVariant
{14057});
1262 CVideoDatabase database
;
1263 if (!database
.Open())
1266 int iDbId
= pItem
->GetVideoInfoTag()->m_iDbId
;
1267 MediaType mediaType
= pItem
->GetVideoInfoTag()->m_type
;
1269 CVideoInfoTag detail
;
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))
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
);
1315 detail
.m_strTitle
= title
;
1316 VideoDbContentType iType
= pItem
->GetVideoContentType();
1317 database
.UpdateMovieTitle(iDbId
, detail
.m_strTitle
, iType
);
1323 bool CGUIDialogVideoInfo::CanDeleteVideoItem(const std::shared_ptr
<CFileItem
>& item
)
1325 if (item
== nullptr || !item
->HasVideoInfoTag())
1328 if (item
->GetVideoInfoTag()->m_type
== "tag")
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
))
1347 // dont allow update while scanning
1348 if (CVideoLibraryQueue::GetInstance().IsScanningLibrary())
1350 HELPERS::ShowOKDialogText(CVariant
{257}, CVariant
{14057});
1354 CGUIDialogYesNo
* pDialog
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogYesNo
>(WINDOW_DIALOG_YES_NO
);
1355 if (pDialog
== nullptr)
1359 VideoDbContentType type
= item
->GetVideoContentType();
1360 const std::string
& subtype
= item
->GetVideoInfoTag()->m_type
;
1361 if (subtype
!= "tag")
1365 case VideoDbContentType::MOVIES
:
1368 case VideoDbContentType::EPISODES
:
1371 case VideoDbContentType::TVSHOWS
:
1374 case VideoDbContentType::MUSICVIDEOS
:
1377 case VideoDbContentType::MOVIE_SETS
:
1389 pDialog
->SetHeading(CVariant
{heading
});
1393 pDialog
->SetLine(0, CVariant
{g_localizeStrings
.Get(662)});
1394 pDialog
->SetLine(1, CVariant
{g_localizeStrings
.Get(663)});
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
{""});
1406 if (!pDialog
->IsConfirmed())
1409 CVideoDatabase database
;
1412 if (item
->GetVideoInfoTag()->m_iDbId
< 0)
1415 if (subtype
== "tag")
1417 database
.DeleteTag(item
->GetVideoInfoTag()->m_iDbId
, type
);
1423 case VideoDbContentType::MOVIES
:
1424 database
.DeleteMovie(item
->GetVideoInfoTag()->m_iDbId
);
1426 case VideoDbContentType::EPISODES
:
1427 database
.DeleteEpisode(item
->GetVideoInfoTag()->m_iDbId
);
1429 case VideoDbContentType::TVSHOWS
:
1430 database
.DeleteTvShow(item
->GetVideoInfoTag()->m_iDbId
);
1432 case VideoDbContentType::MUSICVIDEOS
:
1433 database
.DeleteMusicVideo(item
->GetVideoInfoTag()->m_iDbId
);
1435 case VideoDbContentType::MOVIE_SETS
:
1436 database
.DeleteSet(item
->GetVideoInfoTag()->m_iDbId
);
1444 bool CGUIDialogVideoInfo::DeleteVideoItem(const std::shared_ptr
<CFileItem
>& item
,
1445 bool unavailable
/* = false */)
1447 if (item
== nullptr)
1450 // delete the video item from the database
1451 if (!DeleteVideoItemFromDatabase(item
, unavailable
))
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();
1494 bool CGUIDialogVideoInfo::ManageMovieSets(const std::shared_ptr
<CFileItem
>& item
)
1496 if (item
== nullptr)
1499 CFileItemList originalItems
;
1500 CFileItemList selectedItems
;
1502 if (!GetMoviesForSet(item
.get(), originalItems
, selectedItems
) ||
1503 selectedItems
.Size() == 0) // need at least one item selected
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())
1540 CVideoDatabase videodb
;
1541 if (!videodb
.Open())
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
1551 CFileItemList listItems
;
1552 if (!videodb
.GetSortedVideos(MediaTypeMovie
, "videodb://movies", SortDescription(), listItems
) || listItems
.Size() <= 0)
1555 CGUIDialogSelect
*dialog
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogSelect
>(WINDOW_DIALOG_SELECT
);
1556 if (dialog
== nullptr)
1559 listItems
.Sort(SortByLabel
, SortOrderAscending
, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING
) ? SortAttributeIgnoreArticle
: SortAttributeNone
);
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
);
1577 dialog
->SetSelected(selectedIndices
);
1578 dialog
->EnableButton(true, 186);
1581 if (dialog
->IsConfirmed())
1583 for (int i
: dialog
->GetSelectedItems())
1584 selectedMovies
.Add(listItems
.Get(i
));
1585 return (selectedMovies
.Size() > 0);
1591 bool CGUIDialogVideoInfo::GetSetForMovie(const CFileItem
* movieItem
,
1592 std::shared_ptr
<CFileItem
>& selectedSet
)
1594 if (movieItem
== nullptr || !movieItem
->HasVideoInfoTag())
1597 CVideoDatabase videodb
;
1598 if (!videodb
.Open())
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
))
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
);
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)
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
);
1663 dialog
->EnableButton(true, 20468); // new set via button
1666 if (dialog
->IsButtonPressed())
1667 { // creating new set
1668 std::string newSetTitle
;
1669 if (!CGUIKeyboardFactory::ShowAndGetInput(newSetTitle
, CVariant
{g_localizeStrings
.Get(20468)}, 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
;
1683 else if (dialog
->IsConfirmed())
1685 selectedSet
= dialog
->GetSelectedFileItem();
1686 return (selectedSet
!= nullptr);
1692 bool CGUIDialogVideoInfo::SetMovieSet(const CFileItem
*movieItem
, const CFileItem
*selectedSet
)
1694 if (movieItem
== nullptr || !movieItem
->HasVideoInfoTag() ||
1695 selectedSet
== nullptr || !selectedSet
->HasVideoInfoTag())
1698 CVideoDatabase videodb
;
1699 if (!videodb
.Open())
1702 videodb
.SetMovieSet(movieItem
->GetVideoInfoTag()->m_iDbId
, selectedSet
->GetVideoInfoTag()->m_iDbId
);
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())
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
))
1739 CVideoDatabase::Filter filter
;
1743 videoUrl
.AddOption("tagid", idTag
);
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)
1752 CGUIDialogSelect
*dialog
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogSelect
>(WINDOW_DIALOG_SELECT
);
1753 if (dialog
== nullptr)
1756 listItems
.Sort(SortByLabel
, SortOrderAscending
, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING
) ? SortAttributeIgnoreArticle
: SortAttributeNone
);
1759 dialog
->SetMultiSelection(true);
1760 dialog
->SetHeading(CVariant
{strHeading
});
1761 dialog
->SetItems(listItems
);
1762 dialog
->EnableButton(true, 186);
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())
1775 CVideoDbUrl videoUrl
;
1776 if (!videoUrl
.FromString(tagItem
->GetPath()))
1779 CVideoDatabase videodb
;
1780 if (!videodb
.Open())
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
))
1792 for (int index
= 0; index
< items
.Size(); index
++)
1794 if (!items
[index
]->HasVideoInfoTag() || items
[index
]->GetVideoInfoTag()->m_iDbId
<= 0)
1797 videodb
.AddTagToItem(items
[index
]->GetVideoInfoTag()->m_iDbId
, tagItem
->GetVideoInfoTag()->m_iDbId
, mediaType
);
1803 bool CGUIDialogVideoInfo::RemoveItemsFromTag(const std::shared_ptr
<CFileItem
>& tagItem
)
1805 if (tagItem
== nullptr || !tagItem
->HasVideoInfoTag())
1808 CVideoDbUrl videoUrl
;
1809 if (!videoUrl
.FromString(tagItem
->GetPath()))
1812 CVideoDatabase videodb
;
1813 if (!videodb
.Open())
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))
1825 for (int index
= 0; index
< items
.Size(); index
++)
1827 if (!items
[index
]->HasVideoInfoTag() || items
[index
]->GetVideoInfoTag()->m_iDbId
<= 0)
1830 videodb
.RemoveTagFromItem(items
[index
]->GetVideoInfoTag()->m_iDbId
, tagItem
->GetVideoInfoTag()->m_iDbId
, mediaType
);
1836 bool CGUIDialogVideoInfo::ChooseAndManageVideoItemArtwork(const std::shared_ptr
<CFileItem
>& item
)
1838 bool result
= false;
1840 CArtTypeChooser chooser
{item
};
1843 if (!chooser
.ChooseArtType())
1846 const std::string chosenArtType
{chooser
.GetArtType()};
1847 result
= ManageVideoItemArtwork(item
, item
->GetVideoInfoTag()->m_type
, chosenArtType
);
1849 chooser
.UpdateArtType(chosenArtType
, item
->GetArt(chosenArtType
));
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");
1865 class CAsyncGetArt
: private IRunnable
1868 CAsyncGetArt() = delete;
1869 CAsyncGetArt(const VIDEO::IVideoItemArtworkHandler
& handler
) : m_handler(handler
) {}
1873 CGUIDialogBusy::Wait(this, 100, false);
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
; }
1883 // IRunnable implementation
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())
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
);
1966 std::vector
<CMediaSource
> sources
= *CMediaSourceSettings::GetInstance().GetSources("video");
1967 CServiceBroker::GetMediaManager().GetLocalDrives(sources
);
1968 artHandler
->AddItemPathToFileBrowserSources(sources
);
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")
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
1987 if (result
== "thumb://None")
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(),
2017 CUtil::DeleteVideoDatabaseDirectoryCache();
2018 CGUIMessage
msg(GUI_MSG_NOTIFY_ALL
, 0, 0, GUI_MSG_REFRESH_THUMBS
);
2019 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg
);
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);
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});
2047 CVideoDatabase database
;
2048 if (!database
.Open())
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
;
2064 currentTitle
= detail
.m_strSortTitle
;
2066 // get the new sort title
2067 if (!CGUIKeyboardFactory::ShowAndGetInput(currentTitle
, CVariant
{g_localizeStrings
.Get(16107)}, false))
2070 return database
.UpdateVideoSortTitle(iDbId
, currentTitle
, iType
);
2073 bool CGUIDialogVideoInfo::LinkMovieToTvShow(const std::shared_ptr
<CFileItem
>& item
,
2075 CVideoDatabase
& database
)
2077 int dbId
= item
->GetVideoInfoTag()->m_iDbId
;
2082 std::vector
<int> ids
;
2083 if (!database
.GetLinksToTvShow(dbId
, ids
))
2086 for (unsigned int i
= 0; i
< ids
.size(); ++i
)
2089 database
.GetTvShowInfo("", tag
, ids
[i
], 0 , VideoDbDetailsNone
);
2090 CFileItemPtr
show(new CFileItem(tag
));
2096 database
.GetTvShowsNav("videodb://tvshows/titles", list
);
2098 // remove already linked shows
2099 std::vector
<int> ids
;
2100 if (!database
.GetLinksToTvShow(dbId
, ids
))
2103 for (int i
= 0; i
< list
.Size(); )
2106 for (j
= 0; j
< ids
.size(); ++j
)
2108 if (list
[i
]->GetVideoInfoTag()->m_iDbId
== ids
[j
])
2111 if (j
== ids
.size())
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
);
2126 pDialog
->SetItems(list
);
2127 pDialog
->SetHeading(CVariant
{20356});
2129 iSelectedLabel
= pDialog
->GetSelectedItem();
2133 if (iSelectedLabel
> -1 && iSelectedLabel
< list
.Size())
2134 return database
.LinkMovieToTvshow(dbId
, list
[iSelectedLabel
]->GetVideoInfoTag()->m_iDbId
, bRemove
);
2139 void CGUIDialogVideoInfo::ShowFor(const CFileItem
& item
)
2141 auto window
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIWindowVideoNav
>(WINDOW_VIDEO_NAV
);
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
);