2 * Copyright (C) 2023 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 "GUIDialogVideoManagerVersions.h"
12 #include "GUIUserMessages.h"
13 #include "ServiceBroker.h"
15 #include "cores/VideoPlayer/DVDFileInfo.h"
16 #include "dialogs/GUIDialogFileBrowser.h"
17 #include "dialogs/GUIDialogOK.h"
18 #include "dialogs/GUIDialogSelect.h"
19 #include "dialogs/GUIDialogYesNo.h"
20 #include "guilib/GUIComponent.h"
21 #include "guilib/GUIWindowManager.h"
22 #include "guilib/LocalizeStrings.h"
23 #include "settings/MediaSourceSettings.h"
24 #include "settings/Settings.h"
25 #include "settings/SettingsComponent.h"
26 #include "storage/MediaManager.h"
27 #include "utils/FileExtensionProvider.h"
28 #include "utils/StringUtils.h"
29 #include "utils/log.h"
30 #include "video/VideoManagerTypes.h"
31 #include "video/VideoThumbLoader.h"
36 static constexpr unsigned int CONTROL_BUTTON_ADD_VERSION
= 22;
37 static constexpr unsigned int CONTROL_BUTTON_SET_DEFAULT
= 25;
39 CGUIDialogVideoManagerVersions::CGUIDialogVideoManagerVersions()
40 : CGUIDialogVideoManager(WINDOW_DIALOG_MANAGE_VIDEO_VERSIONS
),
41 m_defaultVideoVersion(std::make_shared
<CFileItem
>())
45 VideoAssetType
CGUIDialogVideoManagerVersions::GetVideoAssetType()
47 return VideoAssetType::VERSION
;
50 bool CGUIDialogVideoManagerVersions::OnMessage(CGUIMessage
& message
)
52 switch (message
.GetMessage())
56 const int control
{message
.GetSenderId()};
57 if (control
== CONTROL_BUTTON_ADD_VERSION
)
59 if (AddVideoVersion())
61 // refresh data and controls
64 m_hasUpdatedItems
= true;
67 else if (control
== CONTROL_BUTTON_SET_DEFAULT
)
75 return CGUIDialogVideoManager::OnMessage(message
);
78 void CGUIDialogVideoManagerVersions::Clear()
80 m_defaultVideoVersion
= std::make_shared
<CFileItem
>();
81 CGUIDialogVideoManager::Clear();
84 void CGUIDialogVideoManagerVersions::UpdateButtons()
86 CGUIDialogVideoManager::UpdateButtons();
89 CONTROL_ENABLE(CONTROL_BUTTON_ADD_VERSION
);
91 // Enabled for non-default version only
92 if (m_selectedVideoAsset
->GetVideoInfoTag()->m_iDbId
==
93 m_defaultVideoVersion
->GetVideoInfoTag()->m_iDbId
)
96 CONTROL_DISABLE(CONTROL_BUTTON_SET_DEFAULT
);
101 CONTROL_ENABLE(CONTROL_BUTTON_SET_DEFAULT
);
104 // Enabled if list not empty
105 if (m_videoAssetsList
->IsEmpty())
107 SET_CONTROL_FOCUS(CONTROL_BUTTON_ADD_VERSION
, 0);
111 void CGUIDialogVideoManagerVersions::UpdateDefaultVideoVersionSelection()
113 // find new item in list and select it
114 const int defaultDbId
{m_defaultVideoVersion
->GetVideoInfoTag()->m_iDbId
};
115 for (const auto& item
: *m_videoAssetsList
)
117 item
->Select(item
->GetVideoInfoTag()->m_iDbId
== defaultDbId
);
121 void CGUIDialogVideoManagerVersions::Refresh()
123 CGUIDialogVideoManager::Refresh();
125 // update default video version
126 const int dbId
{m_videoAsset
->GetVideoInfoTag()->m_iDbId
};
127 const VideoDbContentType itemType
{m_videoAsset
->GetVideoContentType()};
128 m_database
.GetDefaultVideoVersion(itemType
, dbId
, *m_defaultVideoVersion
);
130 UpdateDefaultVideoVersionSelection();
133 void CGUIDialogVideoManagerVersions::SetVideoAsset(const std::shared_ptr
<CFileItem
>& item
)
135 CGUIDialogVideoManager::SetVideoAsset(item
);
137 SetSelectedVideoAsset(m_defaultVideoVersion
);
140 void CGUIDialogVideoManagerVersions::Remove()
142 const MediaType mediaType
{m_videoAsset
->GetVideoInfoTag()->m_type
};
144 // default video version is not allowed
145 if (m_database
.IsDefaultVideoVersion(m_selectedVideoAsset
->GetVideoInfoTag()->m_iDbId
))
147 CGUIDialogOK::ShowAndGetInput(CVariant
{40018}, CVariant
{40019});
151 CGUIDialogVideoManager::Remove();
154 void CGUIDialogVideoManagerVersions::SetDefault()
156 // set the selected video version as default
157 SetDefaultVideoVersion(*m_selectedVideoAsset
);
159 const int dbId
{m_videoAsset
->GetVideoInfoTag()->m_iDbId
};
160 const VideoDbContentType itemType
{m_videoAsset
->GetVideoContentType()};
162 // update our default video version
163 m_database
.GetDefaultVideoVersion(itemType
, dbId
, *m_defaultVideoVersion
);
166 UpdateDefaultVideoVersionSelection();
169 void CGUIDialogVideoManagerVersions::SetDefaultVideoVersion(const CFileItem
& version
)
171 const int dbId
{m_videoAsset
->GetVideoInfoTag()->m_iDbId
};
172 const VideoDbContentType itemType
{m_videoAsset
->GetVideoContentType()};
174 // set the specified video version as default
175 m_database
.SetDefaultVideoVersion(itemType
, dbId
, version
.GetVideoInfoTag()->m_iDbId
);
177 // update the video item
178 m_videoAsset
->SetPath(version
.GetPath());
179 m_videoAsset
->SetDynPath(version
.GetPath());
181 // update video details since we changed the video file for the item
182 m_database
.GetDetailsByTypeAndId(*m_videoAsset
, itemType
, dbId
);
184 // notify all windows to update the file item
185 CGUIMessage msg
{GUI_MSG_NOTIFY_ALL
, 0, 0, GUI_MSG_UPDATE_ITEM
,
186 GUI_MSG_FLAG_FORCE_UPDATE
, m_videoAsset
};
187 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg
);
190 bool CGUIDialogVideoManagerVersions::AddVideoVersion()
192 if (!m_videoAsset
|| !m_videoAsset
->HasVideoInfoTag())
194 CLog::LogF(LOGERROR
, "invalid video asset");
198 CVideoDatabase videoDb
;
201 CLog::LogF(LOGERROR
, "Failed to open video database!");
206 if (!GetSimilarMovies(m_videoAsset
, items
, videoDb
))
209 if (items
.Size() == 0)
211 // No button = browse library
212 // Yes button = browse files
213 // Custom button = cancel
215 const int dlgResult
{CGUIDialogYesNo::ShowAndGetInput(
216 CVariant
{40030}, CVariant
{40032}, CVariant
{40029}, CVariant
{40028}, CVariant
{222},
217 CGUIDialogYesNo::NO_TIMEOUT
)};
221 case CGUIDialogYesNo::DIALOG_RESULT_CANCEL
:
222 case CGUIDialogYesNo::DIALOG_RESULT_CUSTOM
:
223 // Dialog dismissed or Cancel button
226 case CGUIDialogYesNo::DIALOG_RESULT_NO
:
229 if (!GetAllOtherMovies(m_videoAsset
, items
, videoDb
))
232 const auto tag
{m_videoAsset
->GetVideoInfoTag()};
234 return ChooseVideoAndConvertToVideoVersion(items
, m_videoAsset
->GetVideoContentType(),
235 tag
->m_type
, tag
->m_iDbId
, videoDb
,
239 case CGUIDialogYesNo::DIALOG_RESULT_YES
:
241 return AddVideoVersionFilePicker();
244 CLog::LogF(LOGERROR
, "Unknown return value {} from CGUIDialogYesNo", dlgResult
);
248 CGUIDialogSelect
* dialog
{CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogSelect
>(
249 WINDOW_DIALOG_SELECT
)};
253 CLog::LogF(LOGERROR
, "Unable to get WINDOW_DIALOG_SELECT instance!");
258 CVideoThumbLoader loader
;
262 dialog
->SetItems(items
);
263 dialog
->SetHeading(40030);
264 dialog
->SetUseDetails(true);
265 dialog
->EnableButton(true, 40028); // Browse files
266 dialog
->EnableButton2(true, 40029); // Browse library
269 if (loader
.IsLoading())
272 if (dialog
->IsConfirmed())
274 // A similar movie was selected
275 return AddSimilarMovieAsVersion(dialog
->GetSelectedFileItem());
277 else if (dialog
->IsButtonPressed())
279 // User wants to browse the files
280 return AddVideoVersionFilePicker();
282 else if (dialog
->IsButton2Pressed())
284 // User wants to browse the library
285 if (!GetAllOtherMovies(m_videoAsset
, items
, videoDb
))
288 const auto tag
{m_videoAsset
->GetVideoInfoTag()};
290 return ChooseVideoAndConvertToVideoVersion(items
, m_videoAsset
->GetVideoContentType(),
291 tag
->m_type
, tag
->m_iDbId
, videoDb
,
297 bool CGUIDialogVideoManagerVersions::ManageVideoVersions(const std::shared_ptr
<CFileItem
>& item
)
299 CGUIDialogVideoManagerVersions
* dialog
{
300 CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogVideoManagerVersions
>(
301 WINDOW_DIALOG_MANAGE_VIDEO_VERSIONS
)};
304 CLog::LogF(LOGERROR
, "Unable to get WINDOW_DIALOG_MANAGE_VIDEO_VERSIONS instance!");
308 dialog
->SetVideoAsset(item
);
310 return dialog
->HasUpdatedItems();
313 bool CGUIDialogVideoManagerVersions::ChooseVideoAndConvertToVideoVersion(
314 CFileItemList
& items
,
315 VideoDbContentType itemType
,
316 const std::string
& mediaType
,
318 CVideoDatabase
& videoDb
,
321 if (items
.Size() == 0)
323 CGUIDialogOK::ShowAndGetInput(role
== MediaRole::NewVersion
? 40002 : 40030, 40031);
328 CGUIDialogSelect
* dialog
{CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogSelect
>(
329 WINDOW_DIALOG_SELECT
)};
332 CLog::LogF(LOGERROR
, "Unable to get WINDOW_DIALOG_SELECT instance!");
337 CVideoThumbLoader loader
;
341 dialog
->SetItems(items
);
342 dialog
->SetHeading(role
== MediaRole::NewVersion
? 40002 : 40030);
343 dialog
->SetUseDetails(true);
346 if (loader
.IsLoading())
349 if (!dialog
->IsConfirmed())
352 const std::shared_ptr
<CFileItem
> selectedItem
{dialog
->GetSelectedFileItem()};
356 // choose a video version for the video
357 const int idVideoVersion
{ChooseVideoAsset(selectedItem
, VideoAssetType::VERSION
)};
358 if (idVideoVersion
< 0)
361 int sourceDbId
, targetDbId
;
364 case MediaRole::NewVersion
:
366 targetDbId
= selectedItem
->GetVideoInfoTag()->m_iDbId
;
368 case MediaRole::Parent
:
369 sourceDbId
= selectedItem
->GetVideoInfoTag()->m_iDbId
;
376 return videoDb
.ConvertVideoToVersion(itemType
, sourceDbId
, targetDbId
, idVideoVersion
);
379 bool CGUIDialogVideoManagerVersions::GetAllOtherMovies(const std::shared_ptr
<CFileItem
>& item
,
381 CVideoDatabase
& videoDb
)
383 if (!item
|| !item
->HasVideoInfoTag())
387 const std::string videoTitlesDir
{StringUtils::Format(
388 "videodb://{}/titles", CMediaTypes::ToPlural(item
->GetVideoInfoTag()->m_type
))};
392 if (item
->GetVideoContentType() == VideoDbContentType::MOVIES
)
393 videoDb
.GetMoviesNav(videoTitlesDir
, list
);
400 list
.Sort(SortByLabel
, SortOrderAscending
,
401 CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
402 CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING
)
403 ? SortAttributeIgnoreArticle
404 : SortAttributeNone
);
406 if (!PostProcessList(list
, item
->GetVideoInfoTag()->m_iDbId
))
412 bool CGUIDialogVideoManagerVersions::ProcessVideoVersion(VideoDbContentType itemType
, int dbId
)
414 if (itemType
!= VideoDbContentType::MOVIES
)
417 CVideoDatabase videodb
;
420 CLog::LogF(LOGERROR
, "Failed to open video database!");
425 if (!videodb
.GetDetailsByTypeAndId(item
, itemType
, dbId
))
429 videodb
.GetSameVideoItems(item
, list
);
434 const MediaType mediaType
{item
.GetVideoInfoTag()->m_type
};
437 videodb
.GetFilePathById(dbId
, path
, itemType
);
439 if (!CGUIDialogYesNo::ShowAndGetInput(
440 CVariant
{40008}, StringUtils::Format(g_localizeStrings
.Get(40009),
441 item
.GetVideoInfoTag()->GetTitle(), path
)))
446 if (!PostProcessList(list
, dbId
))
449 return ChooseVideoAndConvertToVideoVersion(list
, itemType
, mediaType
, dbId
, videodb
,
450 MediaRole::NewVersion
);
453 bool CGUIDialogVideoManagerVersions::AddVideoVersionFilePicker()
455 // @todo: combine with extras add file logic, structured similarly and sharing most logic.
457 const MediaType mediaType
{m_videoAsset
->GetVideoInfoTag()->m_type
};
459 // prompt to choose a video file
460 VECSOURCES sources
{*CMediaSourceSettings::GetInstance().GetSources("files")};
462 CServiceBroker::GetMediaManager().GetLocalDrives(sources
);
463 CServiceBroker::GetMediaManager().GetNetworkLocations(sources
);
464 AppendItemFolderToFileBrowserSources(sources
);
467 if (CGUIDialogFileBrowser::ShowAndGetFile(
468 sources
, CServiceBroker::GetFileExtensionProvider().GetVideoExtensions(),
469 g_localizeStrings
.Get(40014), path
))
471 const int dbId
{m_videoAsset
->GetVideoInfoTag()->m_iDbId
};
472 const VideoDbContentType itemType
{m_videoAsset
->GetVideoContentType()};
474 const VideoAssetInfo newAsset
{m_database
.GetVideoVersionInfo(path
)};
476 // @todo look only for a version identified by idFile instead of retrieving all versions
477 if (newAsset
.m_idFile
!= -1 && newAsset
.m_assetTypeId
!= -1)
479 // The video already is an asset of the movie
480 if (newAsset
.m_idMedia
== dbId
&&
481 newAsset
.m_mediaType
== m_videoAsset
->GetVideoInfoTag()->m_type
)
483 unsigned int msgid
{};
485 if (newAsset
.m_assetType
== VideoAssetType::VERSION
)
486 msgid
= 40016; // video is a version of the movie
487 else if (newAsset
.m_assetType
== VideoAssetType::EXTRA
)
488 msgid
= 40026; // video is an extra of the movie
491 CLog::LogF(LOGERROR
, "unexpected asset type {}", static_cast<int>(newAsset
.m_assetType
));
495 CGUIDialogOK::ShowAndGetInput(
497 StringUtils::Format(g_localizeStrings
.Get(msgid
), newAsset
.m_assetTypeName
));
501 // The video is an asset of another movie
503 // The video is an extra, ask for confirmation
504 if (newAsset
.m_assetType
== VideoAssetType::EXTRA
&&
505 !CGUIDialogYesNo::ShowAndGetInput(CVariant
{40014},
506 StringUtils::Format(g_localizeStrings
.Get(40035))))
511 std::string videoTitle
;
512 if (newAsset
.m_mediaType
== MediaTypeMovie
)
514 videoTitle
= m_database
.GetMovieTitle(newAsset
.m_idMedia
);
520 unsigned int msgid
{};
522 if (newAsset
.m_assetType
== VideoAssetType::VERSION
)
523 msgid
= 40017; // video is a version of another movie
524 else if (newAsset
.m_assetType
== VideoAssetType::EXTRA
)
525 msgid
= 40027; // video is an extra of another movie
528 CLog::LogF(LOGERROR
, "unexpected asset type {}", static_cast<int>(newAsset
.m_assetType
));
532 if (!CGUIDialogYesNo::ShowAndGetInput(
533 CVariant
{40014}, StringUtils::Format(g_localizeStrings
.Get(msgid
),
534 newAsset
.m_assetTypeName
, videoTitle
)))
540 // Additional constraints for the conversion of a movie version
541 if (newAsset
.m_assetType
== VideoAssetType::VERSION
&&
542 m_database
.IsDefaultVideoVersion(newAsset
.m_idFile
))
545 m_database
.GetVideoVersions(itemType
, newAsset
.m_idMedia
, list
, newAsset
.m_assetType
);
549 // cannot add the default version of a movie with multiple versions to another movie
550 CGUIDialogOK::ShowAndGetInput(CVariant
{40014}, CVariant
{40033});
555 if (newAsset
.m_mediaType
== MediaTypeMovie
)
557 // @todo: should be in a database transaction with the addition as a new asset below
558 m_database
.DeleteMovie(newAsset
.m_idMedia
);
566 // @todo: should be in a database transaction with the addition as a new asset below
567 if (!m_database
.RemoveVideoVersion(newAsset
.m_idFile
))
572 CFileItem item
{path
, false};
574 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
575 CSettings::SETTING_MYVIDEOS_EXTRACTFLAGS
))
577 CDVDFileInfo::GetFileStreamDetails(&item
);
578 CLog::LogF(LOGDEBUG
, "Extracted filestream details from video file {}",
579 CURL::GetRedacted(item
.GetPath()));
582 const int idNewVideoVersion
{ChooseVideoAsset(m_videoAsset
, VideoAssetType::VERSION
)};
583 if (idNewVideoVersion
== -1)
586 m_database
.AddPrimaryVideoVersion(itemType
, dbId
, idNewVideoVersion
, item
);
593 bool CGUIDialogVideoManagerVersions::GetSimilarMovies(const std::shared_ptr
<CFileItem
>& item
,
595 CVideoDatabase
& videoDb
)
599 videoDb
.GetSameVideoItems(*item
, list
);
607 if (!PostProcessList(list
, item
->GetVideoInfoTag()->m_iDbId
))
613 bool CGUIDialogVideoManagerVersions::AddSimilarMovieAsVersion(
614 const std::shared_ptr
<CFileItem
> itemMovie
)
616 // A movie with versions cannot be turned into a version
617 if (itemMovie
->GetVideoInfoTag()->HasVideoVersions())
619 CGUIDialogOK::ShowAndGetInput(CVariant
{40005}, CVariant
{40006});
623 // choose a video version type for the video
624 const int idVideoVersion
{ChooseVideoAsset(itemMovie
, VideoAssetType::VERSION
)};
625 if (idVideoVersion
< 0)
628 CVideoDatabase videoDb
;
631 CLog::LogF(LOGERROR
, "Failed to open video database!");
635 const int sourceDbId
{itemMovie
->GetVideoInfoTag()->m_iDbId
};
636 const int targetDbId
{m_videoAsset
->GetVideoInfoTag()->m_iDbId
};
637 return videoDb
.ConvertVideoToVersion(VideoDbContentType::MOVIES
, sourceDbId
, targetDbId
,
641 bool CGUIDialogVideoManagerVersions::PostProcessList(CFileItemList
& list
, int dbId
)
643 // Exclude the provided dbId and decorate the items
646 while (i
< list
.Size())
648 const auto item
{list
[i
]};
649 const auto itemtag
{item
->GetVideoInfoTag()};
651 if (itemtag
->m_iDbId
== dbId
)
654 // i is not incremented for the next iteration because the removal shifted what would have
655 // been the next item into the current position.
659 item
->SetLabel2(itemtag
->m_strFileNameAndPath
);