summaryrefslogtreecommitdiffstats
path: root/xbmc/video/jobs
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/video/jobs')
-rw-r--r--xbmc/video/jobs/CMakeLists.txt17
-rw-r--r--xbmc/video/jobs/VideoLibraryCleaningJob.cpp45
-rw-r--r--xbmc/video/jobs/VideoLibraryCleaningJob.h51
-rw-r--r--xbmc/video/jobs/VideoLibraryJob.cpp24
-rw-r--r--xbmc/video/jobs/VideoLibraryJob.h51
-rw-r--r--xbmc/video/jobs/VideoLibraryMarkWatchedJob.cpp118
-rw-r--r--xbmc/video/jobs/VideoLibraryMarkWatchedJob.h41
-rw-r--r--xbmc/video/jobs/VideoLibraryProgressJob.cpp24
-rw-r--r--xbmc/video/jobs/VideoLibraryProgressJob.h29
-rw-r--r--xbmc/video/jobs/VideoLibraryRefreshingJob.cpp380
-rw-r--r--xbmc/video/jobs/VideoLibraryRefreshingJob.h55
-rw-r--r--xbmc/video/jobs/VideoLibraryResetResumePointJob.cpp87
-rw-r--r--xbmc/video/jobs/VideoLibraryResetResumePointJob.h38
-rw-r--r--xbmc/video/jobs/VideoLibraryScanningJob.cpp50
-rw-r--r--xbmc/video/jobs/VideoLibraryScanningJob.h52
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;
+};