diff options
Diffstat (limited to 'xbmc/video/jobs')
-rw-r--r-- | xbmc/video/jobs/CMakeLists.txt | 17 | ||||
-rw-r--r-- | xbmc/video/jobs/VideoLibraryCleaningJob.cpp | 45 | ||||
-rw-r--r-- | xbmc/video/jobs/VideoLibraryCleaningJob.h | 51 | ||||
-rw-r--r-- | xbmc/video/jobs/VideoLibraryJob.cpp | 24 | ||||
-rw-r--r-- | xbmc/video/jobs/VideoLibraryJob.h | 51 | ||||
-rw-r--r-- | xbmc/video/jobs/VideoLibraryMarkWatchedJob.cpp | 118 | ||||
-rw-r--r-- | xbmc/video/jobs/VideoLibraryMarkWatchedJob.h | 41 | ||||
-rw-r--r-- | xbmc/video/jobs/VideoLibraryProgressJob.cpp | 24 | ||||
-rw-r--r-- | xbmc/video/jobs/VideoLibraryProgressJob.h | 29 | ||||
-rw-r--r-- | xbmc/video/jobs/VideoLibraryRefreshingJob.cpp | 380 | ||||
-rw-r--r-- | xbmc/video/jobs/VideoLibraryRefreshingJob.h | 55 | ||||
-rw-r--r-- | xbmc/video/jobs/VideoLibraryResetResumePointJob.cpp | 87 | ||||
-rw-r--r-- | xbmc/video/jobs/VideoLibraryResetResumePointJob.h | 38 | ||||
-rw-r--r-- | xbmc/video/jobs/VideoLibraryScanningJob.cpp | 50 | ||||
-rw-r--r-- | xbmc/video/jobs/VideoLibraryScanningJob.h | 52 |
15 files changed, 1062 insertions, 0 deletions
diff --git a/xbmc/video/jobs/CMakeLists.txt b/xbmc/video/jobs/CMakeLists.txt new file mode 100644 index 0000000..b63d8fe --- /dev/null +++ b/xbmc/video/jobs/CMakeLists.txt @@ -0,0 +1,17 @@ +set(SOURCES VideoLibraryCleaningJob.cpp + VideoLibraryJob.cpp + VideoLibraryMarkWatchedJob.cpp + VideoLibraryProgressJob.cpp + VideoLibraryRefreshingJob.cpp + VideoLibraryScanningJob.cpp + VideoLibraryResetResumePointJob.cpp) + +set(HEADERS VideoLibraryCleaningJob.h + VideoLibraryJob.h + VideoLibraryMarkWatchedJob.h + VideoLibraryProgressJob.h + VideoLibraryRefreshingJob.h + VideoLibraryScanningJob.h + VideoLibraryResetResumePointJob.h) + +core_add_library(video_jobs) diff --git a/xbmc/video/jobs/VideoLibraryCleaningJob.cpp b/xbmc/video/jobs/VideoLibraryCleaningJob.cpp new file mode 100644 index 0000000..7f4adfd --- /dev/null +++ b/xbmc/video/jobs/VideoLibraryCleaningJob.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "VideoLibraryCleaningJob.h" + +#include "dialogs/GUIDialogExtendedProgressBar.h" +#include "video/VideoDatabase.h" + +CVideoLibraryCleaningJob::CVideoLibraryCleaningJob(const std::set<int>& paths /* = std::set<int>() */, bool showDialog /* = false */) + : CVideoLibraryProgressJob(NULL), + m_paths(paths), + m_showDialog(showDialog) +{ } + +CVideoLibraryCleaningJob::CVideoLibraryCleaningJob(const std::set<int>& paths, CGUIDialogProgressBarHandle* progressBar) + : CVideoLibraryProgressJob(progressBar), + m_paths(paths), + m_showDialog(false) +{ } + +CVideoLibraryCleaningJob::~CVideoLibraryCleaningJob() = default; + +bool CVideoLibraryCleaningJob::operator==(const CJob* job) const +{ + if (strcmp(job->GetType(), GetType()) != 0) + return false; + + const CVideoLibraryCleaningJob* cleaningJob = dynamic_cast<const CVideoLibraryCleaningJob*>(job); + if (cleaningJob == NULL) + return false; + + return m_paths == cleaningJob->m_paths && + m_showDialog == cleaningJob->m_showDialog; +} + +bool CVideoLibraryCleaningJob::Work(CVideoDatabase &db) +{ + db.CleanDatabase(GetProgressBar(), m_paths, m_showDialog); + return true; +} diff --git a/xbmc/video/jobs/VideoLibraryCleaningJob.h b/xbmc/video/jobs/VideoLibraryCleaningJob.h new file mode 100644 index 0000000..f85682c --- /dev/null +++ b/xbmc/video/jobs/VideoLibraryCleaningJob.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "video/jobs/VideoLibraryProgressJob.h" + +#include <set> + +class CGUIDialogProgressBarHandle; + +/*! + \brief Video library job implementation for cleaning the video library. +*/ +class CVideoLibraryCleaningJob : public CVideoLibraryProgressJob +{ +public: + /*! + \brief Creates a new video library cleaning job for the given paths. + + \param[in] paths Set with database IDs of paths to be cleaned + \param[in] showDialog Whether to show a modal dialog or not + */ + CVideoLibraryCleaningJob(const std::set<int>& paths = std::set<int>(), bool showDialog = false); + + /*! + \brief Creates a new video library cleaning job for the given paths. + + \param[in] paths Set with database IDs of paths to be cleaned + \param[in] progressBar Progress bar to be used to display the cleaning progress + */ + CVideoLibraryCleaningJob(const std::set<int>& paths, CGUIDialogProgressBarHandle* progressBar); + ~CVideoLibraryCleaningJob() override; + + // specialization of CJob + const char *GetType() const override { return "VideoLibraryCleaningJob"; } + bool operator==(const CJob* job) const override; + +protected: + // implementation of CVideoLibraryJob + bool Work(CVideoDatabase &db) override; + +private: + std::set<int> m_paths; + bool m_showDialog; +}; diff --git a/xbmc/video/jobs/VideoLibraryJob.cpp b/xbmc/video/jobs/VideoLibraryJob.cpp new file mode 100644 index 0000000..efbbdb4 --- /dev/null +++ b/xbmc/video/jobs/VideoLibraryJob.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "VideoLibraryJob.h" + +#include "video/VideoDatabase.h" + +CVideoLibraryJob::CVideoLibraryJob() = default; + +CVideoLibraryJob::~CVideoLibraryJob() = default; + +bool CVideoLibraryJob::DoWork() +{ + CVideoDatabase db; + if (!db.Open()) + return false; + + return Work(db); +} diff --git a/xbmc/video/jobs/VideoLibraryJob.h b/xbmc/video/jobs/VideoLibraryJob.h new file mode 100644 index 0000000..a8f7851 --- /dev/null +++ b/xbmc/video/jobs/VideoLibraryJob.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "utils/Job.h" + +class CVideoDatabase; + +/*! + \brief Basic implementation/interface of a CJob which interacts with the + video database. + */ +class CVideoLibraryJob : public CJob +{ +public: + ~CVideoLibraryJob() override; + + /*! + \brief Whether the job can be cancelled or not. + */ + virtual bool CanBeCancelled() const { return false; } + + /*! + \brief Tries to cancel the running job. + + \return True if the job was cancelled, false otherwise + */ + virtual bool Cancel() { return false; } + + // implementation of CJob + bool DoWork() override; + const char *GetType() const override { return "VideoLibraryJob"; } + bool operator==(const CJob* job) const override { return false; } + +protected: + CVideoLibraryJob(); + + /*! + \brief Worker method to be implemented by an actual implementation. + + \param[in] db Already open video database to be used for interaction + \return True if the process succeeded, false otherwise + */ + virtual bool Work(CVideoDatabase &db) = 0; +}; diff --git a/xbmc/video/jobs/VideoLibraryMarkWatchedJob.cpp b/xbmc/video/jobs/VideoLibraryMarkWatchedJob.cpp new file mode 100644 index 0000000..25381bb --- /dev/null +++ b/xbmc/video/jobs/VideoLibraryMarkWatchedJob.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include <vector> + +#include "VideoLibraryMarkWatchedJob.h" +#include "FileItem.h" +#include "Util.h" +#include "filesystem/Directory.h" +#ifdef HAS_UPNP +#include "network/upnp/UPnP.h" +#endif +#include "pvr/PVRManager.h" +#include "pvr/recordings/PVRRecordings.h" +#include "profiles/ProfileManager.h" +#include "settings/SettingsComponent.h" +#include "ServiceBroker.h" +#include "utils/URIUtils.h" +#include "video/VideoDatabase.h" + +CVideoLibraryMarkWatchedJob::CVideoLibraryMarkWatchedJob(const std::shared_ptr<CFileItem>& item, + bool mark) + : m_item(item), m_mark(mark) +{ } + +CVideoLibraryMarkWatchedJob::~CVideoLibraryMarkWatchedJob() = default; + +bool CVideoLibraryMarkWatchedJob::operator==(const CJob* job) const +{ + if (strcmp(job->GetType(), GetType()) != 0) + return false; + + const CVideoLibraryMarkWatchedJob* markJob = dynamic_cast<const CVideoLibraryMarkWatchedJob*>(job); + if (markJob == NULL) + return false; + + return m_item->IsSamePath(markJob->m_item.get()) && markJob->m_mark == m_mark; +} + +bool CVideoLibraryMarkWatchedJob::Work(CVideoDatabase &db) +{ + const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager(); + + if (!profileManager->GetCurrentProfile().canWriteDatabases()) + return false; + + CFileItemList items; + items.Add(CFileItemPtr(new CFileItem(*m_item))); + + if (m_item->m_bIsFolder) + CUtil::GetRecursiveListing(m_item->GetPath(), items, "", XFILE::DIR_FLAG_NO_FILE_INFO); + + std::vector<CFileItemPtr> markItems; + for (int i = 0; i < items.Size(); i++) + { + CFileItemPtr item = items.Get(i); + if (item->HasVideoInfoTag() && m_mark == (item->GetVideoInfoTag()->GetPlayCount() > 0)) + continue; + +#ifdef HAS_UPNP + if (URIUtils::IsUPnP(item->GetPath()) && UPNP::CUPnP::MarkWatched(*item, m_mark)) + continue; +#endif + + if (item->HasPVRRecordingInfoTag() && + CServiceBroker::GetPVRManager().Recordings()->MarkWatched(item->GetPVRRecordingInfoTag(), m_mark)) + { + CDateTime newLastPlayed; + if (m_mark) + newLastPlayed = db.IncrementPlayCount(*item); + else + newLastPlayed = db.SetPlayCount(*item, 0); + + if (newLastPlayed.IsValid()) + item->GetVideoInfoTag()->m_lastPlayed = newLastPlayed; + + continue; + } + + markItems.push_back(item); + } + + if (markItems.empty()) + return true; + + db.BeginTransaction(); + + for (std::vector<CFileItemPtr>::const_iterator iter = markItems.begin(); iter != markItems.end(); ++iter) + { + const CFileItemPtr& item = *iter; + + std::string path(item->GetPath()); + if (item->HasVideoInfoTag() && !item->GetVideoInfoTag()->GetPath().empty()) + path = item->GetVideoInfoTag()->GetPath(); + + // With both mark as watched and unwatched we want the resume bookmarks to be reset + db.ClearBookMarksOfFile(path, CBookmark::RESUME); + + CDateTime newLastPlayed; + if (m_mark) + newLastPlayed = db.IncrementPlayCount(*item); + else + newLastPlayed = db.SetPlayCount(*item, 0); + + if (newLastPlayed.IsValid() && item->HasVideoInfoTag()) + item->GetVideoInfoTag()->m_lastPlayed = newLastPlayed; + } + + db.CommitTransaction(); + db.Close(); + + return true; +} diff --git a/xbmc/video/jobs/VideoLibraryMarkWatchedJob.h b/xbmc/video/jobs/VideoLibraryMarkWatchedJob.h new file mode 100644 index 0000000..e683b3f --- /dev/null +++ b/xbmc/video/jobs/VideoLibraryMarkWatchedJob.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "video/jobs/VideoLibraryJob.h" + +#include <memory> + +class CFileItem; + +/*! + \brief Video library job implementation for marking items as watched/unwatched. + */ +class CVideoLibraryMarkWatchedJob : public CVideoLibraryJob +{ +public: + /*! + \brief Creates a new video library scanning job. + + \param[in] item Item to be marked as watched/unwatched + \param[in] mark Whether to mark the item as watched or unwatched + */ + CVideoLibraryMarkWatchedJob(const std::shared_ptr<CFileItem>& item, bool mark); + ~CVideoLibraryMarkWatchedJob() override; + + const char *GetType() const override { return "CVideoLibraryMarkWatchedJob"; } + bool operator==(const CJob* job) const override; + +protected: + bool Work(CVideoDatabase &db) override; + +private: + std::shared_ptr<CFileItem> m_item; + bool m_mark; +}; diff --git a/xbmc/video/jobs/VideoLibraryProgressJob.cpp b/xbmc/video/jobs/VideoLibraryProgressJob.cpp new file mode 100644 index 0000000..0aab524 --- /dev/null +++ b/xbmc/video/jobs/VideoLibraryProgressJob.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "VideoLibraryProgressJob.h" + +CVideoLibraryProgressJob::CVideoLibraryProgressJob(CGUIDialogProgressBarHandle* progressBar) + : CProgressJob(progressBar) +{ } + +CVideoLibraryProgressJob::~CVideoLibraryProgressJob() = default; + +bool CVideoLibraryProgressJob::DoWork() +{ + bool result = CVideoLibraryJob::DoWork(); + + MarkFinished(); + + return result; +} diff --git a/xbmc/video/jobs/VideoLibraryProgressJob.h b/xbmc/video/jobs/VideoLibraryProgressJob.h new file mode 100644 index 0000000..278072b --- /dev/null +++ b/xbmc/video/jobs/VideoLibraryProgressJob.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "utils/ProgressJob.h" +#include "video/jobs/VideoLibraryJob.h" + +/*! + \brief Combined base implementation of a video library job with a progress bar. + */ +class CVideoLibraryProgressJob : public CProgressJob, public CVideoLibraryJob +{ +public: + ~CVideoLibraryProgressJob() override; + + // implementation of CJob + bool DoWork() override; + const char *GetType() const override { return "CVideoLibraryProgressJob"; } + bool operator==(const CJob* job) const override { return false; } + +protected: + explicit CVideoLibraryProgressJob(CGUIDialogProgressBarHandle* progressBar); +}; diff --git a/xbmc/video/jobs/VideoLibraryRefreshingJob.cpp b/xbmc/video/jobs/VideoLibraryRefreshingJob.cpp new file mode 100644 index 0000000..dbac5ed --- /dev/null +++ b/xbmc/video/jobs/VideoLibraryRefreshingJob.cpp @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "VideoLibraryRefreshingJob.h" + +#include "FileItem.h" +#include "ServiceBroker.h" +#include "TextureDatabase.h" +#include "URL.h" +#include "addons/Scraper.h" +#include "dialogs/GUIDialogSelect.h" +#include "dialogs/GUIDialogYesNo.h" +#include "filesystem/PluginDirectory.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIKeyboardFactory.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "media/MediaType.h" +#include "messaging/helpers/DialogOKHelper.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "utils/log.h" +#include "video/VideoDatabase.h" +#include "video/VideoInfoDownloader.h" +#include "video/VideoInfoScanner.h" +#include "video/tags/IVideoInfoTagLoader.h" +#include "video/tags/VideoInfoTagLoaderFactory.h" +#include "video/tags/VideoTagLoaderPlugin.h" + +#include <utility> + +using namespace KODI::MESSAGING; +using namespace VIDEO; + +CVideoLibraryRefreshingJob::CVideoLibraryRefreshingJob(std::shared_ptr<CFileItem> item, + bool forceRefresh, + bool refreshAll, + bool ignoreNfo /* = false */, + const std::string& searchTitle /* = "" */) + : CVideoLibraryProgressJob(nullptr), + m_item(std::move(item)), + m_forceRefresh(forceRefresh), + m_refreshAll(refreshAll), + m_ignoreNfo(ignoreNfo), + m_searchTitle(searchTitle) +{ } + +CVideoLibraryRefreshingJob::~CVideoLibraryRefreshingJob() = default; + +bool CVideoLibraryRefreshingJob::operator==(const CJob* job) const +{ + if (strcmp(job->GetType(), GetType()) != 0) + return false; + + const CVideoLibraryRefreshingJob* refreshingJob = dynamic_cast<const CVideoLibraryRefreshingJob*>(job); + if (refreshingJob == nullptr) + return false; + + return m_item->GetPath() == refreshingJob->m_item->GetPath(); +} + +bool CVideoLibraryRefreshingJob::Work(CVideoDatabase &db) +{ + if (m_item == nullptr) + return false; + + // determine the scraper for the item's path + VIDEO::SScanSettings scanSettings; + ADDON::ScraperPtr scraper = db.GetScraperForPath(m_item->GetPath(), scanSettings); + if (scraper == nullptr) + return false; + + if (URIUtils::IsPlugin(m_item->GetPath()) && !XFILE::CPluginDirectory::IsMediaLibraryScanningAllowed(ADDON::TranslateContent(scraper->Content()), m_item->GetPath())) + { + CLog::Log(LOGINFO, + "CVideoLibraryRefreshingJob: Plugin '{}' does not support media library scanning and " + "refreshing", + CURL::GetRedacted(m_item->GetPath())); + return false; + } + + // copy the scraper in case we need it again + ADDON::ScraperPtr originalScraper(scraper); + + // get the item's correct title + std::string itemTitle = m_searchTitle; + if (itemTitle.empty()) + itemTitle = m_item->GetMovieName(scanSettings.parent_name); + + CScraperUrl scraperUrl; + bool needsRefresh = m_forceRefresh; + bool hasDetails = false; + bool ignoreNfo = m_ignoreNfo; + + // run this in a loop in case we need to refresh again + bool failure = false; + do + { + std::unique_ptr<CVideoInfoTag> pluginTag; + std::unique_ptr<CGUIListItem::ArtMap> pluginArt; + + if (!ignoreNfo) + { + std::unique_ptr<IVideoInfoTagLoader> loader; + loader.reset(CVideoInfoTagLoaderFactory::CreateLoader(*m_item, scraper, + scanSettings.parent_name_root, m_forceRefresh)); + // check if there's an NFO for the item + CInfoScanner::INFO_TYPE nfoResult = CInfoScanner::NO_NFO; + if (loader) + { + std::unique_ptr<CVideoInfoTag> tag(new CVideoInfoTag()); + nfoResult = loader->Load(*tag, false); + if (nfoResult == CInfoScanner::FULL_NFO && m_item->IsPlugin() && scraper->ID() == "metadata.local") + { + // get video info and art from plugin source with metadata.local scraper + if (scraper->Content() == CONTENT_TVSHOWS && !m_item->m_bIsFolder && tag->m_iIdShow < 0) + // preserve show_id for episode + tag->m_iIdShow = m_item->GetVideoInfoTag()->m_iIdShow; + pluginTag = std::move(tag); + CVideoTagLoaderPlugin* nfo = dynamic_cast<CVideoTagLoaderPlugin*>(loader.get()); + if (nfo && nfo->GetArt()) + pluginArt = std::move(nfo->GetArt()); + } + else if (nfoResult == CInfoScanner::URL_NFO) + scraperUrl = loader->ScraperUrl(); + } + + // if there's no NFO remember it in case we have to refresh again + if (nfoResult == CInfoScanner::ERROR_NFO) + ignoreNfo = true; + else if (nfoResult != CInfoScanner::NO_NFO) + hasDetails = true; + + // if we are performing a forced refresh ask the user to choose between using a valid NFO and a valid scraper + if (needsRefresh && IsModal() && !scraper->IsNoop() + && nfoResult != CInfoScanner::ERROR_NFO) + { + int heading = 20159; + if (scraper->Content() == CONTENT_MOVIES) + heading = 13346; + else if (scraper->Content() == CONTENT_TVSHOWS) + heading = m_item->m_bIsFolder ? 20351 : 20352; + else if (scraper->Content() == CONTENT_MUSICVIDEOS) + heading = 20393; + if (CGUIDialogYesNo::ShowAndGetInput(heading, 20446)) + { + hasDetails = false; + ignoreNfo = true; + scraperUrl.Clear(); + scraper = originalScraper; + } + } + } + + // no need to re-fetch the episode guide for episodes + if (scraper->Content() == CONTENT_TVSHOWS && !m_item->m_bIsFolder) + hasDetails = true; + + // if we don't have an url or need to refresh anyway do the web search + if (!hasDetails && (needsRefresh || !scraperUrl.HasUrls())) + { + SetTitle(StringUtils::Format(g_localizeStrings.Get(197), scraper->Name())); + SetText(itemTitle); + SetProgress(0); + + // clear any cached data from the scraper + scraper->ClearCache(); + + // create the info downloader for the scraper + CVideoInfoDownloader infoDownloader(scraper); + + // try to find a matching item + MOVIELIST itemResultList; + int result = infoDownloader.FindMovie(itemTitle, -1, itemResultList, GetProgressDialog()); + + // close the progress dialog + MarkFinished(); + + if (result > 0) + { + // there are multiple matches for the item + if (!itemResultList.empty()) + { + // choose the first match + if (!IsModal()) + scraperUrl = itemResultList.at(0); + else + { + // ask the user what to do + CGUIDialogSelect* selectDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT); + selectDialog->Reset(); + selectDialog->SetHeading(scraper->Content() == CONTENT_TVSHOWS ? 20356 : 196); + for (const auto& itemResult : itemResultList) + selectDialog->Add(itemResult.GetTitle()); + selectDialog->EnableButton(true, 413); // "Manual" + selectDialog->Open(); + + // check if the user has chosen one of the results + int selectedItem = selectDialog->GetSelectedItem(); + if (selectedItem >= 0) + scraperUrl = itemResultList.at(selectedItem); + // the user hasn't chosen one of the results and but has chosen to manually enter a title to use + else if (selectDialog->IsButtonPressed()) + { + // ask the user to input a title to use + if (!CGUIKeyboardFactory::ShowAndGetInput(itemTitle, g_localizeStrings.Get(scraper->Content() == CONTENT_TVSHOWS ? 20357 : 16009), false)) + return false; + + // go through the whole process again + needsRefresh = true; + continue; + } + // nothing else we can do + else + return false; + } + + CLog::Log(LOGDEBUG, "CVideoLibraryRefreshingJob: user selected item '{}' with URL '{}'", + scraperUrl.GetTitle(), scraperUrl.GetFirstThumbUrl()); + } + } + else if (result < 0 || !VIDEO::CVideoInfoScanner::DownloadFailed(GetProgressDialog())) + { + failure = true; + break; + } + } + + // if the URL is still empty, check whether or not we're allowed + // to prompt and ask the user to input a new search title + if (!hasDetails && !scraperUrl.HasUrls()) + { + if (IsModal()) + { + // ask the user to input a title to use + if (!CGUIKeyboardFactory::ShowAndGetInput(itemTitle, g_localizeStrings.Get(scraper->Content() == CONTENT_TVSHOWS ? 20357 : 16009), false)) + return false; + + // go through the whole process again + needsRefresh = true; + continue; + } + + // nothing else we can do + failure = true; + break; + } + + // before we start downloading all the necessary information cleanup any existing artwork and hashes + CTextureDatabase textureDb; + if (textureDb.Open()) + { + for (const auto& artwork : m_item->GetArt()) + textureDb.InvalidateCachedTexture(artwork.second); + + textureDb.Close(); + } + m_item->ClearArt(); + + // put together the list of items to refresh + std::string path = m_item->GetPath(); + CFileItemList items; + if (m_item->HasVideoInfoTag() && m_item->GetVideoInfoTag()->m_iDbId > 0) + { + // for a tvshow we need to handle all paths of it + std::vector<std::string> tvshowPaths; + if (CMediaTypes::IsMediaType(m_item->GetVideoInfoTag()->m_type, MediaTypeTvShow) && m_refreshAll && + db.GetPathsLinkedToTvShow(m_item->GetVideoInfoTag()->m_iDbId, tvshowPaths)) + { + for (const auto& tvshowPath : tvshowPaths) + { + CFileItemPtr tvshowItem(new CFileItem(*m_item->GetVideoInfoTag())); + tvshowItem->SetPath(tvshowPath); + items.Add(tvshowItem); + } + } + // otherwise just add a copy of the item + else + items.Add(CFileItemPtr(new CFileItem(*m_item->GetVideoInfoTag()))); + + // update the path to the real path (instead of a videodb:// one) + path = m_item->GetVideoInfoTag()->m_strPath; + } + else + items.Add(CFileItemPtr(new CFileItem(*m_item))); + + // set the proper path of the list of items to lookup + items.SetPath(m_item->m_bIsFolder ? URIUtils::GetParentPath(path) : URIUtils::GetDirectory(path)); + + int headingLabel = 198; + if (scraper->Content() == CONTENT_TVSHOWS) + { + if (m_item->m_bIsFolder) + headingLabel = 20353; + else + headingLabel = 20361; + } + else if (scraper->Content() == CONTENT_MUSICVIDEOS) + headingLabel = 20394; + + // prepare the progress dialog for downloading all the necessary information + SetTitle(g_localizeStrings.Get(headingLabel)); + SetText(scraperUrl.GetTitle()); + SetProgress(0); + + // remove any existing data for the item we're going to refresh + if (m_item->GetVideoInfoTag()->m_iDbId > 0) + { + int dbId = m_item->GetVideoInfoTag()->m_iDbId; + if (scraper->Content() == CONTENT_MOVIES) + db.DeleteMovie(dbId); + else if (scraper->Content() == CONTENT_MUSICVIDEOS) + db.DeleteMusicVideo(dbId); + else if (scraper->Content() == CONTENT_TVSHOWS) + { + if (!m_item->m_bIsFolder) + db.DeleteEpisode(dbId); + else if (m_refreshAll) + db.DeleteTvShow(dbId); + else + db.DeleteDetailsForTvShow(dbId); + } + } + + if (pluginTag || pluginArt) + // set video info and art from plugin source with metadata.local scraper to items + for (auto &i: items) + { + if (pluginTag) + *i->GetVideoInfoTag() = *pluginTag; + if (pluginArt) + i->SetArt(*pluginArt); + } + + // finally download the information for the item + CVideoInfoScanner scanner; + if (!scanner.RetrieveVideoInfo(items, scanSettings.parent_name, + scraper->Content(), !ignoreNfo, + scraperUrl.HasUrls() ? &scraperUrl : nullptr, + m_refreshAll, GetProgressDialog())) + { + // something went wrong + MarkFinished(); + + // check if the user cancelled + if (!IsCancelled() && IsModal()) + HELPERS::ShowOKDialogText(CVariant{195}, CVariant{itemTitle}); + + return false; + } + + // retrieve the updated information from the database + if (scraper->Content() == CONTENT_MOVIES) + db.GetMovieInfo(m_item->GetPath(), *m_item->GetVideoInfoTag()); + else if (scraper->Content() == CONTENT_MUSICVIDEOS) + db.GetMusicVideoInfo(m_item->GetPath(), *m_item->GetVideoInfoTag()); + else if (scraper->Content() == CONTENT_TVSHOWS) + { + // update tvshow info to get updated episode numbers + if (m_item->m_bIsFolder) + db.GetTvShowInfo(m_item->GetPath(), *m_item->GetVideoInfoTag()); + else + db.GetEpisodeInfo(m_item->GetPath(), *m_item->GetVideoInfoTag()); + } + + // we're finally done + MarkFinished(); + break; + } while (needsRefresh); + + if (failure && IsModal()) + HELPERS::ShowOKDialogText(CVariant{195}, CVariant{itemTitle}); + + return true; +} diff --git a/xbmc/video/jobs/VideoLibraryRefreshingJob.h b/xbmc/video/jobs/VideoLibraryRefreshingJob.h new file mode 100644 index 0000000..e98f4c0 --- /dev/null +++ b/xbmc/video/jobs/VideoLibraryRefreshingJob.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "video/jobs/VideoLibraryProgressJob.h" + +#include <memory> +#include <string> + +class CFileItem; + +/*! + \brief Video library job implementation for refreshing a single item. +*/ +class CVideoLibraryRefreshingJob : public CVideoLibraryProgressJob +{ +public: + /*! + \brief Creates a new video library cleaning job for the given paths. + + \param[inout] item Video item to be refreshed + \param[in] forceRefresh Whether to force a complete refresh (including NFO or internet lookup) + \param[in] refreshAll Whether to refresh all sub-items (in case of a tvshow) + \param[in] ignoreNfo Whether or not to ignore local NFO files + \param[in] searchTitle Title to use for the search (instead of determining it from the item's filename/path) + */ + CVideoLibraryRefreshingJob(std::shared_ptr<CFileItem> item, + bool forceRefresh, + bool refreshAll, + bool ignoreNfo = false, + const std::string& searchTitle = ""); + + ~CVideoLibraryRefreshingJob() override; + + // specialization of CJob + const char *GetType() const override { return "VideoLibraryRefreshingJob"; } + bool operator==(const CJob* job) const override; + +protected: + // implementation of CVideoLibraryJob + bool Work(CVideoDatabase &db) override; + +private: + std::shared_ptr<CFileItem> m_item; + bool m_forceRefresh; + bool m_refreshAll; + bool m_ignoreNfo; + std::string m_searchTitle; +}; diff --git a/xbmc/video/jobs/VideoLibraryResetResumePointJob.cpp b/xbmc/video/jobs/VideoLibraryResetResumePointJob.cpp new file mode 100644 index 0000000..15b71a2 --- /dev/null +++ b/xbmc/video/jobs/VideoLibraryResetResumePointJob.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2017-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "VideoLibraryResetResumePointJob.h" + +#include <vector> + +#include "FileItem.h" +#include "ServiceBroker.h" +#include "Util.h" +#include "filesystem/IDirectory.h" +#ifdef HAS_UPNP +#include "network/upnp/UPnP.h" +#endif +#include "profiles/ProfileManager.h" +#include "pvr/PVRManager.h" +#include "pvr/recordings/PVRRecordings.h" +#include "settings/SettingsComponent.h" +#include "utils/URIUtils.h" +#include "video/VideoDatabase.h" + +CVideoLibraryResetResumePointJob::CVideoLibraryResetResumePointJob( + const std::shared_ptr<CFileItem>& item) + : m_item(item) +{ +} + +bool CVideoLibraryResetResumePointJob::operator==(const CJob* job) const +{ + if (strcmp(job->GetType(), GetType()) != 0) + return false; + + const CVideoLibraryResetResumePointJob* resetJob = dynamic_cast<const CVideoLibraryResetResumePointJob*>(job); + if (!resetJob) + return false; + + return m_item->IsSamePath(resetJob->m_item.get()); +} + +bool CVideoLibraryResetResumePointJob::Work(CVideoDatabase &db) +{ + const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager(); + + if (!profileManager->GetCurrentProfile().canWriteDatabases()) + return false; + + CFileItemList items; + items.Add(std::make_shared<CFileItem>(*m_item)); + + if (m_item->m_bIsFolder) + CUtil::GetRecursiveListing(m_item->GetPath(), items, "", XFILE::DIR_FLAG_NO_FILE_INFO); + + std::vector<CFileItemPtr> resetItems; + for (const auto& item : items) + { +#ifdef HAS_UPNP + if (URIUtils::IsUPnP(item->GetPath()) && UPNP::CUPnP::SaveFileState(*item, CBookmark(), false /* updatePlayCount */)) + continue; +#endif + + if (item->HasPVRRecordingInfoTag() && + CServiceBroker::GetPVRManager().Recordings()->ResetResumePoint(item->GetPVRRecordingInfoTag())) + continue; + + resetItems.emplace_back(item); + } + + if (resetItems.empty()) + return true; + + db.BeginTransaction(); + + for (const auto& resetItem : resetItems) + { + db.DeleteResumeBookMark(*resetItem); + } + + db.CommitTransaction(); + db.Close(); + + return true; +} diff --git a/xbmc/video/jobs/VideoLibraryResetResumePointJob.h b/xbmc/video/jobs/VideoLibraryResetResumePointJob.h new file mode 100644 index 0000000..1e2d69a --- /dev/null +++ b/xbmc/video/jobs/VideoLibraryResetResumePointJob.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "FileItem.h" +#include "video/jobs/VideoLibraryJob.h" + +#include <memory> + +/*! + \brief Video library job implementation for resetting a resume point. + */ +class CVideoLibraryResetResumePointJob : public CVideoLibraryJob +{ +public: + /*! + \brief Creates a new job for resetting a given item's resume point. + + \param[in] item Item for that the resume point shall be reset. + */ + CVideoLibraryResetResumePointJob(const std::shared_ptr<CFileItem>& item); + ~CVideoLibraryResetResumePointJob() override = default; + + const char *GetType() const override { return "CVideoLibraryResetResumePointJob"; } + bool operator==(const CJob* job) const override; + +protected: + bool Work(CVideoDatabase &db) override; + +private: + std::shared_ptr<CFileItem> m_item; +}; diff --git a/xbmc/video/jobs/VideoLibraryScanningJob.cpp b/xbmc/video/jobs/VideoLibraryScanningJob.cpp new file mode 100644 index 0000000..f938420 --- /dev/null +++ b/xbmc/video/jobs/VideoLibraryScanningJob.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "VideoLibraryScanningJob.h" + +#include "video/VideoDatabase.h" + +CVideoLibraryScanningJob::CVideoLibraryScanningJob(const std::string& directory, bool scanAll /* = false */, bool showProgress /* = true */) + : m_scanner(), + m_directory(directory), + m_showProgress(showProgress), + m_scanAll(scanAll) +{ } + +CVideoLibraryScanningJob::~CVideoLibraryScanningJob() = default; + +bool CVideoLibraryScanningJob::Cancel() +{ + if (!m_scanner.IsScanning()) + return true; + + m_scanner.Stop(); + return true; +} + +bool CVideoLibraryScanningJob::operator==(const CJob* job) const +{ + if (strcmp(job->GetType(), GetType()) != 0) + return false; + + const CVideoLibraryScanningJob* scanningJob = dynamic_cast<const CVideoLibraryScanningJob*>(job); + if (scanningJob == NULL) + return false; + + return m_directory == scanningJob->m_directory && + m_scanAll == scanningJob->m_scanAll; +} + +bool CVideoLibraryScanningJob::Work(CVideoDatabase &db) +{ + m_scanner.ShowDialog(m_showProgress); + m_scanner.Start(m_directory, m_scanAll); + + return true; +} diff --git a/xbmc/video/jobs/VideoLibraryScanningJob.h b/xbmc/video/jobs/VideoLibraryScanningJob.h new file mode 100644 index 0000000..3df98a7 --- /dev/null +++ b/xbmc/video/jobs/VideoLibraryScanningJob.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "video/VideoInfoScanner.h" +#include "video/jobs/VideoLibraryJob.h" + +#include <string> + +/*! + \brief Video library job implementation for scanning items. + + Uses CVideoInfoScanner for the whole filesystem scanning and can be run with + or without a visible progress bar. + */ +class CVideoLibraryScanningJob : public CVideoLibraryJob +{ +public: + /*! + \brief Creates a new video library scanning job. + + \param[in] directory Directory to be scanned for new items + \param[in] scanAll Whether to scan all items or not + \param[in] showProgress Whether to show a progress bar or not + */ + CVideoLibraryScanningJob(const std::string& directory, bool scanAll = false, bool showProgress = true); + ~CVideoLibraryScanningJob() override; + + // specialization of CVideoLibraryJob + bool CanBeCancelled() const override { return true; } + bool Cancel() override; + + // specialization of CJob + const char *GetType() const override { return "VideoLibraryScanningJob"; } + bool operator==(const CJob* job) const override; + +protected: + // implementation of CVideoLibraryJob + bool Work(CVideoDatabase &db) override; + +private: + VIDEO::CVideoInfoScanner m_scanner; + std::string m_directory; + bool m_showProgress; + bool m_scanAll; +}; |