[videodb] remove unused seasons table from episode_view
[xbmc.git] / xbmc / favourites / FavouritesService.cpp
bloba5a78d1a02589e3d5fee0d9f58d8d2d9e45549f7
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 "FavouritesService.h"
11 #include "FileItem.h"
12 #include "GUIPassword.h"
13 #include "ServiceBroker.h"
14 #include "Util.h"
15 #include "favourites/FavouritesURL.h"
16 #include "input/WindowTranslator.h"
17 #include "music/MusicFileItemClassify.h"
18 #include "profiles/ProfileManager.h"
19 #include "settings/SettingsComponent.h"
20 #include "utils/ContentUtils.h"
21 #include "utils/FileUtils.h"
22 #include "utils/URIUtils.h"
23 #include "utils/XBMCTinyXML2.h"
24 #include "utils/log.h"
25 #include "video/VideoFileItemClassify.h"
27 #include <mutex>
29 using namespace KODI;
31 namespace
33 bool IsMediasourceOfFavItemUnlocked(const std::shared_ptr<CFileItem>& item)
35 if (!item)
37 CLog::Log(LOGERROR, "{}: No item passed (nullptr).", __func__);
38 return true;
41 if (!item->IsFavourite())
43 CLog::Log(LOGERROR, "{}: Wrong item passed (not a favourite).", __func__);
44 return true;
47 const auto settingsComponent = CServiceBroker::GetSettingsComponent();
48 if (!settingsComponent)
50 CLog::Log(LOGERROR, "{}: returned nullptr.", __func__);
51 return true;
54 const auto profileManager = settingsComponent->GetProfileManager();
55 if (!profileManager)
57 CLog::Log(LOGERROR, "{}: returned nullptr.", __func__);
58 return true;
61 const CFavouritesURL url(item->GetPath());
62 if (!url.IsValid())
64 CLog::Log(LOGERROR, "{}: Invalid exec string (syntax error).", __func__);
65 return true;
68 const CFavouritesURL::Action action = url.GetAction();
70 if (action != CFavouritesURL::Action::PLAY_MEDIA &&
71 action != CFavouritesURL::Action::SHOW_PICTURE)
72 return true;
74 const CFileItem itemToCheck(url.GetTarget(), url.IsDir());
76 if (action == CFavouritesURL::Action::PLAY_MEDIA)
78 if (VIDEO::IsVideo(itemToCheck))
80 if (!profileManager->GetCurrentProfile().videoLocked())
81 return g_passwordManager.IsMediaFileUnlocked("video", itemToCheck.GetPath());
83 return false;
85 else if (MUSIC::IsAudio(itemToCheck))
87 if (!profileManager->GetCurrentProfile().musicLocked())
88 return g_passwordManager.IsMediaFileUnlocked("music", itemToCheck.GetPath());
90 return false;
93 else if (action == CFavouritesURL::Action::SHOW_PICTURE && itemToCheck.IsPicture())
95 if (!profileManager->GetCurrentProfile().picturesLocked())
96 return g_passwordManager.IsMediaFileUnlocked("pictures", itemToCheck.GetPath());
98 return false;
101 return true;
104 bool LoadFromFile(const std::string& strPath, CFileItemList& items)
106 CXBMCTinyXML2 doc;
107 if (!doc.LoadFile(strPath))
109 CLog::Log(LOGERROR, "Unable to load {} (line {})", strPath, doc.ErrorLineNum());
110 return false;
112 auto* root = doc.RootElement();
113 if (!root || strcmp(root->Value(), "favourites"))
115 CLog::Log(LOGERROR, "Favourites.xml doesn't contain the <favourites> root element");
116 return false;
119 auto* favourite = root->FirstChildElement("favourite");
120 while (favourite)
122 // format:
123 // <favourite name="Cool Video" thumb="foo.jpg">PlayMedia(c:\videos\cool_video.avi)</favourite>
124 // <favourite name="My Album" thumb="bar.tbn">ActivateWindow(MyMusic,c:\music\my album)</favourite>
125 // <favourite name="Apple Movie Trailers" thumb="path_to_thumb.png">RunScript(special://xbmc/scripts/apple movie trailers/default.py)</favourite>
126 const char *name = favourite->Attribute("name");
127 const char *thumb = favourite->Attribute("thumb");
128 if (name && favourite->FirstChild())
130 const std::string favURL(
131 CFavouritesURL(CExecString(favourite->FirstChild()->Value())).GetURL());
132 if (!items.Contains(favURL))
134 const CFileItemPtr item(std::make_shared<CFileItem>(name));
135 item->SetPath(favURL);
136 if (thumb)
137 item->SetArt("thumb", thumb);
138 items.Add(item);
141 favourite = favourite->NextSiblingElement("favourite");
143 return true;
145 } // unnamed namespace
147 CFavouritesService::CFavouritesService(std::string userDataFolder) : m_favourites("favourites://")
149 ReInit(std::move(userDataFolder));
152 void CFavouritesService::ReInit(std::string userDataFolder)
154 std::unique_lock<CCriticalSection> lock(m_criticalSection);
156 m_userDataFolder = std::move(userDataFolder);
157 m_favourites.Clear();
158 m_targets.clear();
159 m_favourites.SetContent("favourites");
161 std::string favourites = "special://xbmc/system/favourites.xml";
162 if (CFileUtils::Exists(favourites))
163 LoadFromFile(favourites, m_favourites);
164 else
165 CLog::Log(LOGDEBUG, "CFavourites::Load - no system favourites found, skipping");
167 favourites = URIUtils::AddFileToFolder(m_userDataFolder, "favourites.xml");
168 if (CFileUtils::Exists(favourites))
169 LoadFromFile(favourites, m_favourites);
170 else
171 CLog::Log(LOGDEBUG, "CFavourites::Load - no userdata favourites found, skipping");
174 bool CFavouritesService::Persist()
176 CXBMCTinyXML2 doc;
177 auto* element = doc.NewElement("favourites");
178 auto* rootNode = doc.InsertEndChild(element);
179 if (!rootNode)
180 return false;
182 for (const auto& item : m_favourites)
184 auto* favNode = doc.NewElement("favourite");
185 favNode->SetAttribute("name", item->GetLabel().c_str());
186 if (item->HasArt("thumb"))
187 favNode->SetAttribute("thumb", item->GetArt("thumb").c_str());
189 auto* execute = doc.NewText(CFavouritesURL(item->GetPath()).GetExecString().c_str());
190 favNode->InsertEndChild(execute);
191 rootNode->InsertEndChild(favNode);
194 auto path = URIUtils::AddFileToFolder(m_userDataFolder, "favourites.xml");
195 return doc.SaveFile(path);
198 bool CFavouritesService::Save(const CFileItemList& items)
201 std::unique_lock<CCriticalSection> lock(m_criticalSection);
202 m_favourites.Clear();
203 m_targets.clear();
204 m_favourites.Copy(items);
205 Persist();
207 OnUpdated();
208 return true;
211 void CFavouritesService::OnUpdated()
213 m_events.Publish(FavouritesUpdated{});
216 bool CFavouritesService::AddOrRemove(const CFileItem& item, int contextWindow)
219 std::unique_lock<CCriticalSection> lock(m_criticalSection);
221 const std::shared_ptr<CFileItem> match{GetFavourite(item, contextWindow)};
222 if (match)
224 // remove the item
225 const auto it = m_targets.find(match->GetPath());
226 if (it != m_targets.end())
227 m_targets.erase(it);
229 m_favourites.Remove(match.get());
231 else
233 // create our new favourite item
234 const auto favourite{std::make_shared<CFileItem>(item.GetLabel())};
235 if (item.GetLabel().empty())
236 favourite->SetLabel(CUtil::GetTitleFromPath(item.GetPath(), item.m_bIsFolder));
237 favourite->SetArt("thumb", ContentUtils::GetPreferredArtImage(item));
238 const std::string favUrl{CFavouritesURL(item, contextWindow).GetURL()};
239 favourite->SetPath(favUrl);
240 m_favourites.Add(favourite);
242 Persist();
244 OnUpdated();
245 return true;
248 std::shared_ptr<CFileItem> CFavouritesService::GetFavourite(const CFileItem& item,
249 int contextWindow) const
251 std::unique_lock<CCriticalSection> lock(m_criticalSection);
253 const CFavouritesURL favURL{item, contextWindow};
254 const bool isVideoDb{URIUtils::IsVideoDb(favURL.GetTarget())};
255 const bool isMusicDb{URIUtils::IsMusicDb(favURL.GetTarget())};
257 for (const auto& favItem : m_favourites)
259 const CFavouritesURL favItemURL{*favItem, contextWindow};
261 // Compare the whole target URLs
262 if (favItemURL.GetTarget() == item.GetPath())
263 return favItem;
265 // Compare the target URLs ignoring optional parameters
266 if (favItemURL.GetAction() == favURL.GetAction() &&
267 (favItemURL.GetAction() != CFavouritesURL::Action::ACTIVATE_WINDOW ||
268 favItemURL.GetWindowID() == favURL.GetWindowID()))
270 if (favItemURL.GetTarget() == favURL.GetTarget())
271 return favItem;
273 // Check videodb and musicdb paths. Might be different strings pointing to same resource!
274 // Example: "musicdb://recentlyaddedalbums/4711/" and "musicdb://recentlyplayedalbums/4711/",
275 // both pointing to same album with db id 4711.
276 if ((isVideoDb && URIUtils::IsVideoDb(favItemURL.GetTarget())) ||
277 (isMusicDb && URIUtils::IsMusicDb(favItemURL.GetTarget())))
279 const std::shared_ptr<CFileItem> targetItem{ResolveFavourite(*favItem)};
280 if (targetItem && targetItem->IsSamePath(&item))
281 return favItem;
285 return {};
288 bool CFavouritesService::IsFavourited(const CFileItem& item, int contextWindow) const
290 return (GetFavourite(item, contextWindow) != nullptr);
293 std::shared_ptr<CFileItem> CFavouritesService::ResolveFavourite(const CFileItem& item) const
295 if (item.IsFavourite())
297 std::unique_lock<CCriticalSection> lock(m_criticalSection);
299 const auto it = m_targets.find(item.GetPath());
300 if (it != m_targets.end())
301 return (*it).second;
303 const CFavouritesURL favURL{item.GetPath()};
304 if (favURL.IsValid())
306 auto targetItem{std::make_shared<CFileItem>(favURL.GetTarget(), favURL.IsDir())};
307 targetItem->LoadDetails();
308 if (favURL.GetWindowID() != -1)
310 const std::string window{CWindowTranslator::TranslateWindow(favURL.GetWindowID())};
311 targetItem->SetProperty("targetwindow", CVariant{window});
313 m_targets.insert({item.GetPath(), targetItem});
314 return targetItem;
317 return {};
320 int CFavouritesService::Size() const
322 std::unique_lock<CCriticalSection> lock(m_criticalSection);
323 return m_favourites.Size();
326 void CFavouritesService::GetAll(CFileItemList& items) const
328 std::unique_lock<CCriticalSection> lock(m_criticalSection);
329 items.Clear();
330 if (g_passwordManager.IsMasterLockUnlocked(false)) // don't prompt
332 items.Copy(m_favourites, true); // copy items
334 else
336 for (const auto& fav : m_favourites)
338 if (IsMediasourceOfFavItemUnlocked(fav))
339 items.Add(fav);
343 int index = 0;
344 for (const auto& item : items)
346 const CFavouritesURL favURL(item->GetPath());
347 item->SetProperty("favourite.action", favURL.GetActionLabel());
348 item->SetProperty("favourite.provider", favURL.GetProviderLabel());
349 item->SetProperty("favourite.index", index++);
353 void CFavouritesService::RefreshFavourites()
355 m_events.Publish(FavouritesUpdated{});