[PVR][Estuary] Timer settings dialog: Show client name in timer type selection dialog...
[xbmc.git] / xbmc / video / dialogs / GUIDialogVideoBookmarks.cpp
blobf2669d39dc8433215db45deff0388f991e306c31
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "GUIDialogVideoBookmarks.h"
11 #include "FileItem.h"
12 #include "ServiceBroker.h"
13 #include "TextureCache.h"
14 #include "Util.h"
15 #include "application/Application.h"
16 #include "application/ApplicationComponents.h"
17 #include "application/ApplicationPlayer.h"
18 #include "dialogs/GUIDialogContextMenu.h"
19 #include "dialogs/GUIDialogKaiToast.h"
20 #include "guilib/GUIComponent.h"
21 #include "guilib/GUIWindowManager.h"
22 #include "guilib/LocalizeStrings.h"
23 #include "input/actions/Action.h"
24 #include "input/actions/ActionIDs.h"
25 #include "messaging/ApplicationMessenger.h"
26 #include "pictures/Picture.h"
27 #include "profiles/ProfileManager.h"
28 #include "settings/AdvancedSettings.h"
29 #include "settings/Settings.h"
30 #include "settings/SettingsComponent.h"
31 #include "utils/Crc32.h"
32 #include "utils/FileUtils.h"
33 #include "utils/StringUtils.h"
34 #include "utils/URIUtils.h"
35 #include "utils/Variant.h"
36 #include "utils/log.h"
37 #include "video/VideoDatabase.h"
38 #include "video/VideoThumbLoader.h"
39 #include "view/ViewState.h"
41 #include <mutex>
42 #include <string>
43 #include <vector>
45 #define BOOKMARK_THUMB_WIDTH CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_imageRes
47 #define CONTROL_ADD_BOOKMARK 2
48 #define CONTROL_CLEAR_BOOKMARKS 3
49 #define CONTROL_ADD_EPISODE_BOOKMARK 4
51 #define CONTROL_THUMBS 11
53 CGUIDialogVideoBookmarks::CGUIDialogVideoBookmarks()
54 : CGUIDialog(WINDOW_DIALOG_VIDEO_BOOKMARKS, "VideoOSDBookmarks.xml"),
55 CJobQueue(false, 1, CJob::PRIORITY_NORMAL)
57 m_vecItems = new CFileItemList;
58 m_loadType = LOAD_EVERY_TIME;
59 m_jobsStarted = 0;
62 CGUIDialogVideoBookmarks::~CGUIDialogVideoBookmarks()
64 delete m_vecItems;
67 bool CGUIDialogVideoBookmarks::OnMessage(CGUIMessage& message)
69 switch ( message.GetMessage() )
71 case GUI_MSG_WINDOW_DEINIT:
73 CUtil::DeleteVideoDatabaseDirectoryCache();
74 Clear();
76 break;
78 case GUI_MSG_WINDOW_INIT:
80 // don't init this dialog if we don't playback a file
81 const auto& components = CServiceBroker::GetAppComponents();
82 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
83 if (!appPlayer->IsPlaying())
84 return false;
86 CGUIWindow::OnMessage(message);
87 Update();
88 return true;
90 break;
92 case GUI_MSG_CLICKED:
94 int iControl = message.GetSenderId();
95 if (iControl == CONTROL_ADD_BOOKMARK)
97 AddBookmark();
98 Update();
100 else if (iControl == CONTROL_CLEAR_BOOKMARKS)
102 ClearBookmarks();
104 else if (iControl == CONTROL_ADD_EPISODE_BOOKMARK)
106 AddEpisodeBookmark();
107 Update();
109 else if (m_viewControl.HasControl(iControl)) // list/thumb control
111 int iItem = m_viewControl.GetSelectedItem();
112 int iAction = message.GetParam1();
113 if (iAction == ACTION_DELETE_ITEM)
115 Delete(iItem);
117 else if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
119 GotoBookmark(iItem);
123 break;
124 case GUI_MSG_SETFOCUS:
126 if (m_viewControl.HasControl(message.GetControlId()) && m_viewControl.GetCurrentControl() != message.GetControlId())
128 m_viewControl.SetFocused();
129 return true;
132 break;
133 case GUI_MSG_REFRESH_LIST:
135 switch (message.GetParam1())
137 case 0:
138 OnRefreshList();
139 break;
140 case 1:
141 UpdateItem(message.GetParam2());
142 break;
143 default:
144 break;
147 break;
150 return CGUIDialog::OnMessage(message);
153 bool CGUIDialogVideoBookmarks::OnAction(const CAction &action)
155 switch(action.GetID())
157 case ACTION_CONTEXT_MENU:
158 case ACTION_MOUSE_RIGHT_CLICK:
160 OnPopupMenu(m_viewControl.GetSelectedItem());
161 return true;
164 return CGUIDialog::OnAction(action);
168 void CGUIDialogVideoBookmarks::OnPopupMenu(int item)
170 if (item < 0 || item >= (int) m_bookmarks.size())
171 return;
173 // highlight the item
174 (*m_vecItems)[item]->Select(true);
176 CContextButtons choices;
177 choices.Add(1, (m_bookmarks[item].type == CBookmark::EPISODE ? 20405 : 20404)); // "Remove episode bookmark" or "Remove bookmark"
179 int button = CGUIDialogContextMenu::ShowAndGetChoice(choices);
181 // unhighlight the item
182 (*m_vecItems)[item]->Select(false);
184 if (button == 1)
185 Delete(item);
188 void CGUIDialogVideoBookmarks::Delete(int item)
190 if ( item>=0 && (unsigned)item < m_bookmarks.size() )
192 CVideoDatabase videoDatabase;
193 videoDatabase.Open();
194 std::string path(g_application.CurrentFile());
195 if (g_application.CurrentFileItem().HasProperty("original_listitem_url") &&
196 !URIUtils::IsVideoDb(g_application.CurrentFileItem().GetProperty("original_listitem_url").asString()))
197 path = g_application.CurrentFileItem().GetProperty("original_listitem_url").asString();
198 videoDatabase.ClearBookMarkOfFile(path, m_bookmarks[item], m_bookmarks[item].type);
199 videoDatabase.Close();
200 CUtil::DeleteVideoDatabaseDirectoryCache();
202 Update();
205 void CGUIDialogVideoBookmarks::UpdateItem(unsigned int chapterIdx)
207 std::unique_lock<CCriticalSection> lock(m_refreshSection);
209 int itemPos = 0;
210 for (const auto& item : *m_vecItems)
212 if (chapterIdx == item->GetProperty("chapter").asInteger())
213 break;
214 itemPos++;
217 if (itemPos < m_vecItems->Size())
219 std::string time = StringUtils::Format("chapter://{}/{}", m_filePath, chapterIdx);
220 std::string cachefile = CServiceBroker::GetTextureCache()->GetCachedPath(
221 CServiceBroker::GetTextureCache()->GetCacheFile(time) + ".jpg");
222 if (CFileUtils::Exists(cachefile))
224 (*m_vecItems)[itemPos]->SetArt("thumb", cachefile);
229 void CGUIDialogVideoBookmarks::OnRefreshList()
231 m_bookmarks.clear();
232 std::vector<CFileItemPtr> items;
234 // open the d/b and retrieve the bookmarks for the current movie
235 m_filePath = g_application.CurrentFile();
236 if (g_application.CurrentFileItem().HasProperty("original_listitem_url") &&
237 !URIUtils::IsVideoDb(g_application.CurrentFileItem().GetProperty("original_listitem_url").asString()))
238 m_filePath = g_application.CurrentFileItem().GetProperty("original_listitem_url").asString();
240 CVideoDatabase videoDatabase;
241 videoDatabase.Open();
242 videoDatabase.GetBookMarksForFile(m_filePath, m_bookmarks);
243 videoDatabase.GetBookMarksForFile(m_filePath, m_bookmarks, CBookmark::EPISODE, true);
244 videoDatabase.Close();
246 std::unique_lock<CCriticalSection> lock(m_refreshSection);
247 m_vecItems->Clear();
249 // cycle through each stored bookmark and add it to our list control
250 for (unsigned int i = 0; i < m_bookmarks.size(); ++i)
252 std::string bookmarkTime;
253 if (m_bookmarks[i].type == CBookmark::EPISODE)
254 bookmarkTime = StringUtils::Format("{} {} {} {}", g_localizeStrings.Get(20373),
255 m_bookmarks[i].seasonNumber, g_localizeStrings.Get(20359),
256 m_bookmarks[i].episodeNumber);
257 else
258 bookmarkTime = StringUtils::SecondsToTimeString((long)m_bookmarks[i].timeInSeconds, TIME_FORMAT_HH_MM_SS);
260 CFileItemPtr item(new CFileItem(StringUtils::Format(g_localizeStrings.Get(299), i + 1)));
261 item->SetLabel2(bookmarkTime);
262 item->SetArt("thumb", m_bookmarks[i].thumbNailImage);
263 item->SetProperty("resumepoint", m_bookmarks[i].timeInSeconds);
264 item->SetProperty("playerstate", m_bookmarks[i].playerState);
265 item->SetProperty("isbookmark", "true");
266 items.push_back(item);
269 // add chapters if around
270 const auto& components = CServiceBroker::GetAppComponents();
271 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
272 for (int i = 1; i <= appPlayer->GetChapterCount(); ++i)
274 std::string chapterName;
275 appPlayer->GetChapterName(chapterName, i);
277 int64_t pos = appPlayer->GetChapterPos(i);
278 std::string time = StringUtils::SecondsToTimeString((long) pos, TIME_FORMAT_HH_MM_SS);
280 if (chapterName.empty() ||
281 StringUtils::StartsWithNoCase(chapterName, time) ||
282 StringUtils::IsNaturalNumber(chapterName))
283 chapterName = StringUtils::Format(g_localizeStrings.Get(25010), i);
285 CFileItemPtr item(new CFileItem(chapterName));
286 item->SetLabel2(time);
288 std::string chapterPath = StringUtils::Format("chapter://{}/{}", m_filePath, i);
289 std::string cachefile = CServiceBroker::GetTextureCache()->GetCachedPath(
290 CServiceBroker::GetTextureCache()->GetCacheFile(chapterPath) + ".jpg");
291 if (CFileUtils::Exists(cachefile))
292 item->SetArt("thumb", cachefile);
293 else if (i > m_jobsStarted && CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MYVIDEOS_EXTRACTCHAPTERTHUMBS))
295 CFileItem item(m_filePath, false);
296 CJob* job = new CThumbExtractor(item, m_filePath, true, chapterPath, pos * 1000, false);
297 AddJob(job);
298 m_mapJobsChapter[job] = i;
299 m_jobsStarted++;
302 item->SetProperty("chapter", i);
303 item->SetProperty("resumepoint", static_cast<double>(pos));
304 item->SetProperty("ischapter", "true");
305 items.push_back(item);
308 // sort items by resume point
309 std::sort(items.begin(), items.end(), [](const CFileItemPtr &item1, const CFileItemPtr &item2) {
310 return item1->GetProperty("resumepoint").asDouble() < item2->GetProperty("resumepoint").asDouble();
313 // add items to file list and mark the proper item as selected if the current playtime is above
314 int selectedItemIndex = 0;
315 double playTime = g_application.GetTime();
316 for (auto& item : items)
318 m_vecItems->Add(item);
319 if (playTime >= item->GetProperty("resumepoint").asDouble())
320 selectedItemIndex = m_vecItems->Size() - 1;
323 m_viewControl.SetItems(*m_vecItems);
324 m_viewControl.SetSelectedItem(selectedItemIndex);
327 void CGUIDialogVideoBookmarks::Update()
329 CVideoDatabase videoDatabase;
330 videoDatabase.Open();
332 if (g_application.CurrentFileItem().HasVideoInfoTag() && g_application.CurrentFileItem().GetVideoInfoTag()->m_iEpisode > -1)
334 std::vector<CVideoInfoTag> episodes;
335 videoDatabase.GetEpisodesByFile(g_application.CurrentFile(),episodes);
336 if (episodes.size() > 1)
338 CONTROL_ENABLE(CONTROL_ADD_EPISODE_BOOKMARK);
340 else
342 CONTROL_DISABLE(CONTROL_ADD_EPISODE_BOOKMARK);
345 else
347 CONTROL_DISABLE(CONTROL_ADD_EPISODE_BOOKMARK);
351 m_viewControl.SetCurrentView(DEFAULT_VIEW_ICONS);
353 // empty the list ready for population
354 Clear();
356 OnRefreshList();
358 videoDatabase.Close();
361 void CGUIDialogVideoBookmarks::Clear()
363 m_viewControl.Clear();
364 m_vecItems->Clear();
367 void CGUIDialogVideoBookmarks::GotoBookmark(int item)
369 auto& components = CServiceBroker::GetAppComponents();
370 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
371 if (item < 0 || item >= m_vecItems->Size() || !appPlayer->HasPlayer())
372 return;
374 CFileItemPtr fileItem = m_vecItems->Get(item);
375 int chapter = static_cast<int>(fileItem->GetProperty("chapter").asInteger());
376 if (chapter <= 0)
378 appPlayer->SetPlayerState(fileItem->GetProperty("playerstate").asString());
379 g_application.SeekTime(fileItem->GetProperty("resumepoint").asDouble());
381 else
382 appPlayer->SeekChapter(chapter);
384 Close();
387 void CGUIDialogVideoBookmarks::ClearBookmarks()
389 CVideoDatabase videoDatabase;
390 videoDatabase.Open();
391 std::string path = g_application.CurrentFile();
392 if (g_application.CurrentFileItem().HasProperty("original_listitem_url") &&
393 !URIUtils::IsVideoDb(g_application.CurrentFileItem().GetProperty("original_listitem_url").asString()))
394 path = g_application.CurrentFileItem().GetProperty("original_listitem_url").asString();
395 videoDatabase.ClearBookMarksOfFile(path, CBookmark::STANDARD);
396 videoDatabase.ClearBookMarksOfFile(path, CBookmark::RESUME);
397 videoDatabase.ClearBookMarksOfFile(path, CBookmark::EPISODE);
398 videoDatabase.Close();
399 Update();
402 bool CGUIDialogVideoBookmarks::AddBookmark(CVideoInfoTag* tag)
404 CVideoDatabase videoDatabase;
405 CBookmark bookmark;
406 bookmark.timeInSeconds = (int)g_application.GetTime();
407 bookmark.totalTimeInSeconds = (int)g_application.GetTotalTime();
409 auto& components = CServiceBroker::GetAppComponents();
410 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
412 if (appPlayer->HasPlayer())
413 bookmark.playerState = appPlayer->GetPlayerState();
414 else
415 bookmark.playerState.clear();
417 bookmark.player = g_application.GetCurrentPlayer();
419 // create the thumbnail image
420 float aspectRatio = appPlayer->GetRenderAspectRatio();
421 int width = BOOKMARK_THUMB_WIDTH;
422 int height = (int)(BOOKMARK_THUMB_WIDTH / aspectRatio);
423 if (height > (int)BOOKMARK_THUMB_WIDTH)
425 height = BOOKMARK_THUMB_WIDTH;
426 width = (int)(BOOKMARK_THUMB_WIDTH * aspectRatio);
430 uint8_t *pixels = (uint8_t*)malloc(height * width * 4);
431 unsigned int captureId = appPlayer->RenderCaptureAlloc();
433 appPlayer->RenderCapture(captureId, width, height, CAPTUREFLAG_IMMEDIATELY);
434 bool hasImage = appPlayer->RenderCaptureGetPixels(captureId, 1000, pixels, height * width * 4);
436 if (hasImage)
438 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
440 auto crc = Crc32::ComputeFromLowerCase(g_application.CurrentFile());
441 bookmark.thumbNailImage =
442 StringUtils::Format("{:08x}_{}.jpg", crc, (int)bookmark.timeInSeconds);
443 bookmark.thumbNailImage = URIUtils::AddFileToFolder(profileManager->GetBookmarksThumbFolder(), bookmark.thumbNailImage);
445 if (!CPicture::CreateThumbnailFromSurface(pixels, width, height, width * 4,
446 bookmark.thumbNailImage))
448 bookmark.thumbNailImage.clear();
450 else
451 CLog::Log(LOGERROR,"CGUIDialogVideoBookmarks: failed to create thumbnail");
453 appPlayer->RenderCaptureRelease(captureId);
455 else
456 CLog::Log(LOGERROR,"CGUIDialogVideoBookmarks: failed to create thumbnail 2");
458 free(pixels);
460 videoDatabase.Open();
461 if (tag)
462 videoDatabase.AddBookMarkForEpisode(*tag, bookmark);
463 else
465 std::string path = g_application.CurrentFile();
466 if (g_application.CurrentFileItem().HasProperty("original_listitem_url") &&
467 !URIUtils::IsVideoDb(g_application.CurrentFileItem().GetProperty("original_listitem_url").asString()))
468 path = g_application.CurrentFileItem().GetProperty("original_listitem_url").asString();
469 videoDatabase.AddBookMarkToFile(path, bookmark, CBookmark::STANDARD);
471 videoDatabase.Close();
472 return true;
475 void CGUIDialogVideoBookmarks::OnWindowLoaded()
477 CGUIDialog::OnWindowLoaded();
478 m_viewControl.Reset();
479 m_viewControl.SetParentWindow(GetID());
480 m_viewControl.AddView(GetControl(CONTROL_THUMBS));
481 m_jobsStarted = 0;
482 m_mapJobsChapter.clear();
483 m_vecItems->Clear();
486 void CGUIDialogVideoBookmarks::OnWindowUnload()
488 //stop running thumb extraction jobs
489 CancelJobs();
490 m_mapJobsChapter.clear();
491 m_vecItems->Clear();
492 CGUIDialog::OnWindowUnload();
493 m_viewControl.Reset();
496 CGUIControl *CGUIDialogVideoBookmarks::GetFirstFocusableControl(int id)
498 if (m_viewControl.HasControl(id))
499 id = m_viewControl.GetCurrentControl();
500 return CGUIWindow::GetFirstFocusableControl(id);
503 bool CGUIDialogVideoBookmarks::AddEpisodeBookmark()
505 std::vector<CVideoInfoTag> episodes;
506 CVideoDatabase videoDatabase;
507 videoDatabase.Open();
508 videoDatabase.GetEpisodesByFile(g_application.CurrentFile(), episodes);
509 videoDatabase.Close();
510 if (!episodes.empty())
512 CContextButtons choices;
513 for (unsigned int i=0; i < episodes.size(); ++i)
515 std::string strButton =
516 StringUtils::Format("{} {}, {} {}", g_localizeStrings.Get(20373), episodes[i].m_iSeason,
517 g_localizeStrings.Get(20359), episodes[i].m_iEpisode);
518 choices.Add(i, strButton);
521 int pressed = CGUIDialogContextMenu::ShowAndGetChoice(choices);
522 if (pressed >= 0)
524 AddBookmark(&episodes[pressed]);
525 return true;
528 return false;
533 bool CGUIDialogVideoBookmarks::OnAddBookmark()
535 if (!g_application.CurrentFileItem().IsVideo())
536 return false;
538 if (CGUIDialogVideoBookmarks::AddBookmark())
540 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_REFRESH_LIST, 0, WINDOW_DIALOG_VIDEO_BOOKMARKS);
541 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info,
542 g_localizeStrings.Get(298), // "Bookmarks"
543 g_localizeStrings.Get(21362));// "Bookmark created"
544 return true;
546 return false;
549 bool CGUIDialogVideoBookmarks::OnAddEpisodeBookmark()
551 bool bReturn = false;
552 if (g_application.CurrentFileItem().HasVideoInfoTag() && g_application.CurrentFileItem().GetVideoInfoTag()->m_iEpisode > -1)
554 CVideoDatabase videoDatabase;
555 videoDatabase.Open();
556 std::vector<CVideoInfoTag> episodes;
557 videoDatabase.GetEpisodesByFile(g_application.CurrentFile(),episodes);
558 if (episodes.size() > 1)
560 bReturn = CGUIDialogVideoBookmarks::AddEpisodeBookmark();
561 if(bReturn)
563 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_REFRESH_LIST, 0, WINDOW_DIALOG_VIDEO_BOOKMARKS);
564 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info,
565 g_localizeStrings.Get(298), // "Bookmarks"
566 g_localizeStrings.Get(21363));// "Episode Bookmark created"
570 videoDatabase.Close();
572 return bReturn;
575 void CGUIDialogVideoBookmarks::OnJobComplete(unsigned int jobID,
576 bool success, CJob* job)
578 if (success && IsActive())
580 MAPJOBSCHAPS::iterator iter = m_mapJobsChapter.find(job);
581 if (iter != m_mapJobsChapter.end())
583 unsigned int chapterIdx = (*iter).second;
584 CGUIMessage m(GUI_MSG_REFRESH_LIST, GetID(), 0, 1, chapterIdx);
585 CServiceBroker::GetAppMessenger()->SendGUIMessage(m);
586 m_mapJobsChapter.erase(iter);
589 CJobQueue::OnJobComplete(jobID, success, job);