aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaveTBlake <oak99sky@yahoo.co.uk>2019-03-30 19:01:03 +0000
committerDaveTBlake <oak99sky@yahoo.co.uk>2019-04-06 13:07:55 +0100
commitf7caf87377eead02e259e55ef78655b86b744817 (patch)
tree873f5ab118abd71a937039401ecdd737f7895506
parentd135aaad5ff22370e1dc5bfe4c403a330fb4d92f (diff)
Import music library data on a separate thread from GUI. This allows cancel button on progress dialog to be clicked.
Import song playback history data (play count, lastplayed and ratings) from xml.
-rw-r--r--addons/resource.language.en_gb/resources/strings.po39
-rw-r--r--xbmc/music/MusicDatabase.cpp348
-rw-r--r--xbmc/music/MusicDatabase.h3
-rw-r--r--xbmc/music/MusicLibraryQueue.cpp40
-rw-r--r--xbmc/music/MusicLibraryQueue.h7
-rw-r--r--xbmc/music/jobs/CMakeLists.txt2
-rw-r--r--xbmc/music/jobs/MusicLibraryImportJob.cpp41
-rw-r--r--xbmc/music/jobs/MusicLibraryImportJob.h42
-rw-r--r--xbmc/settings/MediaSettings.cpp7
9 files changed, 496 insertions, 33 deletions
diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po
index 713cf7bd51..4e239efb2e 100644
--- a/addons/resource.language.en_gb/resources/strings.po
+++ b/addons/resource.language.en_gb/resources/strings.po
@@ -2987,12 +2987,12 @@ msgctxt "#648"
msgid "Import video library"
msgstr ""
-#: xbmc/music/MusicDatabase.cpp
+#. unused?
msgctxt "#649"
msgid "Importing"
msgstr ""
-#: xbmc/music/MusicDatabase.cpp
+#. unused?
msgctxt "#650"
msgid "Exporting"
msgstr ""
@@ -12394,6 +12394,7 @@ msgctxt "#20196"
msgid "Export music library"
msgstr ""
+#: xbmc/music/MusicDatabase.cpp
#: system/settings/settings.xml
msgctxt "#20197"
msgid "Import music library"
@@ -21393,7 +21394,39 @@ msgctxt "#38339"
msgid "How to apply information provider settings"
msgstr ""
-#empty strings from id 38340 to 38999
+#empty strings from id 38340 to 38349
+
+#. Progress statement when importing music library is processing song playback history data
+#: xbmc/music/MusicDatabase.cpp
+msgctxt "#38350"
+msgid "Importing song playback history"
+msgstr ""
+
+#. Progress statement when importing music library - data matching phase
+#: xbmc/music/MusicDatabase.cpp
+msgctxt "#38351"
+msgid "Matching data"
+msgstr ""
+
+#. Progress statement when importing music library - song history being updated
+#: xbmc/music/MusicDatabase.cpp
+msgctxt "#38352"
+msgid "Updating songs"
+msgstr ""
+
+#. Notification of import success "Importing song history - <number of songs matched> updated out of <total songs in xml> imported songs"
+#: xbmc/music/MusicDatabase.cpp
+msgctxt "#38353"
+msgid "Importing song history - {0:d} updated out of {0:d} imported songs"
+msgstr ""
+
+#. Message when reading the user specified XML for import to library fails
+#: xbmc/music/MusicDatabase.cpp
+msgctxt "#38354"
+msgid "Unable to read xml file"
+msgstr ""
+
+#empty strings from id 38355 to 38999
#: system/settings/settings.xml
msgctxt "#39000"
diff --git a/xbmc/music/MusicDatabase.cpp b/xbmc/music/MusicDatabase.cpp
index 2316551599..1d72b8dd2e 100644
--- a/xbmc/music/MusicDatabase.cpp
+++ b/xbmc/music/MusicDatabase.cpp
@@ -20,6 +20,8 @@
#include "dialogs/GUIDialogKaiToast.h"
#include "dialogs/GUIDialogProgress.h"
#include "dialogs/GUIDialogSelect.h"
+#include "events/EventLog.h"
+#include "events/NotificationEvent.h"
#include "FileItem.h"
#include "filesystem/Directory.h"
#include "filesystem/DirectoryCache.h"
@@ -52,6 +54,7 @@
#include "utils/FileUtils.h"
#include "utils/LegacyPathTranslation.h"
#include "utils/log.h"
+#include "utils/MathUtils.h"
#include "utils/Random.h"
#include "utils/StringUtils.h"
#include "utils/URIUtils.h"
@@ -9906,41 +9909,36 @@ bool CMusicDatabase::ExportSongHistory(TiXmlNode* pNode, CGUIDialogProgress* pro
return false;
}
-void CMusicDatabase::ImportFromXML(const std::string &xmlFile)
-{
- CGUIDialogProgress *progress = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS);
+void CMusicDatabase::ImportFromXML(const std::string& xmlFile, CGUIDialogProgress* progressDialog)
+{
try
{
if (NULL == m_pDB.get()) return;
if (NULL == m_pDS.get()) return;
CXBMCTinyXML xmlDoc;
- if (!xmlDoc.LoadFile(xmlFile))
+ if (!xmlDoc.LoadFile(xmlFile) && progressDialog)
+ {
+ HELPERS::ShowOKDialogLines(CVariant{ 20197 }, CVariant{ 38354 }); //"Unable to read xml file"
return;
+ }
TiXmlElement *root = xmlDoc.RootElement();
if (!root) return;
- if (progress)
- {
- progress->SetHeading(CVariant{20197});
- progress->SetLine(0, CVariant{649});
- progress->SetLine(1, CVariant{330});
- progress->SetLine(2, CVariant{""});
- progress->SetPercentage(0);
- progress->Open();
- progress->ShowProgressBar(true);
- }
-
TiXmlElement *entry = root->FirstChildElement();
int current = 0;
int total = 0;
- // first count the number of items...
+ int songtotal = 0;
+ // Count the number of artists, albums and songs
while (entry)
{
if (strnicmp(entry->Value(), "artist", 6)==0 ||
strnicmp(entry->Value(), "album", 5)==0)
total++;
+ else if (strnicmp(entry->Value(), "song", 4) == 0)
+ songtotal++;
+
entry = entry->NextSiblingElement();
}
@@ -9988,14 +9986,13 @@ void CMusicDatabase::ImportFromXML(const std::string &xmlFile)
current++;
}
entry = entry ->NextSiblingElement();
- if (progress && total)
+ if (progressDialog && total)
{
- progress->SetPercentage(current * 100 / total);
- progress->SetLine(2, CVariant{std::move(strTitle)});
- progress->Progress();
- if (progress->IsCanceled())
+ progressDialog->SetPercentage(current * 100 / total);
+ progressDialog->SetLine(2, CVariant{std::move(strTitle)});
+ progressDialog->Progress();
+ if (progressDialog->IsCanceled())
{
- progress->Close();
RollbackTransaction();
return;
}
@@ -10003,6 +10000,11 @@ void CMusicDatabase::ImportFromXML(const std::string &xmlFile)
}
CommitTransaction();
+ // Import song playback history <song> entries found
+ if (songtotal > 0)
+ if (!ImportSongHistory(xmlFile, songtotal, progressDialog))
+ return;
+
CGUIComponent* gui = CServiceBroker::GetGUI();
if (gui)
gui->GetInfoManager().GetInfoProviders().GetLibraryInfoProvider().ResetLibraryBools();
@@ -10012,8 +10014,306 @@ void CMusicDatabase::ImportFromXML(const std::string &xmlFile)
CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
RollbackTransaction();
}
- if (progress)
- progress->Close();
+ if (progressDialog)
+ progressDialog->Close();
+}
+
+bool CMusicDatabase::ImportSongHistory(const std::string& xmlFile, const int total, CGUIDialogProgress* progressDialog)
+{
+ bool bHistSongExists = false;
+ try
+ {
+ CXBMCTinyXML xmlDoc;
+ if (!xmlDoc.LoadFile(xmlFile))
+ return false;
+
+ TiXmlElement* root = xmlDoc.RootElement();
+ if (!root)
+ return false;
+
+ TiXmlElement* entry = root->FirstChildElement();
+ int current = 0;
+
+ if (progressDialog)
+ {
+ progressDialog->SetLine(1, CVariant{38350}); //"Importing song playback history"
+ progressDialog->SetLine(2, CVariant{ "" });
+ }
+
+ // As can be many songs do in db, not song at a time which would be slow
+ // Convert xml entries into a SQL bulk insert statement
+ std::string strSQL;
+ entry = root->FirstChildElement();
+ while (entry)
+ {
+ std::string strArtistDisp;
+ std::string strTitle;
+ int iTrack;
+ std::string strFilename;
+ std::string strMusicBrainzTrackID;
+ std::string strAlbum;
+ std::string strMusicBrainzAlbumID;
+ std::string strAlbumArtistDisp;
+ int iTimesplayed;
+ std::string lastplayed;
+ int iUserrating = 0;
+ float fRating = 0.0;
+ int iVotes;
+ std::string strSQLSong;
+ if (strnicmp(entry->Value(), "song", 4) == 0)
+ {
+ XMLUtils::GetString(entry, "artistdesc", strArtistDisp);
+ XMLUtils::GetString(entry, "title", strTitle);
+ XMLUtils::GetInt(entry, "track", iTrack);
+ XMLUtils::GetString(entry, "filename", strFilename);
+ XMLUtils::GetString(entry, "musicbrainztrackid", strMusicBrainzTrackID);
+ XMLUtils::GetString(entry, "albumtitle", strAlbum);
+ XMLUtils::GetString(entry, "musicbrainzalbumid", strMusicBrainzAlbumID);
+ XMLUtils::GetString(entry, "albumartistdesc", strAlbumArtistDisp);
+ XMLUtils::GetInt(entry, "timesplayed", iTimesplayed);
+ XMLUtils::GetString(entry, "lastplayed", lastplayed);
+ const TiXmlElement* rElement = entry->FirstChildElement("rating");
+ if (rElement)
+ {
+ float rating = 0;
+ float max_rating = 10;
+ XMLUtils::GetFloat(entry, "rating", rating);
+ if (rElement->QueryFloatAttribute("max", &max_rating) == TIXML_SUCCESS && max_rating >= 1)
+ rating *= (10.f / max_rating); // Normalise the value to between 0 and 10
+ if (rating > 10.f)
+ rating = 10.f;
+ fRating = rating;
+ }
+ XMLUtils::GetInt(entry, "votes", iVotes);
+ const TiXmlElement* userrating = entry->FirstChildElement("userrating");
+ if (userrating)
+ {
+ float rating = 0;
+ float max_rating = 10;
+ XMLUtils::GetFloat(entry, "userrating", rating);
+ if (userrating->QueryFloatAttribute("max", &max_rating) == TIXML_SUCCESS && max_rating >= 1)
+ rating *= (10.f / max_rating); // Normalise the value to between 0 and 10
+ if (rating > 10.f)
+ rating = 10.f;
+ iUserrating = MathUtils::round_int(rating);
+ }
+
+ strSQLSong = PrepareSQL("(%d, %d, ", current + 1, iTrack);
+ strSQLSong += PrepareSQL("'%s', '%s', '%s', ", strArtistDisp.c_str(), strTitle.c_str(), strFilename.c_str());
+ if (strMusicBrainzTrackID.empty())
+ strSQLSong += PrepareSQL("NULL, ");
+ else
+ strSQLSong += PrepareSQL("'%s', ", strMusicBrainzTrackID.c_str());
+ strSQLSong += PrepareSQL("'%s', '%s', ", strAlbum.c_str(), strAlbumArtistDisp.c_str());
+ if (strMusicBrainzAlbumID.empty())
+ strSQLSong += PrepareSQL("NULL, ");
+ else
+ strSQLSong += PrepareSQL("'%s', ", strMusicBrainzAlbumID.c_str());
+ strSQLSong += PrepareSQL("%d, ", iTimesplayed);
+ if (lastplayed.empty())
+ strSQLSong += PrepareSQL("NULL, ");
+ else
+ strSQLSong += PrepareSQL("'%s', ", lastplayed.c_str());
+ strSQLSong += PrepareSQL("%.1f, %d, %d, -1, -1)", fRating, iVotes, iUserrating);
+
+ if (current > 0)
+ strSQLSong = ", " + strSQLSong;
+ strSQL += strSQLSong;
+ current++;
+ }
+
+ entry = entry->NextSiblingElement();
+
+ if ((current % 100) == 0 && progressDialog)
+ {
+ progressDialog->SetPercentage(current * 100 / total);
+ progressDialog->SetLine(3, CVariant{ std::move(strTitle) });
+ progressDialog->Progress();
+ if (progressDialog->IsCanceled())
+ return false;
+ }
+ }
+
+ CLog::Log(LOGINFO, "{0}: Create temporary HistSong table and insert {1} records", __FUNCTION__, total);
+ /* Can not use CREATE TEMPORARY TABLE as MySQL does not support updates of
+ song table using correlated subqueries to a temp table. An updatable join
+ to temp table would work in MySQL but SQLite not support updatable joins.
+ */
+ m_pDS->exec("CREATE TABLE HistSong ("
+ "idSongSrc INTEGER primary key, "
+ "strAlbum varchar(256), "
+ "strMusicBrainzAlbumID text, "
+ "strAlbumArtistDisp text, "
+ "strArtistDisp text, strTitle varchar(512), "
+ "iTrack INTEGER, strFileName text, strMusicBrainzTrackID text, "
+ "iTimesPlayed INTEGER, lastplayed varchar(20) default NULL, "
+ "rating FLOAT NOT NULL DEFAULT 0, votes INTEGER NOT NULL DEFAULT 0, "
+ "userrating INTEGER NOT NULL DEFAULT 0, "
+ "idAlbum INTEGER, idSong INTEGER)");
+ bHistSongExists = true;
+
+ strSQL = "INSERT INTO HistSong (idSongSrc, iTrack, strArtistDisp, strTitle, "
+ "strFileName, strMusicBrainzTrackID, "
+ "strAlbum, strAlbumArtistDisp, strMusicBrainzAlbumID, "
+ " iTimesPlayed, lastplayed, rating, votes, userrating, idAlbum, idSong) VALUES " + strSQL;
+ m_pDS->exec(strSQL);
+
+ if (progressDialog)
+ {
+ progressDialog->SetLine(2, CVariant{38351}); //"Matching data"
+ progressDialog->SetLine(3, CVariant{ "" });
+ progressDialog->Progress();
+ if (progressDialog->IsCanceled())
+ {
+ m_pDS->exec("DROP TABLE HistSong");
+ return false;
+ }
+ }
+
+ BeginTransaction();
+ // Match albums first on mbid then artist string and album title, setting idAlbum
+ strSQL = "UPDATE HistSong "
+ "SET idAlbum = (SELECT album.idAlbum FROM album "
+ "WHERE album.strMusicBrainzAlbumID = HistSong.strMusicBrainzAlbumID) "
+ "WHERE EXISTS(SELECT 1 FROM album "
+ "WHERE album.strMusicBrainzAlbumID = HistSong.strMusicBrainzAlbumID) AND idAlbum < 0";
+ m_pDS->exec(strSQL);
+
+ strSQL = "UPDATE HistSong "
+ "SET idAlbum = (SELECT album.idAlbum FROM album "
+ "WHERE HistSong.strAlbumArtistDisp = album.strArtistDisp AND HistSong.strAlbum = album.strAlbum) "
+ "WHERE EXISTS(SELECT 1 FROM album "
+ "WHERE HistSong.strAlbumArtistDisp = album.strArtistDisp AND HistSong.strAlbum = album.strAlbum)"
+ "AND idAlbum < 0";
+ m_pDS->exec(strSQL);
+ if (progressDialog)
+ {
+ progressDialog->Progress();
+ if (progressDialog->IsCanceled())
+ {
+ RollbackTransaction();
+ m_pDS->exec("DROP TABLE HistSong");
+ return false;
+ }
+ }
+
+ // Match songs on first on idAlbum, track and mbid, then idAlbum, track and title, setting idSong
+ strSQL = "UPDATE HistSong "
+ "SET idSong = (SELECT idsong FROM song "
+ "WHERE HistSong.idAlbum = song.idAlbum AND "
+ "HistSong.iTrack = song.iTrack AND "
+ "HistSong.strMusicBrainzTrackID = song.strMusicBrainzTrackID) "
+ "WHERE EXISTS(SELECT 1 FROM song "
+ "WHERE HistSong.idAlbum = song.idAlbum AND "
+ "HistSong.iTrack = song.iTrack AND "
+ "HistSong.strMusicBrainzTrackID = song.strMusicBrainzTrackID) AND idSong < 0";
+ m_pDS->exec(strSQL);
+
+ strSQL = "UPDATE HistSong "
+ "SET idSong = (SELECT idsong FROM song "
+ "WHERE HistSong.idAlbum = song.idAlbum AND "
+ "HistSong.iTrack = song.iTrack AND HistSong.strTitle = song.strTitle) "
+ "WHERE EXISTS(SELECT 1 FROM song "
+ "WHERE HistSong.idAlbum = song.idAlbum AND "
+ "HistSong.iTrack = song.iTrack AND HistSong.strTitle = song.strTitle) AND idSong < 0";
+ m_pDS->exec(strSQL);
+ CommitTransaction();
+ if (progressDialog)
+ {
+ progressDialog->Progress();
+ if (progressDialog->IsCanceled())
+ {
+ m_pDS->exec("DROP TABLE HistSong");
+ return false;
+ }
+ }
+
+ // Create an index to speed up the updates
+ m_pDS->exec("CREATE INDEX idxHistSong ON HistSong(idSong)");
+
+ // Log how many songs matched
+ int unmatched = static_cast<int>(strtol(GetSingleValue("SELECT COUNT(1) FROM HistSong WHERE idSong < 0", m_pDS).c_str(), nullptr, 10));
+ CLog::Log(LOGINFO, "{0}: Importing song history {1} of {2} songs matched", __FUNCTION__, total - unmatched, total);
+
+ if (progressDialog)
+ {
+ progressDialog->SetLine(2, CVariant{38352}); //"Updating song playback history"
+ progressDialog->Progress();
+ if (progressDialog->IsCanceled())
+ {
+ m_pDS->exec("DROP TABLE HistSong"); // Drops index too
+ return false;
+ }
+ }
+
+ /* Update song table using the song ids we have matched.
+ Use correlated subqueries as SQLite does not support updatable joins.
+ MySQL requires HistSong table not to be defined temporary for this.
+ */
+ BeginTransaction();
+ // Times played and last played date(when count is greater)
+ strSQL = "UPDATE song SET iTimesPlayed = "
+ "(SELECT iTimesPlayed FROM HistSong WHERE HistSong.idSong = song.idSong), "
+ "lastplayed = "
+ "(SELECT lastplayed FROM HistSong WHERE HistSong.idSong = song.idSong) "
+ "WHERE EXISTS(SELECT 1 FROM HistSong WHERE "
+ "HistSong.idSong = song.idSong AND HistSong.iTimesPlayed > song.iTimesPlayed)";
+ m_pDS->exec(strSQL);
+
+ // User rating
+ strSQL = "UPDATE song SET userrating = "
+ "(SELECT userrating FROM HistSong WHERE HistSong.idSong = song.idSong) "
+ "WHERE EXISTS(SELECT 1 FROM HistSong WHERE "
+ "HistSong.idSong = song.idSong AND HistSong.userrating > 0)";
+ m_pDS->exec(strSQL);
+
+ // Rating and votes
+ strSQL = "UPDATE song SET rating = "
+ "(SELECT rating FROM HistSong WHERE HistSong.idSong = song.idSong), "
+ "votes = "
+ "(SELECT votes FROM HistSong WHERE HistSong.idSong = song.idSong) "
+ "WHERE EXISTS(SELECT 1 FROM HistSong WHERE "
+ "HistSong.idSong = song.idSong AND HistSong.rating > 0)";
+ m_pDS->exec(strSQL);
+
+ if (progressDialog)
+ {
+ progressDialog->Progress();
+ if (progressDialog->IsCanceled())
+ {
+ RollbackTransaction();
+ m_pDS->exec("DROP TABLE HistSong");
+ return false;
+ }
+ }
+ CommitTransaction();
+
+ // Tidy up temp table (index also removed)
+ m_pDS->exec("DROP TABLE HistSong");
+ // Compact db to recover space as had to add/drop actual table
+ if (progressDialog)
+ {
+ progressDialog->SetLine(2, CVariant{ 331 });
+ progressDialog->Progress();
+ }
+ Compress(false);
+
+ // Write event log entry
+ // "Importing song history {1} of {2} songs matched", total - unmatched, total)
+ std::string strLine = StringUtils::Format(g_localizeStrings.Get(38353).c_str(), total - unmatched, total);
+ CServiceBroker::GetEventLog().Add(
+ EventPtr(new CNotificationEvent(20197, strLine, EventLevel::Information)));
+
+ return true;
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
+ RollbackTransaction();
+ if (bHistSongExists)
+ m_pDS->exec("DROP TABLE HistSong");
+ }
+ return false;
}
void CMusicDatabase::SetPropertiesFromArtist(CFileItem& item, const CArtist& artist)
diff --git a/xbmc/music/MusicDatabase.h b/xbmc/music/MusicDatabase.h
index eae395735d..ed65526a94 100644
--- a/xbmc/music/MusicDatabase.h
+++ b/xbmc/music/MusicDatabase.h
@@ -499,7 +499,8 @@ public:
/////////////////////////////////////////////////
void ExportToXML(const CLibExportSettings& settings, CGUIDialogProgress* progressDialog = nullptr);
bool ExportSongHistory(TiXmlNode* pNode, CGUIDialogProgress* progressDialog = nullptr);
- void ImportFromXML(const std::string &xmlFile);
+ void ImportFromXML(const std::string& xmlFile, CGUIDialogProgress* progressDialog = nullptr);
+ bool ImportSongHistory(const std::string& xmlFile, const int total, CGUIDialogProgress* progressDialog = nullptr);
/////////////////////////////////////////////////
// Properties
diff --git a/xbmc/music/MusicLibraryQueue.cpp b/xbmc/music/MusicLibraryQueue.cpp
index 740fa3095b..acfaffb114 100644
--- a/xbmc/music/MusicLibraryQueue.cpp
+++ b/xbmc/music/MusicLibraryQueue.cpp
@@ -17,6 +17,7 @@
#include "GUIUserMessages.h"
#include "music/jobs/MusicLibraryCleaningJob.h"
#include "music/jobs/MusicLibraryExportJob.h"
+#include "music/jobs/MusicLibraryImportJob.h"
#include "music/jobs/MusicLibraryScanningJob.h"
#include "music/jobs/MusicLibraryJob.h"
#include "threads/SingleLock.h"
@@ -77,6 +78,45 @@ void CMusicLibraryQueue::ExportLibrary(const CLibExportSettings& settings, bool
}
}
+void CMusicLibraryQueue::ImportLibrary(const std::string& xmlFile, bool showDialog /* = false */)
+{
+ CGUIDialogProgress* progress = nullptr;
+ if (showDialog)
+ {
+ progress = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS);
+ if (progress)
+ {
+ progress->SetHeading(CVariant{ 20197 }); //"Import music library"
+ progress->SetText(CVariant{ 649 }); //"Importing"
+ progress->SetLine(1, CVariant{ 330 }); //"This could take some time"
+ progress->SetLine(2, CVariant{ "" });
+ progress->SetPercentage(0);
+ progress->Open();
+ progress->ShowProgressBar(true);
+ }
+ }
+
+ CMusicLibraryImportJob* importJob = new CMusicLibraryImportJob(xmlFile, progress);
+ if (showDialog)
+ {
+ AddJob(importJob);
+
+ // Wait for import to complete or be canceled, but render every 10ms so that the
+ // pointer movements work on dialog even when import is reporting progress infrequently
+ if (progress)
+ progress->Wait();
+ }
+ else
+ {
+ m_modal = true;
+ importJob->DoWork();
+
+ delete importJob;
+ m_modal = false;
+ Refresh();
+ }
+}
+
void CMusicLibraryQueue::ScanLibrary(const std::string& strDirectory, int flags /* = 0 */, bool showProgress /* = true */)
{
AddJob(new CMusicLibraryScanningJob(strDirectory, flags, showProgress));
diff --git a/xbmc/music/MusicLibraryQueue.h b/xbmc/music/MusicLibraryQueue.h
index 2ba993e13a..4ccf215524 100644
--- a/xbmc/music/MusicLibraryQueue.h
+++ b/xbmc/music/MusicLibraryQueue.h
@@ -44,6 +44,13 @@ public:
void ExportLibrary(const CLibExportSettings& settings, bool showDialog = false);
/*!
+ \brief Enqueue a music library import job.
+ \param[in] xmlFile xml file to import
+ \param[in] showDialog Show a progress dialog while (asynchronously) exporting, otherwise export in synchronous
+ */
+ void ImportLibrary(const std::string& xmlFile, bool showDialog = false);
+
+ /*!
\brief Enqueue a music library update job, scanning tags embedded in music files and optionally scraping additional data.
\param[in] strDirectory Directory to scan or "" (empty string) for a global scan.
\param[in] flags Flags for controlling the scanning process. See xbmc/music/infoscanner/MusicInfoScanner.h for possible values.
diff --git a/xbmc/music/jobs/CMakeLists.txt b/xbmc/music/jobs/CMakeLists.txt
index 0392781fee..8ee0f5dda6 100644
--- a/xbmc/music/jobs/CMakeLists.txt
+++ b/xbmc/music/jobs/CMakeLists.txt
@@ -2,12 +2,14 @@ set(SOURCES MusicLibraryJob.cpp
MusicLibraryProgressJob.cpp
MusicLibraryCleaningJob.cpp
MusicLibraryExportJob.cpp
+ MusicLibraryImportJob.cpp
MusicLibraryScanningJob.cpp)
set(HEADERS MusicLibraryJob.h
MusicLibraryProgressJob.h
MusicLibraryCleaningJob.h
MusicLibraryExportJob.h
+ MusicLibraryImportJob.h
MusicLibraryScanningJob.h)
core_add_library(music_jobs)
diff --git a/xbmc/music/jobs/MusicLibraryImportJob.cpp b/xbmc/music/jobs/MusicLibraryImportJob.cpp
new file mode 100644
index 0000000000..714e535b6e
--- /dev/null
+++ b/xbmc/music/jobs/MusicLibraryImportJob.cpp
@@ -0,0 +1,41 @@
+/*
+* 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 "MusicLibraryImportJob.h"
+#include "dialogs/GUIDialogProgress.h"
+#include "music/MusicDatabase.h"
+
+CMusicLibraryImportJob::CMusicLibraryImportJob(const std::string& xmlFile, CGUIDialogProgress* progressDialog)
+ : CMusicLibraryProgressJob(nullptr)
+ , m_xmlFile(xmlFile)
+{
+ if (progressDialog)
+ SetProgressIndicators(nullptr, progressDialog);
+ SetAutoClose(true);
+}
+
+CMusicLibraryImportJob::~CMusicLibraryImportJob() = default;
+
+bool CMusicLibraryImportJob::operator==(const CJob* job) const
+{
+ if (strcmp(job->GetType(), GetType()) != 0)
+ return false;
+
+ const CMusicLibraryImportJob* importJob = dynamic_cast<const CMusicLibraryImportJob*>(job);
+ if (importJob == nullptr)
+ return false;
+
+ return !(m_xmlFile != importJob->m_xmlFile);
+}
+
+bool CMusicLibraryImportJob::Work(CMusicDatabase &db)
+{
+ db.ImportFromXML(m_xmlFile, GetProgressDialog());
+
+ return true;
+}
diff --git a/xbmc/music/jobs/MusicLibraryImportJob.h b/xbmc/music/jobs/MusicLibraryImportJob.h
new file mode 100644
index 0000000000..bcca44f245
--- /dev/null
+++ b/xbmc/music/jobs/MusicLibraryImportJob.h
@@ -0,0 +1,42 @@
+/*
+* 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 "MusicLibraryProgressJob.h"
+
+class CGUIDialogProgress;
+
+/*!
+\brief Music library job implementation for importing data to the music library.
+*/
+class CMusicLibraryImportJob : public CMusicLibraryProgressJob
+{
+public:
+ /*!
+ \brief Creates a new music library import job for the given xml file.
+
+ \param[in] xmlFile xml file to import
+ \param[in] progressDialog Progress dialog to be used to display the import progress
+ */
+ CMusicLibraryImportJob(const std::string &xmlFile, CGUIDialogProgress* progressDialog);
+
+ ~CMusicLibraryImportJob() override;
+
+ // specialization of CJob
+ const char *GetType() const override { return "MusicLibraryImportJob"; }
+ bool operator==(const CJob* job) const override;
+
+protected:
+ // implementation of CMusicLibraryJob
+ bool Work(CMusicDatabase &db) override;
+
+private:
+ std::string m_xmlFile;
+};
+
diff --git a/xbmc/settings/MediaSettings.cpp b/xbmc/settings/MediaSettings.cpp
index 8cccd71c4d..d88829787c 100644
--- a/xbmc/settings/MediaSettings.cpp
+++ b/xbmc/settings/MediaSettings.cpp
@@ -19,7 +19,6 @@
#include "guilib/LocalizeStrings.h"
#include "interfaces/AnnouncementManager.h"
#include "interfaces/builtins/Builtins.h"
-#include "music/MusicDatabase.h"
#include "music/MusicLibraryQueue.h"
#include "messaging/helpers/DialogHelper.h"
#include "ServiceBroker.h"
@@ -315,10 +314,8 @@ void CMediaSettings::OnSettingAction(std::shared_ptr<const CSetting> setting)
if (CGUIDialogFileBrowser::ShowAndGetFile(shares, "musicdb.xml", g_localizeStrings.Get(651) , path))
{
- CMusicDatabase musicdatabase;
- musicdatabase.Open();
- musicdatabase.ImportFromXML(path);
- musicdatabase.Close();
+ // Import data to music library showing progress dialog
+ CMusicLibraryQueue::GetInstance().ImportLibrary(path, true);
}
}
else if (settingId == CSettings::SETTING_VIDEOLIBRARY_CLEANUP)