aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/resource.language.en_gb/resources/strings.po151
-rw-r--r--cmake/treedata/common/music.txt1
-rwxr-xr-xsystem/settings/settings.xml42
-rw-r--r--xbmc/Application.cpp13
-rw-r--r--xbmc/guilib/GUIWindowManager.cpp4
-rw-r--r--xbmc/guilib/WindowIDs.h1
-rw-r--r--xbmc/input/WindowTranslator.cpp1
-rw-r--r--xbmc/interfaces/builtins/LibraryBuiltins.cpp111
-rw-r--r--xbmc/interfaces/json-rpc/AudioLibrary.cpp14
-rw-r--r--xbmc/interfaces/json-rpc/VideoLibrary.cpp16
-rw-r--r--xbmc/music/Artist.cpp30
-rw-r--r--xbmc/music/Artist.h6
-rw-r--r--xbmc/music/CMakeLists.txt2
-rw-r--r--xbmc/music/MusicDatabase.cpp753
-rw-r--r--xbmc/music/MusicDatabase.h15
-rw-r--r--xbmc/music/MusicLibraryQueue.cpp180
-rw-r--r--xbmc/music/MusicLibraryQueue.h100
-rw-r--r--xbmc/music/dialogs/GUIDialogMusicInfo.cpp57
-rw-r--r--xbmc/music/infoscanner/MusicInfoScanner.cpp210
-rw-r--r--xbmc/music/infoscanner/MusicInfoScanner.h65
-rw-r--r--xbmc/music/jobs/CMakeLists.txt9
-rw-r--r--xbmc/music/jobs/MusicLibraryExportJob.cpp53
-rw-r--r--xbmc/music/jobs/MusicLibraryExportJob.h53
-rw-r--r--xbmc/music/jobs/MusicLibraryJob.cpp35
-rw-r--r--xbmc/music/jobs/MusicLibraryJob.h61
-rw-r--r--xbmc/music/jobs/MusicLibraryProgressJob.cpp36
-rw-r--r--xbmc/music/jobs/MusicLibraryProgressJob.h40
-rw-r--r--xbmc/music/windows/GUIWindowMusicBase.cpp78
-rw-r--r--xbmc/music/windows/GUIWindowMusicBase.h4
-rw-r--r--xbmc/settings/AdvancedSettings.cpp2
-rw-r--r--xbmc/settings/AdvancedSettings.h1
-rw-r--r--xbmc/settings/CMakeLists.txt2
-rw-r--r--xbmc/settings/LibExportSettings.cpp96
-rw-r--r--xbmc/settings/LibExportSettings.h73
-rw-r--r--xbmc/settings/MediaSettings.cpp11
-rw-r--r--xbmc/settings/MediaSettings.h2
-rw-r--r--xbmc/settings/Settings.cpp8
-rw-r--r--xbmc/settings/Settings.h8
-rw-r--r--xbmc/settings/dialogs/CMakeLists.txt2
-rw-r--r--xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp368
-rw-r--r--xbmc/settings/dialogs/GUIDialogLibExportSettings.h66
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoInfo.cpp31
42 files changed, 2482 insertions, 329 deletions
diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po
index d7d3ec71aa..28462c83b6 100644
--- a/addons/resource.language.en_gb/resources/strings.po
+++ b/addons/resource.language.en_gb/resources/strings.po
@@ -820,6 +820,7 @@ msgstr ""
#: xbmc/addons/GUIDialogAddonInfo.cpp
#: xbmc/dialogs/GUIDialogSelect.cpp
#: addons/skin.estuary/xml/DialogPVRGroupManager.xml
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
msgctxt "#186"
msgid "OK"
msgstr ""
@@ -1018,6 +1019,15 @@ msgctxt "#221"
msgid "Network is not connected"
msgstr ""
+#: xbmc/addons/settings/GUIDialogAddonSettings.cpp
+#: xbmc/network/GUIDialogNetworkSetup.cpp
+#: xbmc/peripherals/dialogs/GUIDialogPeripheralSettings.cpp
+#: xbmc/profiles/dialogs/GUIDialogLockSettings.cpp
+#: xbmc/profiles/dialogs/GUIDialogProfileSettings.cpp
+#: xbmc/pvr/dialogs/GUIDialogPVRRecordingSettings.cpp
+#: xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp
+#: xbmc/settings/dialogs/GUIDialogContentSettings.cpp
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
#: addons/skin.estuary/xml/DialogKeyboard.xml
msgctxt "#222"
msgid "Cancel"
@@ -3020,6 +3030,8 @@ msgctxt "#660"
msgid "Volume amplification"
msgstr ""
+#: xbmc/interfaces/builtins/LibraryBuiltins.cpp
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
msgctxt "#661"
msgid "Choose export folder"
msgstr ""
@@ -4728,6 +4740,7 @@ msgstr ""
#: xbmc/guilib/WindowIDs.h
#: addons/skin.estuary/xml/AddonBrowser.xml
#: xbmc/settings/dialogs/GUIDialogContentSettings.cpp
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
msgctxt "#10004"
msgid "Settings"
msgstr ""
@@ -12544,7 +12557,13 @@ msgctxt "#20222"
msgid "Look for external subtitles"
msgstr ""
-#empty strings from id 20223 to 20239
+#: system/settings/settings.xml
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+msgctxt "#20223"
+msgid "Artist information folder"
+msgstr ""
+
+#empty strings from id 20224 to 20239
#: xbmc/dialogs/GUIDialogMediaSource.cpp
msgctxt "#20240"
@@ -12741,6 +12760,7 @@ msgctxt "#20332"
msgid "Use file or folder names in lookups?"
msgstr ""
+#: xbmc/dialogs/GUIDialogContentSettings.cpp
msgctxt "#20333"
msgid "Set content"
msgstr ""
@@ -12809,6 +12829,7 @@ msgctxt "#20343"
msgid "TV shows"
msgstr ""
+#. xbmc/settings/dialogs/GUIDialogContentSettings.cpp
#: addons/skin.estuary/xml/DialogContentSettings.xml
msgctxt "#20344"
msgid "This directory contains"
@@ -12983,6 +13004,7 @@ msgctxt "#20378"
msgid "Refresh information for all episodes?"
msgstr ""
+#. xbmc/settings/dialogs/GUIDialogContentSettings.cpp
msgctxt "#20379"
msgid "Selected folder contains a single TV show"
msgstr ""
@@ -13210,6 +13232,7 @@ msgctxt "#20425"
msgid "Fanart slideshow"
msgstr ""
+#. xbmc/interfaces/builtins/LibraryBuiltins.cpp
msgctxt "#20426"
msgid "Export to a single file or separate files per entry?"
msgstr ""
@@ -13219,14 +13242,17 @@ msgctxt "#20427"
msgid "Choose rule type"
msgstr ""
+#. xbmc/interfaces/builtins/LibraryBuiltins.cpp
msgctxt "#20428"
msgid "Single file"
msgstr ""
+#. xbmc/interfaces/builtins/LibraryBuiltins.cpp
msgctxt "#20429"
msgid "Separate"
msgstr ""
+#. xbmc/interfaces/builtins/LibraryBuiltins.cpp
msgctxt "#20430"
msgid "Export thumbnails and fanart?"
msgstr ""
@@ -13235,6 +13261,7 @@ msgctxt "#20431"
msgid "Overwrite old files?"
msgstr ""
+#. xbmc/settings/dialogs/GUIDialogContentSettings.cpp
msgctxt "#20432"
msgid "Exclude path from library updates"
msgstr ""
@@ -14881,7 +14908,7 @@ msgctxt "#24040"
msgid "(Clear the current setting)"
msgstr ""
-#: xbmc/addons/GUIViewStateAddonBrowser.cpp
+#: xbmc/addons/GUIWindowAddonBrowser.cpp
msgctxt "#24041"
msgid "Install from zip file"
msgstr ""
@@ -18040,7 +18067,7 @@ msgstr ""
#. Description of setting with label #648 "Import video library"
#: system/settings/settings.xml
msgctxt "#36150"
-msgid "Import a XML file into the video library database."
+msgid "Import an XML file into the video library database."
msgstr ""
#. Description of settings category with label #14086 "Playback"
@@ -18633,13 +18660,13 @@ msgstr ""
#. Description of setting with label #20196 "Export music library"
#: system/settings/settings.xml
msgctxt "#36262"
-msgid "Export the music library database to XML files. This will optionally overwrite your current XML files."
+msgid "Export parts of the music library to an XML file or NFO files. This will optionally overwrite your current NFO and artwork files."
msgstr ""
#. Description of setting with label #20197 "Import music library"
#: system/settings/settings.xml
msgctxt "#36263"
-msgid "Import a XML file into the music library database."
+msgid "Import an XML file into the music library database."
msgstr ""
#. Description of settings category with label #14086 "Playback"
@@ -18816,7 +18843,13 @@ msgctxt "#36292"
msgid "This category contains startup settings."
msgstr ""
-#empty strings from id 36293 to 36301
+#. Description of setting with label #20223 "Artist information folder"
+#: system/settings/settings.xml
+msgctxt "#36293"
+msgid "Select the folder where artist information (nfo files and images) should be saved in."
+msgstr ""
+
+#empty strings from id 36294 to 36301
#: system/settings/settings.xml
msgctxt "#36302"
@@ -20987,7 +21020,111 @@ msgctxt "#38209"
msgid "Reset resume position"
msgstr ""
-#empty strings from id 38210 to 38999
+#empty strings from id 38210 to 38299
+
+#. Dialog header for settings for how to export specific music library data
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+msgctxt "#38300"
+msgid "Export Music Library"
+msgstr ""
+
+#. Kind of export output, see #38304
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+msgctxt "#38301"
+msgid "Single file"
+msgstr ""
+
+#. Kind of export output, see #38304
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+msgctxt "#38302"
+msgid "Separate files for each item"
+msgstr ""
+
+#. Kind of export output, see #38304
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+msgctxt "#38303"
+msgid "To library folders"
+msgstr ""
+
+#. Selection of export to a single file, many separate files or the library folder(s)
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+msgctxt "#38304"
+msgid "Choose kind of export output"
+msgstr ""
+
+#. Folder path for export output file or files
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+msgctxt "#38305"
+msgid "Destination folder"
+msgstr ""
+
+#. Section of music items to export - albums, album artists, song artists etc.
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+msgctxt "#38306"
+msgid "Items to export"
+msgstr ""
+
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+msgctxt "#38307"
+msgid "Include art work such as thumbnails and fanart"
+msgstr ""
+
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+msgctxt "#38308"
+msgid "Include items that have not been scraped (to create template NFO files)"
+msgstr ""
+
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+msgctxt "#38309"
+msgid "Output only artwork, not NFO files"
+msgstr ""
+
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+msgctxt "#38310"
+msgid "Overwrite existing files"
+msgstr ""
+
+#empty string id 38311
+
+#. Music item to export output, see #38093
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+msgctxt "#38312"
+msgid "Song artists"
+msgstr ""
+
+#. Music item to export output, see #38093
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+msgctxt "#38313"
+msgid "Other artists"
+msgstr ""
+
+#empty strings from id 38314 to 38316
+
+#. Message when trying to do a kind #38303 of export and setting #20223 is empty
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+msgctxt "#38317"
+msgid "Unable to export to library folders as the system artist information folder setting is empty"
+msgstr ""
+
+#. Message when trying to do export and destination folder #38305 does not exist
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+msgctxt "#38318"
+msgid "Unable to export data as the destination folder does not exist"
+msgstr ""
+
+#. Button text to start export
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+msgctxt "#38319"
+msgid "Export"
+msgstr ""
+
+#. Message when adding music source and setting #20223 is empty
+#: xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+msgctxt "#38320"
+msgid "Do you have local artist information (NFO) and art files that you want to fetch? Set the location of these artist folders now"
+msgstr ""
+
+#empty strings from id 38321 to 38999
#: system/settings/settings.xml
msgctxt "#39000"
diff --git a/cmake/treedata/common/music.txt b/cmake/treedata/common/music.txt
index 71f30e16fa..29aae7b135 100644
--- a/cmake/treedata/common/music.txt
+++ b/cmake/treedata/common/music.txt
@@ -1,5 +1,6 @@
xbmc/music music
xbmc/music/dialogs music/dialogs
xbmc/music/infoscanner music/infoscanner
+xbmc/music/jobs music/jobs
xbmc/music/tags music/tags
xbmc/music/windows music/windows
diff --git a/system/settings/settings.xml b/system/settings/settings.xml
index bcaf14e315..2d276e3d95 100755
--- a/system/settings/settings.xml
+++ b/system/settings/settings.xml
@@ -893,6 +893,38 @@
<level>2</level>
<control type="button" format="action" />
</setting>
+ <!-- Hidden settings edited using CGUIDialogMusicExportSettings -->
+ <setting id="musiclibrary.exportfiletype" type="integer" label="38304" help="">
+ <level>4</level>
+ <default>0</default>
+ </setting>
+ <setting id="musiclibrary.exportfolder" type="string" label="38305" help="">
+ <level>4</level>
+ <default></default>
+ <constraints>
+ <allowempty>true</allowempty>
+ </constraints>
+ </setting>
+ <setting id="musiclibrary.exportitems" type="integer" label="" help="">
+ <level>4</level>
+ <default>48</default> <!-- Albums + Album Artists -->
+ </setting>
+ <setting id="musiclibrary.exportunscraped" type="boolean" label="" help="">
+ <level>4</level>
+ <default>false</default>
+ </setting>
+ <setting id="musiclibrary.exportoverwrite" type="boolean" label="" help="">
+ <level>4</level>
+ <default>false</default>
+ </setting>
+ <setting id="musiclibrary.exportartwork" type="boolean" label="" help="">
+ <level>4</level>
+ <default>false</default>
+ </setting>
+ <setting id="musiclibrary.exportskipnfo" type="boolean" label="" help="">
+ <level>4</level>
+ <default>false</default>
+ </setting>
<setting id="musiclibrary.import" type="action" label="14249" help="36263">
<level>2</level>
<control type="button" format="action" />
@@ -1135,6 +1167,16 @@
<default>false</default>
<control type="toggle" />
</setting>
+ <setting id="musiclibrary.artistsfolder" type="path" label="20223" help="36293">
+ <level>1</level>
+ <default></default>
+ <constraints>
+ <allowempty>true</allowempty>
+ </constraints>
+ <control type="button" format="path">
+ <heading>657</heading>
+ </control>
+ </setting>
<setting id="musiclibrary.albumsscraper" type="addon" label="20193" help="36257">
<level>1</level>
<default>metadata.album.universal</default>
diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp
index 87230f7cba..0e25a9498c 100644
--- a/xbmc/Application.cpp
+++ b/xbmc/Application.cpp
@@ -5040,18 +5040,15 @@ void CApplication::StartMusicScan(const std::string &strDirectory, bool userInit
if (m_musicInfoScanner->IsScanning())
return;
+ // Setup default flags
if (!flags)
- { // setup default flags
+ { // Online scraping of additional info during scanning
if (m_ServiceManager->GetSettings().GetBool(CSettings::SETTING_MUSICLIBRARY_DOWNLOADINFO))
flags |= CMusicInfoScanner::SCAN_ONLINE;
- if (!userInitiated || m_ServiceManager->GetSettings().GetBool(CSettings::SETTING_MUSICLIBRARY_BACKGROUNDUPDATE))
- flags |= CMusicInfoScanner::SCAN_BACKGROUND;
- // Ask for full rescan of music files
- //! @todo replace with a music library setting in UI
- if (g_advancedSettings.m_bMusicLibraryPromptFullTagScan)
- if (CGUIDialogYesNo::ShowAndGetInput(CVariant{ 799 }, CVariant{ 38062 }))
- flags |= CMusicInfoScanner::SCAN_RESCAN;
}
+ if (!userInitiated || m_ServiceManager->GetSettings().GetBool(CSettings::SETTING_MUSICLIBRARY_BACKGROUNDUPDATE))
+ flags |= CMusicInfoScanner::SCAN_BACKGROUND;
+
if (!(flags & CMusicInfoScanner::SCAN_BACKGROUND))
m_musicInfoScanner->ShowDialog(true);
diff --git a/xbmc/guilib/GUIWindowManager.cpp b/xbmc/guilib/GUIWindowManager.cpp
index abefb31c84..c962e0d801 100644
--- a/xbmc/guilib/GUIWindowManager.cpp
+++ b/xbmc/guilib/GUIWindowManager.cpp
@@ -90,6 +90,7 @@
#include "profiles/dialogs/GUIDialogProfileSettings.h"
#include "profiles/dialogs/GUIDialogLockSettings.h"
#include "settings/dialogs/GUIDialogContentSettings.h"
+#include "settings/dialogs/GUIDialogLibExportSettings.h"
#include "dialogs/GUIDialogBusy.h"
#include "dialogs/GUIDialogKeyboardGeneric.h"
#include "dialogs/GUIDialogKeyboardTouch.h"
@@ -249,6 +250,8 @@ void CGUIWindowManager::CreateWindows()
Add(new CGUIDialogLockSettings);
Add(new CGUIDialogContentSettings);
+
+ Add(new CGUIDialogLibExportSettings);
Add(new CGUIDialogPlayEject);
@@ -351,6 +354,7 @@ bool CGUIWindowManager::DestroyWindows()
DestroyWindow(WINDOW_DIALOG_AUDIO_OSD_SETTINGS);
DestroyWindow(WINDOW_DIALOG_VIDEO_BOOKMARKS);
DestroyWindow(WINDOW_DIALOG_CONTENT_SETTINGS);
+ DestroyWindow(WINDOW_DIALOG_LIBEXPORT_SETTINGS);
DestroyWindow(WINDOW_DIALOG_FAVOURITES);
DestroyWindow(WINDOW_DIALOG_SONG_INFO);
DestroyWindow(WINDOW_DIALOG_SMART_PLAYLIST_EDITOR);
diff --git a/xbmc/guilib/WindowIDs.h b/xbmc/guilib/WindowIDs.h
index e79f190272..50d9ea30b2 100644
--- a/xbmc/guilib/WindowIDs.h
+++ b/xbmc/guilib/WindowIDs.h
@@ -82,6 +82,7 @@
#define WINDOW_DIALOG_PROFILE_SETTINGS 10130
#define WINDOW_DIALOG_LOCK_SETTINGS 10131
#define WINDOW_DIALOG_CONTENT_SETTINGS 10132
+#define WINDOW_DIALOG_LIBEXPORT_SETTINGS 10133
#define WINDOW_DIALOG_FAVOURITES 10134
#define WINDOW_DIALOG_SONG_INFO 10135
#define WINDOW_DIALOG_SMART_PLAYLIST_EDITOR 10136
diff --git a/xbmc/input/WindowTranslator.cpp b/xbmc/input/WindowTranslator.cpp
index 2d3c69439c..0ee770ce31 100644
--- a/xbmc/input/WindowTranslator.cpp
+++ b/xbmc/input/WindowTranslator.cpp
@@ -106,6 +106,7 @@ const CWindowTranslator::WindowMapByName CWindowTranslator::WindowMappingByName
{ "profilesettings" , WINDOW_DIALOG_PROFILE_SETTINGS },
{ "locksettings" , WINDOW_DIALOG_LOCK_SETTINGS },
{ "contentsettings" , WINDOW_DIALOG_CONTENT_SETTINGS },
+ { "libexportsettings" , WINDOW_DIALOG_LIBEXPORT_SETTINGS },
{ "songinformation" , WINDOW_DIALOG_SONG_INFO },
{ "smartplaylisteditor" , WINDOW_DIALOG_SMART_PLAYLIST_EDITOR },
{ "smartplaylistrule" , WINDOW_DIALOG_SMART_PLAYLIST_RULE },
diff --git a/xbmc/interfaces/builtins/LibraryBuiltins.cpp b/xbmc/interfaces/builtins/LibraryBuiltins.cpp
index a318d161fb..79573d36d5 100644
--- a/xbmc/interfaces/builtins/LibraryBuiltins.cpp
+++ b/xbmc/interfaces/builtins/LibraryBuiltins.cpp
@@ -29,6 +29,8 @@
#include "MediaSource.h"
#include "messaging/helpers/DialogHelper.h"
#include "music/MusicDatabase.h"
+#include "music/MusicLibraryQueue.h"
+#include "settings/LibExportSettings.h"
#include "storage/MediaManager.h"
#include "utils/log.h"
#include "utils/StringUtils.h"
@@ -67,8 +69,8 @@ static int CleanLibrary(const std::vector<std::string>& params)
/*! \brief Export a library.
* \param params The parameters.
* \details params[0] = "video" or "music".
- * params[1] = "true" to export to a single file (optional).
- * params[2] = "true" to export thumbs (optional).
+ * params[1] = "true" to export to separate files (optional).
+ * params[2] = "true" to export thumbs (optional) or the file path for export to singlefile.
* params[3] = "true" to overwrite existing files (optional).
* params[4] = "true" to export actor thumbs (optional).
*/
@@ -160,18 +162,90 @@ static int ExportLibrary(const std::vector<std::string>& params)
}
else
{
- if (URIUtils::HasSlashAtEnd(path))
- path = URIUtils::AddFileToFolder(path, "musicdb.xml");
- CMusicDatabase musicdatabase;
- musicdatabase.Open();
- musicdatabase.ExportToXML(path, singleFile, thumbs, overwrite);
- musicdatabase.Close();
+ CLibExportSettings settings;
+ // ELIBEXPORT_SINGLEFILE, ELIBEXPORT_ALBUMS + ELIBEXPORT_ALBUMARTISTS by default
+ settings.m_strPath = path;
+ if (!singleFile)
+ settings.SetExportType(ELIBEXPORT_TOLIBRARYFOLDER);
+ settings.m_artwork = thumbs;
+ settings.m_overwrite = overwrite;
+ // Export music library (not showing progress dialog)
+ CMusicLibraryQueue::GetInstance().ExportLibrary(settings, false);
}
}
return 0;
}
+/*! \brief Export a library with extended parameters
+Avoiding breaking change to original ExportLibrary routine parameters
+* \param params The parameters.
+* \details params[0] = "video" or "music".
+* params[1] = export type "singlefile", "separate", or "library".
+* params[2] = path of destination folder.
+* params[3,...] = "unscraped" to include unscraped items
+* params[3,...] = "overwrite" to overwrite exitsing files.
+* params[3,...] = "artwork" to include images such as thumbs and fanart.
+* params[3,...] = "skipnfo" to not include nfo files (just art).
+* params[3,...] = "ablums" to include albums.
+* params[3,...] = "albumartists" to include album artists.
+* params[3,...] = "songartists" to include song artists.
+* params[3,...] = "otherartists" to include other artists.
+*/
+static int ExportLibrary2(const std::vector<std::string>& params)
+{
+ CLibExportSettings settings;
+ if (params.size() < 3)
+ return -1;
+ settings.m_strPath = params[2];
+ settings.SetExportType(ELIBEXPORT_SINGLEFILE);
+ if (StringUtils::EqualsNoCase(params[1], "separate"))
+ settings.SetExportType(ELIBEXPORT_SEPARATEFILES);
+ else if (StringUtils::EqualsNoCase(params[1], "library"))
+ {
+ settings.SetExportType(ELIBEXPORT_TOLIBRARYFOLDER);
+ settings.m_strPath.clear();
+ }
+ settings.ClearItems();
+
+ for (unsigned int i = 2; i < params.size(); i++)
+ {
+ if (StringUtils::EqualsNoCase(params[i], "artwork"))
+ settings.m_artwork = true;
+ else if (StringUtils::EqualsNoCase(params[i], "overwrite"))
+ settings.m_overwrite = true;
+ else if (StringUtils::EqualsNoCase(params[i], "unscraped"))
+ settings.m_unscraped = true;
+ else if (StringUtils::EqualsNoCase(params[i], "skipnfo"))
+ settings.m_skipnfo = true;
+ else if (StringUtils::EqualsNoCase(params[i], "albums"))
+ settings.AddItem(ELIBEXPORT_ALBUMS);
+ else if (StringUtils::EqualsNoCase(params[i], "albumartists"))
+ settings.AddItem(ELIBEXPORT_ALBUMARTISTS);
+ else if (StringUtils::EqualsNoCase(params[i], "songartists"))
+ settings.AddItem(ELIBEXPORT_SONGARTISTS);
+ else if (StringUtils::EqualsNoCase(params[i], "otherartists"))
+ settings.AddItem(ELIBEXPORT_OTHERARTISTS);
+ else if (StringUtils::EqualsNoCase(params[i], "actorthumbs"))
+ settings.AddItem(ELIBEXPORT_ACTORTHUMBS);
+ }
+ if (StringUtils::EqualsNoCase(params[0], "music"))
+ {
+ // Export music library (not showing progress dialog)
+ CMusicLibraryQueue::GetInstance().ExportLibrary(settings, false);
+ }
+ else
+ {
+ CVideoDatabase videodatabase;
+ videodatabase.Open();
+ videodatabase.ExportToXML(settings.m_strPath, settings.IsSingleFile(),
+ settings.m_artwork, settings.IsItemExported(ELIBEXPORT_ACTORTHUMBS), settings.m_overwrite);
+ videodatabase.Close();
+ }
+ return 0;
+}
+
+
/*! \brief Update a library.
* \param params The parameters.
* \details params[0] = "video" or "music".
@@ -233,12 +307,30 @@ static int SearchVideoLibrary(const std::vector<std::string>& params)
/// ,
/// Export the video/music library
/// @param[in] type "video" or "music".
-/// @param[in] exportSingleFile Add "true" to export to a single file (optional).
+/// @param[in] exportSingleFile Add "true" to export to separate files (optional).
/// @param[in] exportThumbs Add "true" to export thumbs (optional).
/// @param[in] overwrite Add "true" to overwrite existing files (optional).
/// @param[in] exportActorThumbs Add "true" to export actor thumbs (optional).
/// }
/// \table_row2_l{
+/// <b>`exportlibrary2(library\, exportFiletype\, path [\, unscraped][\, overwrite][\, artwork][\, skipnfo]
+/// [\, albums][\, albumartists][\, songartists][\, otherartists][\, actorthumbs])`</b>
+/// ,
+/// Export the video/music library with extended parameters
+/// @param[in] library "video" or "music".
+/// @param[in] exportFiletype "singlefile", "separate" or "library"
+/// @param[in] path Path to destination folder
+/// @param[in] unscraped Add "unscraped" to include unscraped items
+/// @param[in] overwrite Add "overwrite" to overwrite exitsing files.
+/// @param[in] artwork Add "artwork" to include images such as thumbs and fanart.
+/// @param[in] skipnfo Add "skipnfo" to not include nfo files(just art).
+/// @param[in] albums Add "ablums" to include albums.
+/// @param[in] albumartists Add "albumartists" to include album artists.
+/// @param[in] songartists Add "songartists" to include song artists.
+/// @param[in] otherartists Add "otherartists" to include other artists.
+/// @param[in] actorthumbs Add "actorthumbs" to include other actor thumbs.
+/// }
+/// \table_row2_l{
/// <b>`updatelibrary([type\, suppressDialogs])`</b>
/// ,
/// Update the selected library (music or video)
@@ -258,6 +350,7 @@ CBuiltins::CommandMap CLibraryBuiltins::GetOperations() const
return {
{"cleanlibrary", {"Clean the video/music library", 1, CleanLibrary}},
{"exportlibrary", {"Export the video/music library", 1, ExportLibrary}},
+ {"exportlibrary2", {"Export the video/music library", 1, ExportLibrary2}},
{"updatelibrary", {"Update the selected library (music or video)", 1, UpdateLibrary}},
{"videolibrary.search", {"Brings up a search dialog which will search the library", 0, SearchVideoLibrary}}
};
diff --git a/xbmc/interfaces/json-rpc/AudioLibrary.cpp b/xbmc/interfaces/json-rpc/AudioLibrary.cpp
index f3c70c2d8c..82a0c16714 100644
--- a/xbmc/interfaces/json-rpc/AudioLibrary.cpp
+++ b/xbmc/interfaces/json-rpc/AudioLibrary.cpp
@@ -686,12 +686,16 @@ JSONRPC_STATUS CAudioLibrary::Export(const std::string &method, ITransportLayer
{
std::string cmd;
if (parameterObject["options"].isMember("path"))
- cmd = StringUtils::Format("exportlibrary(music, false, %s)", StringUtils::Paramify(parameterObject["options"]["path"].asString()).c_str());
+ cmd = StringUtils::Format("exportlibrary2(music, singlefile, %s, albums, albumartists)", StringUtils::Paramify(parameterObject["options"]["path"].asString()).c_str());
else
- cmd = StringUtils::Format("exportlibrary(music, true, %s, %s)",
- parameterObject["options"]["images"].asBoolean() ? "true" : "false",
- parameterObject["options"]["overwrite"].asBoolean() ? "true" : "false");
-
+ {
+ cmd = "exportlibrary2(music, library, dummy, albums, albumartists";
+ if (parameterObject["options"].isMember("images"))
+ cmd += ", artwork";
+ if (parameterObject["options"].isMember("overwrite"))
+ cmd += ", overwrite";
+ cmd += ")";
+ }
CApplicationMessenger::GetInstance().SendMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, cmd);
return ACK;
}
diff --git a/xbmc/interfaces/json-rpc/VideoLibrary.cpp b/xbmc/interfaces/json-rpc/VideoLibrary.cpp
index 34cf7f3f43..37f126b8c0 100644
--- a/xbmc/interfaces/json-rpc/VideoLibrary.cpp
+++ b/xbmc/interfaces/json-rpc/VideoLibrary.cpp
@@ -891,12 +891,18 @@ JSONRPC_STATUS CVideoLibrary::Export(const std::string &method, ITransportLayer
{
std::string cmd;
if (parameterObject["options"].isMember("path"))
- cmd = StringUtils::Format("exportlibrary(video, false, %s)", StringUtils::Paramify(parameterObject["options"]["path"].asString()).c_str());
+ cmd = StringUtils::Format("exportlibrary2(video, singlefile, %s)", StringUtils::Paramify(parameterObject["options"]["path"].asString()).c_str());
else
- cmd = StringUtils::Format("exportlibrary(video, true, %s, %s, %s)",
- parameterObject["options"]["images"].asBoolean() ? "true" : "false",
- parameterObject["options"]["overwrite"].asBoolean() ? "true" : "false",
- parameterObject["options"]["actorthumbs"].asBoolean() ? "true" : "false");
+ {
+ cmd = "exportlibrary2(video, separate, dummy";
+ if (parameterObject["options"].isMember("images"))
+ cmd += ", artwork";
+ if (parameterObject["options"].isMember("overwrite"))
+ cmd += ", overwrite";
+ if (parameterObject["options"].isMember("actorthumbs"))
+ cmd += ", actorthumbs";
+ cmd += ")";
+ }
CApplicationMessenger::GetInstance().SendMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, cmd);
return ACK;
diff --git a/xbmc/music/Artist.cpp b/xbmc/music/Artist.cpp
index d4a33dcb3f..822a62e582 100644
--- a/xbmc/music/Artist.cpp
+++ b/xbmc/music/Artist.cpp
@@ -60,8 +60,13 @@ void CArtist::MergeScrapedArtist(const CArtist& source, bool override /* = true
strDied = source.strDied;
strDisbanded = source.strDisbanded;
yearsActive = source.yearsActive;
- thumbURL = source.thumbURL;
- fanart = source.fanart;
+
+ thumbURL = source.thumbURL; // Available remote thumbs
+ fanart = source.fanart; // Available remote fanart
+ // Current artwork - thumb, fanart etc., to be stored in art table
+ if (!source.art.empty())
+ art = source.art;
+
discography = source.discography;
}
@@ -90,6 +95,7 @@ bool CArtist::Load(const TiXmlElement *artist, bool append, bool prioritise)
size_t iThumbCount = thumbURL.m_url.size();
std::string xmlAdd = thumbURL.m_xml;
+ // Available artist thumbs
const TiXmlElement* thumb = artist->FirstChildElement("thumb");
while (thumb)
{
@@ -110,6 +116,8 @@ bool CArtist::Load(const TiXmlElement *artist, bool append, bool prioritise)
thumbURL.m_url.end());
thumbURL.m_xml = xmlAdd;
}
+
+ // Discography
const TiXmlElement* node = artist->FirstChildElement("album");
while (node)
{
@@ -126,7 +134,7 @@ bool CArtist::Load(const TiXmlElement *artist, bool append, bool prioritise)
node = node->NextSiblingElement("album");
}
- // fanart
+ // Available artist fanart
const TiXmlElement *fanart2 = artist->FirstChildElement("fanart");
if (fanart2)
{
@@ -142,6 +150,18 @@ bool CArtist::Load(const TiXmlElement *artist, bool append, bool prioritise)
fanart.Unpack();
}
+ // Current artwork - thumb, fanart etc. (the chosen art, not the lists of those available)
+ node = artist->FirstChildElement("art");
+ if (node)
+ {
+ const TiXmlNode *artdetailNode = node->FirstChild();
+ while (artdetailNode && artdetailNode->FirstChild())
+ {
+ art.insert(make_pair(artdetailNode->ValueStr(), artdetailNode->FirstChild()->ValueStr()));
+ artdetailNode = artdetailNode->NextSibling();
+ }
+ }
+
return true;
}
@@ -168,6 +188,7 @@ bool CArtist::Save(TiXmlNode *node, const std::string &tag, const std::string& s
XMLUtils::SetString(artist, "biography", strBiography);
XMLUtils::SetString(artist, "died", strDied);
XMLUtils::SetString(artist, "disbanded", strDisbanded);
+ // Available thumbs
if (!thumbURL.m_xml.empty())
{
CXBMCTinyXML doc;
@@ -180,6 +201,7 @@ bool CArtist::Save(TiXmlNode *node, const std::string &tag, const std::string& s
}
}
XMLUtils::SetString(artist, "path", strPath);
+ // Available fanart
if (fanart.m_xml.size())
{
CXBMCTinyXML doc;
@@ -187,7 +209,7 @@ bool CArtist::Save(TiXmlNode *node, const std::string &tag, const std::string& s
artist->InsertEndChild(*doc.RootElement());
}
- // albums
+ // Discography
for (std::vector<std::pair<std::string,std::string> >::const_iterator it = discography.begin(); it != discography.end(); ++it)
{
// add a <album> tag
diff --git a/xbmc/music/Artist.h b/xbmc/music/Artist.h
index 83d7457631..bc3ead5ac3 100644
--- a/xbmc/music/Artist.h
+++ b/xbmc/music/Artist.h
@@ -69,6 +69,7 @@ public:
strDisbanded.clear();
yearsActive.clear();
thumbURL.Clear();
+ art.clear();
discography.clear();
idArtist = -1;
strPath.clear();
@@ -103,8 +104,9 @@ public:
std::string strDisbanded;
std::vector<std::string> yearsActive;
std::string strPath;
- CScraperUrl thumbURL;
- CFanart fanart;
+ CScraperUrl thumbURL; // Data for available thumbs
+ CFanart fanart; // Data for available fanart, urls etc.
+ std::map<std::string, std::string> art; // Current artwork - thumb, fanart etc.
std::vector<std::pair<std::string,std::string> > discography;
CDateTime dateAdded;
bool bScrapedMBID;
diff --git a/xbmc/music/CMakeLists.txt b/xbmc/music/CMakeLists.txt
index 92d8bad212..97b19a72f0 100644
--- a/xbmc/music/CMakeLists.txt
+++ b/xbmc/music/CMakeLists.txt
@@ -5,6 +5,7 @@ set(SOURCES Album.cpp
MusicDatabase.cpp
MusicDbUrl.cpp
MusicInfoLoader.cpp
+ MusicLibraryQueue.cpp
MusicThumbLoader.cpp
Song.cpp)
@@ -16,6 +17,7 @@ set(HEADERS Album.h
MusicDatabase.h
MusicDbUrl.h
MusicInfoLoader.h
+ MusicLibraryQueue.h
MusicThumbLoader.h
Song.h)
diff --git a/xbmc/music/MusicDatabase.cpp b/xbmc/music/MusicDatabase.cpp
index aec9a3d9e1..ea54de35e2 100644
--- a/xbmc/music/MusicDatabase.cpp
+++ b/xbmc/music/MusicDatabase.cpp
@@ -50,7 +50,6 @@
#include "playlists/SmartPlayList.h"
#include "profiles/ProfilesManager.h"
#include "settings/AdvancedSettings.h"
-#include "settings/MediaSettings.h"
#include "settings/Settings.h"
#include "Song.h"
#include "storage/MediaManager.h"
@@ -58,6 +57,7 @@
#include "TextureCache.h"
#include "threads/SystemClock.h"
#include "URL.h"
+#include "Util.h"
#include "utils/FileUtils.h"
#include "utils/LegacyPathTranslation.h"
#include "utils/log.h"
@@ -330,7 +330,7 @@ void CMusicDatabase::CreateViews()
" bCompilation, "
" bScrapedMBID,"
" lastScraped,"
- " (SELECT AVG(song.iTimesPlayed) FROM song WHERE song.idAlbum = album.idAlbum) AS iTimesPlayed, "
+ " (SELECT ROUND(AVG(song.iTimesPlayed)) FROM song WHERE song.idAlbum = album.idAlbum) AS iTimesPlayed, "
" strReleaseType, "
" (SELECT MAX(song.dateAdded) FROM song WHERE song.idAlbum = album.idAlbum) AS dateAdded, "
" (SELECT MAX(song.lastplayed) FROM song WHERE song.idAlbum = album.idAlbum) AS lastplayed "
@@ -1115,6 +1115,10 @@ bool CMusicDatabase::UpdateArtist(const CArtist& artist)
AddArtistDiscography(artist.idArtist, disc.first, disc.second);
}
+ // Set current artwork (held in art table)
+ if (!artist.art.empty())
+ SetArtForItem(artist.idArtist, MediaTypeArtist, artist.art);
+
return true;
}
@@ -1399,6 +1403,16 @@ bool CMusicDatabase::GetArtistExists(int idArtist)
return false;
}
+int CMusicDatabase::GetLastArtist()
+{
+ std::string strSQL = "SELECT MAX(idArtist) FROM artist";
+ std::string lastArtist = GetSingleValue(strSQL);
+ if (lastArtist.empty())
+ return -1;
+
+ return static_cast<int>(strtol(lastArtist.c_str(), NULL, 10));
+}
+
bool CMusicDatabase::HasArtistBeenScraped(int idArtist)
{
std::string strSQL = PrepareSQL("SELECT idArtist FROM artist WHERE idArtist = %i AND lastScraped IS NULL", idArtist);
@@ -5319,34 +5333,65 @@ int CMusicDatabase::GetSongsCount(const Filter &filter)
return 0;
}
-bool CMusicDatabase::GetAlbumPath(int idAlbum, std::string& path)
+bool CMusicDatabase::GetAlbumPath(int idAlbum, std::string &basePath)
{
+ std::string strSQL;
try
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS2.get()) return false;
- path.clear();
+ basePath.clear();
+
+ // Get the unique paths of songs on the album, providing there are no songs from
+ // other albums with the same path. This returns
+ // a) <album> if is contains all the songs and no others, or
+ // b) <album>/cd1, <album>/cd2 etc. for disc sets
+ // but does *not* return any path when albums are mixed together. That could be because of
+ // deliberate file organisation, or (more likely) because of a tagging error in album name
+ // or Musicbrainzalbumid. Thus it avoids finding somme generic music path.
+ strSQL = PrepareSQL("SELECT DISTINCT strPath FROM song "
+ "JOIN path ON song.idPath = path.idPath "
+ "WHERE song.idAlbum = %ld "
+ "AND (SELECT COUNT(DISTINCT(idAlbum)) FROM song AS song2 "
+ "WHERE idPath = song.idPath) = 1", idAlbum);
- std::string strSQL=PrepareSQL("select strPath from song join path on song.idPath = path.idPath where song.idAlbum=%ld", idAlbum);
if (!m_pDS2->query(strSQL)) return false;
int iRowsFound = m_pDS2->num_rows();
+
if (iRowsFound == 0)
{
+ // Album does not have a unique path, files are mixed
m_pDS2->close();
return false;
}
+ else if (iRowsFound == 1)
+ {
+ // Path contains all the songs and no others
+ basePath = m_pDS2->fv("strPath").get_asString();
+ }
+ else
+ {
+ // e.g. <album>/cd1, <album>/cd2 etc. for disc sets
+ // Find the common path
+ while (!m_pDS2->eof())
+ {
+ std::string path = m_pDS2->fv("strPath").get_asString();
+ if (basePath.empty())
+ basePath = path;
+ else
+ URIUtils::GetCommonPath(basePath, path);
- // if this returns more than one path, we just grab the first one. It's just for determining where to obtain + place
- // a local thumbnail
- path = m_pDS2->fv("strPath").get_asString();
-
- m_pDS2->close(); // cleanup recordset data
+ m_pDS2->next();
+ }
+ }
+ // Cleanup recordset data
+ m_pDS2->close();
return true;
}
catch (...)
{
- CLog::Log(LOGERROR, "%s(%i) failed", __FUNCTION__, idAlbum);
+ CLog::Log(LOGERROR, "CMusicDatabase::%s - failed to execute %s", __FUNCTION__, strSQL.c_str());
}
return false;
@@ -5366,7 +5411,11 @@ bool CMusicDatabase::SaveAlbumThumb(int idAlbum, const std::string& strThumb)
return true;
}
-bool CMusicDatabase::GetArtistPath(int idArtist, std::string &basePath)
+// Get old "artist path" - where artist.nfo and art was located v17 and below.
+// It is the path common to all albums by an (album) artist, but ensure it is unique
+// to that artist and not shared with other artists. Previously this caused incorrect nfo
+// and art to be applied to multiple artists.
+bool CMusicDatabase::GetOldArtistPath(int idArtist, std::string &basePath)
{
basePath.clear();
try
@@ -5375,56 +5424,185 @@ bool CMusicDatabase::GetArtistPath(int idArtist, std::string &basePath)
if (NULL == m_pDS2.get()) return false;
// find all albums from this artist, and all the paths to the songs from those albums
- std::string strSQL=PrepareSQL("SELECT strPath"
- " FROM album_artist"
- " JOIN song "
- " ON album_artist.idAlbum = song.idAlbum"
- " JOIN path"
- " ON song.idPath = path.idPath"
- " WHERE album_artist.idArtist = %i"
- " GROUP BY song.idPath", idArtist);
+ std::string strSQL = PrepareSQL("SELECT strPath FROM album_artist "
+ "JOIN song ON album_artist.idAlbum = song.idAlbum "
+ "JOIN path ON song.idPath = path.idPath "
+ "WHERE album_artist.idArtist = %ld "
+ "GROUP BY song.idPath",
+ idArtist);
// run query
if (!m_pDS2->query(strSQL)) return false;
int iRowsFound = m_pDS2->num_rows();
if (iRowsFound == 0)
{
+ // Artist is not an album artist, no path to find
m_pDS2->close();
return false;
}
-
- // special case for single path - assume that we're in an artist/album/songs filesystem
- if (iRowsFound == 1)
- {
+ else if (iRowsFound == 1)
+ {
+ // Special case for single path - assume that we're in an artist/album/songs filesystem
URIUtils::GetParentPath(m_pDS2->fv("strPath").get_asString(), basePath);
m_pDS2->close();
- return true;
}
-
- // find the common path (if any) to these albums
- while (!m_pDS2->eof())
+ else
{
- std::string path = m_pDS2->fv("strPath").get_asString();
- if (basePath.empty())
- basePath = path;
- else
- URIUtils::GetCommonPath(basePath,path);
+ // find the common path (if any) to these albums
+ while (!m_pDS2->eof())
+ {
+ std::string path = m_pDS2->fv("strPath").get_asString();
+ if (basePath.empty())
+ basePath = path;
+ else
+ URIUtils::GetCommonPath(basePath, path);
- m_pDS2->next();
+ m_pDS2->next();
+ }
+ m_pDS2->close();
}
- // cleanup
- m_pDS2->close();
- return true;
-
+ // Check any path found is unique to that album artist, and do *not* return any path
+ // that is shared with other album artists. That could be because of collaborations
+ // i.e. albums with more than one album artist, or because there are albums by the
+ // artist on multiple music sources, or elsewhere in the folder hierarchy.
+ // Avoid returning some generic music path.
+ if (!basePath.empty())
+ {
+ strSQL = PrepareSQL("SELECT COUNT(album_artist.idArtist) FROM album_artist "
+ "JOIN song ON album_artist.idAlbum = song.idAlbum "
+ "JOIN path ON song.idPath = path.idPath "
+ "WHERE album_artist.idArtist <> %ld "
+ "AND strPath LIKE '%s%%'",
+ idArtist, basePath.c_str());
+ std::string strValue = GetSingleValue(strSQL, m_pDS2);
+ if (!strValue.empty())
+ {
+ int countartists = static_cast<int>(strtol(strValue.c_str(), NULL, 10));
+ if (countartists == 0)
+ return true;
+ }
+ }
}
catch (...)
{
CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
}
+ basePath.clear();
return false;
}
+bool CMusicDatabase::GetArtistPath(const CArtist& artist, std::string &path)
+{
+ // Get path for artist in the artists folder
+ path = CServiceBroker::GetSettings().GetString(CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER);
+ if (path.empty())
+ return false; // No Artists folder not set;
+ // Get unique artist folder name
+ std::string strFolder;
+ if (GetArtistFolderName(artist, strFolder))
+ {
+ path = URIUtils::AddFileToFolder(path, strFolder);
+ return true;
+ }
+ path.clear();
+ return false;
+}
+
+bool CMusicDatabase::GetAlbumFolder(const CAlbum& album, const std::string &strAlbumPath, std::string &strFolder)
+{
+ strFolder.clear();
+
+ // First try to get a *unique* album folder name from the music file paths
+ if (!strAlbumPath.empty())
+ {
+ // Get last folder from full path
+ std::vector<std::string> folders = URIUtils::SplitPath(strAlbumPath);
+ if (!folders.empty())
+ {
+ strFolder = folders.back();
+ // Check paths to see folder name derived this way is unique for the (first) albumartist.
+ // Could have different albums on different path with same first album artist and folder name
+ // or duplicate albums from separate music files on different paths
+ // At least one will have mbid, so append it to start of mbid to folder.
+ std::string strSQL = PrepareSQL("SELECT DISTINCT album_artist.idAlbum FROM album_artist "
+ "JOIN song ON album_artist.idAlbum = song.idAlbum "
+ "JOIN path on path.idPath = song.idPath "
+ "WHERE album_artist.iOrder = 0 "
+ "AND album_artist.idArtist = %ld "
+ "AND path.strPath LIKE '%%%s%%'",
+ album.artistCredits[0].GetArtistId(), strFolder.c_str());
+
+ if (!m_pDS2->query(strSQL))
+ return false;
+ int iRowsFound = m_pDS2->num_rows();
+ m_pDS2->close();
+ if (iRowsFound > 1 && !album.strMusicBrainzAlbumID.empty())
+ { // Only one of the duplicate albums can be without mbid
+ strFolder += "_" + album.strMusicBrainzAlbumID.substr(0, 4);
+ }
+ return true;
+ }
+ }
+ else
+ {
+ // Create a valid unique folder name from album title
+ // @todo: Does UFT8 matter or need normalizing?
+ // @todo: Simplify punctuation removing unicode appostraphes, "..." etc.?
+ strFolder = CUtil::MakeLegalFileName(album.strAlbum, LEGAL_WIN32_COMPAT);
+ StringUtils::Replace(strFolder, " _ ", "_");
+
+ // Check <first albumartist name>/<albumname> is unique e.g. 2 x Bruckner Symphony No. 3
+ // To have duplicate albumartist/album names at least one will have mbid, so append start of mbid to folder.
+ // This will not handle names that only differ by reserved chars e.g. "a>album" and "a?name"
+ // will be unique in db, but produce same folder name "a_name", but that kind of album and artist naming is very unlikely
+ std::string strSQL = PrepareSQL("SELECT COUNT(album_artist.idAlbum) FROM album_artist "
+ "JOIN album ON album_artist.idAlbum = album.idAlbum "
+ "WHERE album_artist.iOrder = 0 "
+ "AND album_artist.idArtist = %ld "
+ "AND album.strAlbum LIKE '%s' ",
+ album.artistCredits[0].GetArtistId(), album.strAlbum.c_str());
+ std::string strValue = GetSingleValue(strSQL, m_pDS2);
+ if (strValue.empty())
+ return false;
+ int countalbum = static_cast<int>(strtol(strValue.c_str(), NULL, 10));
+ if (countalbum > 1 && !album.strMusicBrainzAlbumID.empty())
+ { // Only one of the duplicate albums can be without mbid
+ strFolder += "_" + album.strMusicBrainzAlbumID.substr(0, 4);
+ }
+ return !strFolder.empty();
+ }
+ return false;
+}
+
+bool CMusicDatabase::GetArtistFolderName(const CArtist &artist, std::string &strFolder)
+{
+ return GetArtistFolderName(artist.strArtist, artist.strMusicBrainzArtistID, strFolder);
+}
+
+bool CMusicDatabase::GetArtistFolderName(const std::string &strArtist, const std::string &strMusicBrainzArtistID,
+ std::string &strFolder)
+{
+ // Create a valid unique folder name for artist
+ // @todo: Does UFT8 matter or need normalizing?
+ // @todo: Simplify punctuation removing unicode appostraphes, "..." etc.?
+ strFolder = CUtil::MakeLegalFileName(strArtist, LEGAL_WIN32_COMPAT);
+ StringUtils::Replace(strFolder, " _ ", "_");
+
+ // Ensure <artist name> is unique e.g. 2 x John Williams.
+ // To have duplicate artist names there must both have mbids, so append start of mbid to folder.
+ // This will not handle names that only differ by reserved chars e.g. "a>name" and "a?name"
+ // will be unique in db, but produce same folder name "a_name", but that kind of artist naming is very unlikely
+ std::string strSQL = PrepareSQL("SELECT COUNT(1) FROM artist WHERE strArtist LIKE '%s'", strArtist.c_str());
+ std::string strValue = GetSingleValue(strSQL, m_pDS2);
+ if (strValue.empty())
+ return false;
+ int countartist = static_cast<int>(strtol(strValue.c_str(), NULL, 10));
+ if (countartist > 1)
+ strFolder += "_" + strMusicBrainzArtistID.substr(0, 4);
+ return !strFolder.empty();
+}
+
int CMusicDatabase::GetArtistByName(const std::string& strArtist)
{
try
@@ -5453,6 +5631,40 @@ int CMusicDatabase::GetArtistByName(const std::string& strArtist)
return -1;
}
+int CMusicDatabase::GetArtistByMatch(const CArtist& artist)
+{
+ std::string strSQL;
+ try
+ {
+ if (NULL == m_pDB.get() || NULL == m_pDS.get())
+ return false;
+ // Match on MusicBrainz ID, definitively unique
+ if (!artist.strMusicBrainzArtistID.empty())
+ strSQL = PrepareSQL("SELECT idArtist FROM artist WHERE strMusicBrainzArtistID = '%s'",
+ artist.strMusicBrainzArtistID.c_str());
+ else
+ // No MusicBrainz ID, artist by name with no mbid
+ strSQL = PrepareSQL("SELECT idArtist FROM artist WHERE strArtist LIKE '%s' AND strMusicBrainzArtistID IS NULL",
+ artist.strArtist.c_str());
+ if (!m_pDS->query(strSQL)) return false;
+ int iRowsFound = m_pDS->num_rows();
+ if (iRowsFound != 1)
+ {
+ m_pDS->close();
+ // Match on artist name, relax mbid restriction
+ return GetArtistByName(artist.strArtist);
+ }
+ int lResult = m_pDS->fv("idArtist").get_asInt();
+ m_pDS->close();
+ return lResult;
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "CMusicDatabase::%s - failed to execute %", __FUNCTION__, strSQL.c_str());
+ }
+ return -1;
+}
+
int CMusicDatabase::GetAlbumByName(const std::string& strAlbum, const std::string& strArtist)
{
try
@@ -5464,7 +5676,7 @@ int CMusicDatabase::GetAlbumByName(const std::string& strAlbum, const std::strin
if (strArtist.empty())
strSQL=PrepareSQL("SELECT idAlbum FROM album WHERE album.strAlbum LIKE '%s'", strAlbum.c_str());
else
- strSQL=PrepareSQL("SELECT album.idAlbum FROM album WHERE album.strAlbum LIKE '%s' AND album.strArtistDisp LIKE '%s'", strAlbum.c_str(),strArtist.c_str());
+ strSQL=PrepareSQL("SELECT idAlbum FROM album WHERE album.strAlbum LIKE '%s' AND album.strArtistDisp LIKE '%s'", strAlbum.c_str(),strArtist.c_str());
// run query
if (!m_pDS->query(strSQL)) return false;
int iRowsFound = m_pDS->num_rows();
@@ -5473,7 +5685,7 @@ int CMusicDatabase::GetAlbumByName(const std::string& strAlbum, const std::strin
m_pDS->close();
return -1;
}
- return m_pDS->fv("album.idAlbum").get_asInt();
+ return m_pDS->fv("idAlbum").get_asInt();
}
catch (...)
{
@@ -5487,6 +5699,41 @@ int CMusicDatabase::GetAlbumByName(const std::string& strAlbum, const std::vecto
return GetAlbumByName(strAlbum, StringUtils::Join(artist, g_advancedSettings.m_musicItemSeparator));
}
+int CMusicDatabase::GetAlbumByMatch(const CAlbum &album)
+{
+ std::string strSQL;
+ try
+ {
+ if (NULL == m_pDB.get() || NULL == m_pDS.get())
+ return false;
+ // Match on MusicBrainz ID, definitively unique
+ if (!album.strMusicBrainzAlbumID.empty())
+ strSQL = PrepareSQL("SELECT idAlbum FROM album WHERE strMusicBrainzAlbumID = '%s'", album.strMusicBrainzAlbumID.c_str());
+ else
+ // No mbid, match on album title and album artist descriptive string, ignore those with mbid
+ strSQL = PrepareSQL("SELECT idAlbum FROM album WHERE strArtistDisp LIKE '%s' AND strAlbum LIKE '%s' AND strMusicBrainzAlbumID IS NULL",
+ album.GetAlbumArtistString().c_str(),
+ album.strAlbum.c_str());
+ m_pDS->query(strSQL);
+ if (!m_pDS->query(strSQL)) return false;
+ int iRowsFound = m_pDS->num_rows();
+ if (iRowsFound != 1)
+ {
+ m_pDS->close();
+ // Match on album title and album artist descriptive string, relax mbid restriction
+ return GetAlbumByName(album.strAlbum, album.GetAlbumArtistString());
+ }
+ int lResult = m_pDS->fv("idAlbum").get_asInt();
+ m_pDS->close();
+ return lResult;
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "CMusicDatabase::%s - failed to execute %", __FUNCTION__, strSQL.c_str());
+ }
+ return -1;
+}
+
std::string CMusicDatabase::GetGenreById(int id)
{
return GetSingleValue("genre", "strGenre", PrepareSQL("idGenre=%i", id));
@@ -6232,204 +6479,330 @@ std::string CMusicDatabase::GetItemById(const std::string &itemType, int id)
return "";
}
-void CMusicDatabase::ExportToXML(const std::string &xmlFile, bool singleFile, bool images, bool overwrite)
+void CMusicDatabase::ExportToXML(const CLibExportSettings& settings, CGUIDialogProgress* progressDialog /*= NULL*/)
{
+ if (!settings.IsItemExported(ELIBEXPORT_ALBUMARTISTS) &&
+ !settings.IsItemExported(ELIBEXPORT_SONGARTISTS) &&
+ !settings.IsItemExported(ELIBEXPORT_OTHERARTISTS) &&
+ !settings.IsItemExported(ELIBEXPORT_ALBUMS))
+ return;
+
+ if (!settings.IsSingleFile() && settings.m_skipnfo && !settings.m_artwork)
+ return;
+
+ std::string strFolder;
+ if (!settings.IsToLibFolders())
+ {
+ // Exporting to single file or separate files in a specified location
+ if (settings.m_strPath.empty())
+ return;
+
+ strFolder = settings.m_strPath;
+ if (!URIUtils::HasSlashAtEnd(strFolder))
+ URIUtils::AddSlashAtEnd(strFolder);
+ strFolder = URIUtils::GetDirectory(strFolder);
+ if (strFolder.empty())
+ return;
+ }
+ else
+ {
+ // Separate files with artists to library folder and albums to music folders.
+ // Without an artist information folder can not export artist NFO files or images
+ strFolder = CServiceBroker::GetSettings().GetString(CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER);
+ if (!settings.IsItemExported(ELIBEXPORT_ALBUMS) && strFolder.empty())
+ return;
+ }
+
int iFailCount = 0;
- CGUIDialogProgress *progress=NULL;
try
{
if (NULL == m_pDB.get()) return;
if (NULL == m_pDS.get()) return;
if (NULL == m_pDS2.get()) return;
-
- // find all albums
- std::vector<int> albumIds;
- std::string sql = "select idAlbum FROM album WHERE lastScraped IS NOT NULL";
- m_pDS->query(sql);
-
- int total = m_pDS->num_rows();
- int current = 0;
-
- albumIds.reserve(total);
- while (!m_pDS->eof())
- {
- albumIds.push_back(m_pDS->fv("idAlbum").get_asInt());
- m_pDS->next();
- }
- m_pDS->close();
-
- progress = g_windowManager.GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS);
- if (progress)
- {
- progress->SetHeading(CVariant{20196});
- progress->SetLine(0, CVariant{650});
- progress->SetLine(1, CVariant{""});
- progress->SetLine(2, CVariant{""});
- progress->SetPercentage(0);
- progress->Open();
- progress->ShowProgressBar(true);
- }
-
- // create our xml document
+
+ // Create our xml document
CXBMCTinyXML xmlDoc;
TiXmlDeclaration decl("1.0", "UTF-8", "yes");
xmlDoc.InsertEndChild(decl);
TiXmlNode *pMain = NULL;
- if (!singleFile)
+ if (!settings.IsSingleFile())
pMain = &xmlDoc;
else
{
TiXmlElement xmlMainElement("musicdb");
pMain = xmlDoc.InsertEndChild(xmlMainElement);
}
- for (const auto &albumId : albumIds)
+
+ if (settings.IsItemExported(ELIBEXPORT_ALBUMS))
{
- CAlbum album;
- GetAlbum(albumId, album);
- std::string strPath;
- GetAlbumPath(albumId, strPath);
- album.Save(pMain, "album", strPath);
- if (!singleFile)
+ // Find albums to export
+ std::vector<int> albumIds;
+ std::string strSQL = PrepareSQL("SELECT idAlbum FROM album WHERE strReleaseType = '%s' ",
+ CAlbum::ReleaseTypeToString(CAlbum::Album).c_str());
+ if (!settings.m_unscraped)
+ strSQL += "AND lastScraped IS NOT NULL";
+ CLog::Log(LOGDEBUG, "CMusicDatabase::%s - %s", __FUNCTION__, strSQL.c_str());
+ m_pDS->query(strSQL);
+
+ int total = m_pDS->num_rows();
+ int current = 0;
+
+ albumIds.reserve(total);
+ while (!m_pDS->eof())
{
- if (!CDirectory::Exists(strPath))
- CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, strPath.c_str());
- else
+ albumIds.push_back(m_pDS->fv("idAlbum").get_asInt());
+ m_pDS->next();
+ }
+ m_pDS->close();
+
+ for (const auto &albumId : albumIds)
+ {
+ CAlbum album;
+ GetAlbum(albumId, album);
+ std::string strAlbumPath;
+ std::string strPath;
+ // Get album path, empty unless all album songs are under a unique folder, and
+ // there are no songs from another album in the same folder.
+ if (!GetAlbumPath(albumId, strAlbumPath))
+ strAlbumPath.clear();
+ if (settings.IsSingleFile())
{
- std::string nfoFile = URIUtils::AddFileToFolder(strPath, "album.nfo");
- if (overwrite || !CFile::Exists(nfoFile))
- {
- if (!xmlDoc.SaveFile(nfoFile))
+ // Save album to xml, including album path
+ album.Save(pMain, "album", strAlbumPath);
+ }
+ else
+ { // Separate files and artwork
+ bool pathfound = false;
+ if (settings.IsToLibFolders())
+ { // Save album.nfo and artwork with music files.
+ // Most albums are under a unique folder, but if songs from various albums are mixed then
+ // avoid overwriting by not allow NFO and art to be exported
+ if (strAlbumPath.empty())
+ CLog::Log(LOGDEBUG, "CMusicDatabase::%s - Not exporting album %s as unique path not found",
+ __FUNCTION__, album.strAlbum.c_str());
+ else if (!CDirectory::Exists(strAlbumPath))
+ CLog::Log(LOGDEBUG, "CMusicDatabase::%s - Not exporting album %s as found path %s does not exist",
+ __FUNCTION__, album.strAlbum.c_str(), strAlbumPath.c_str());
+ else
{
- CLog::Log(LOGERROR, "%s: Album nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
- CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(20302), nfoFile);
- iFailCount++;
+ strPath = strAlbumPath;
+ pathfound = true;
}
}
-
- if (images)
+ else
+ { // Save album.nfo and artwork to subfolder on export path
+ // strPath = strFolder/<albumartist name>/<albumname>
+ // where <albumname> is either the same name as the album folder containing the music files (if unique)
+ // or is craeted using the album name
+ std::string strAlbumArtist;
+ pathfound = GetArtistFolderName(album.GetAlbumArtist()[0], album.GetMusicBrainzAlbumArtistID()[0], strAlbumArtist);
+ if (pathfound)
+ {
+ strPath = URIUtils::AddFileToFolder(strFolder, strAlbumArtist);
+ pathfound = CDirectory::Exists(strPath);
+ if (!pathfound)
+ pathfound = CDirectory::Create(strPath);
+ }
+ if (!pathfound)
+ CLog::Log(LOGDEBUG, "CMusicDatabase::%s - Not exporting album %s as could not create %s",
+ __FUNCTION__, album.strAlbum.c_str(), strPath.c_str());
+ else
+ {
+ std::string strAlbumFolder;
+ pathfound = GetAlbumFolder(album, strAlbumPath, strAlbumFolder);
+ if (pathfound)
+ {
+ strPath = URIUtils::AddFileToFolder(strPath, strAlbumFolder);
+ pathfound = CDirectory::Exists(strPath);
+ if (!pathfound)
+ pathfound = CDirectory::Create(strPath);
+ }
+ if (!pathfound)
+ CLog::Log(LOGDEBUG, "CMusicDatabase::%s - Not exporting album %s as could not create %s",
+ __FUNCTION__, album.strAlbum.c_str(), strPath.c_str());
+ }
+ }
+ if (pathfound)
{
- std::string thumb = GetArtForItem(album.idAlbum, MediaTypeAlbum, "thumb");
- std::string imagePath = URIUtils::AddFileToFolder(strPath, "folder.jpg");
- if (!thumb.empty() && (overwrite || !CFile::Exists(imagePath)))
- CTextureCache::GetInstance().Export(thumb, imagePath);
+ if (!settings.m_skipnfo)
+ {
+ // Save album to NFO, including album path
+ album.Save(pMain, "album", strAlbumPath);
+ std::string nfoFile = URIUtils::AddFileToFolder(strPath, "album.nfo");
+ if (settings.m_overwrite || !CFile::Exists(nfoFile))
+ {
+ if (!xmlDoc.SaveFile(nfoFile))
+ {
+ CLog::Log(LOGERROR, "CMusicDatabase::%s: Album nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(20302), nfoFile);
+ iFailCount++;
+ }
+ }
+ }
+ if (settings.m_artwork)
+ {
+ // Save art in album folder
+ // Note thumb resoluton may be lower than original when overwriting
+ std::string thumb = GetArtForItem(album.idAlbum, MediaTypeAlbum, "thumb");
+ std::string imagePath = URIUtils::AddFileToFolder(strPath, "folder.jpg");
+ if (!thumb.empty() && (settings.m_overwrite || !CFile::Exists(imagePath)))
+ CTextureCache::GetInstance().Export(thumb, imagePath);
+ }
+ xmlDoc.Clear();
+ TiXmlDeclaration decl("1.0", "UTF-8", "yes");
+ xmlDoc.InsertEndChild(decl);
}
- xmlDoc.Clear();
- TiXmlDeclaration decl("1.0", "UTF-8", "yes");
- xmlDoc.InsertEndChild(decl);
}
- }
- if ((current % 50) == 0 && progress)
- {
- progress->SetLine(1, CVariant{album.strAlbum});
- progress->SetPercentage(current * 100 / total);
- progress->Progress();
- if (progress->IsCanceled())
+ if ((current % 50) == 0 && progressDialog != NULL)
{
- progress->Close();
- m_pDS->close();
- return;
+ progressDialog->SetLine(1, CVariant{ album.strAlbum });
+ progressDialog->SetPercentage(current * 100 / total);
}
+ current++;
}
- current++;
}
+
+ if ((settings.IsItemExported(ELIBEXPORT_ALBUMARTISTS) ||
+ settings.IsItemExported(ELIBEXPORT_SONGARTISTS) ||
+ settings.IsItemExported(ELIBEXPORT_OTHERARTISTS)) && !strFolder.empty())
+ {
+ // Find artists to export
+ std::vector<int> artistIds;
+ std::string sql;
+ Filter filter;
+
+ if (settings.IsItemExported(ELIBEXPORT_ALBUMARTISTS))
+ filter.AppendWhere("EXISTS(SELECT 1 FROM album_artist WHERE album_artist.idArtist = artist.idArtist)", false);
+ if (settings.IsItemExported(ELIBEXPORT_SONGARTISTS))
+ {
+ if (settings.IsItemExported(ELIBEXPORT_OTHERARTISTS))
+ filter.AppendWhere("EXISTS (SELECT 1 FROM song_artist WHERE song_artist.idArtist = artist.idArtist )", false);
+ else
+ filter.AppendWhere("EXISTS (SELECT 1 FROM song_artist WHERE song_artist.idArtist = artist.idArtist AND song_artist.idRole = 1)", false);
+ }
+ else if (settings.IsItemExported(ELIBEXPORT_OTHERARTISTS))
+ filter.AppendWhere("EXISTS (SELECT 1 FROM song_artist WHERE song_artist.idArtist = artist.idArtist AND song_artist.idRole > 1)", false);
+
+ if (!settings.m_unscraped)
+ filter.AppendWhere("lastScraped IS NOT NULL", true);
- // find all artists
- std::vector<int> artistIds;
- std::string artistSQL = "SELECT idArtist FROM artist where lastScraped IS NOT NULL";
- m_pDS->query(artistSQL);
- total = m_pDS->num_rows();
- current = 0;
- artistIds.reserve(total);
- while (!m_pDS->eof())
- {
- artistIds.push_back(m_pDS->fv("idArtist").get_asInt());
- m_pDS->next();
- }
- m_pDS->close();
+ std::string strSQL = "SELECT idArtist FROM artist";
+ BuildSQL(strSQL, filter, strSQL);
+ CLog::Log(LOGDEBUG, "CMusicDatabase::%s - %s", __FUNCTION__, strSQL.c_str());
- for (const auto &artistId : artistIds)
- {
- CArtist artist;
- GetArtist(artistId, artist);
- std::string strPath;
- GetArtistPath(artist.idArtist,strPath);
- artist.Save(pMain, "artist", strPath);
-
- std::map<std::string, std::string> artwork;
- if (GetArtForItem(artist.idArtist, MediaTypeArtist, artwork) && singleFile)
- { // append to the XML
- TiXmlElement additionalNode("art");
- for (const auto &i : artwork)
- XMLUtils::SetString(&additionalNode, i.first.c_str(), i.second);
- pMain->LastChild()->InsertEndChild(additionalNode);
+ m_pDS->query(strSQL);
+ int total = m_pDS->num_rows();
+ int current = 0;
+ artistIds.reserve(total);
+ while (!m_pDS->eof())
+ {
+ artistIds.push_back(m_pDS->fv("idArtist").get_asInt());
+ m_pDS->next();
}
- if (!singleFile)
+ m_pDS->close();
+
+ for (const auto &artistId : artistIds)
{
- if (!CDirectory::Exists(strPath))
- CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, strPath.c_str());
- else
+ CArtist artist;
+ GetArtist(artistId, artist);
+ std::string strPath;
+ std::map<std::string, std::string> artwork;
+ if (settings.IsSingleFile())
{
- std::string nfoFile = URIUtils::AddFileToFolder(strPath, "artist.nfo");
- if (overwrite || !CFile::Exists(nfoFile))
+ // Save artist to xml, and old path (common to music files) if it has one
+ GetOldArtistPath(artist.idArtist, strPath);
+ artist.Save(pMain, "artist", strPath);
+
+ if (GetArtForItem(artist.idArtist, MediaTypeArtist, artwork))
+ { // append to the XML
+ TiXmlElement additionalNode("art");
+ for (const auto &i : artwork)
+ XMLUtils::SetString(&additionalNode, i.first.c_str(), i.second);
+ pMain->LastChild()->InsertEndChild(additionalNode);
+ }
+ }
+ else
+ { // Separate files: artist.nfo and artwork in strFolder/<artist name>
+ // Get unique folder allowing for duplicate names e.g. 2 x John Williams
+ bool pathfound = GetArtistFolderName(artist, strPath);
+ if (pathfound)
{
- if (!xmlDoc.SaveFile(nfoFile))
- {
- CLog::Log(LOGERROR, "%s: Artist nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
- CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(20302), nfoFile);
- iFailCount++;
- }
+ strPath = URIUtils::AddFileToFolder(strFolder, strPath);
+ pathfound = CDirectory::Exists(strPath);
+ if (!pathfound)
+ pathfound = CDirectory::Create(strPath);
}
-
- if (images && !artwork.empty())
+ if (!pathfound)
+ CLog::Log(LOGDEBUG, "CMusicDatabase::%s - Not exporting artist %s as could not create %s",
+ __FUNCTION__, artist.strArtist.c_str(), strPath.c_str());
+ else
{
- std::string savedThumb = URIUtils::AddFileToFolder(strPath,"folder.jpg");
- std::string savedFanart = URIUtils::AddFileToFolder(strPath,"fanart.jpg");
- if (artwork.find("thumb") != artwork.end() && (overwrite || !CFile::Exists(savedThumb)))
- CTextureCache::GetInstance().Export(artwork["thumb"], savedThumb);
- if (artwork.find("fanart") != artwork.end() && (overwrite || !CFile::Exists(savedFanart)))
- CTextureCache::GetInstance().Export(artwork["fanart"], savedFanart);
+ if (!settings.m_skipnfo)
+ {
+ artist.Save(pMain, "artist", strPath);
+ std::string nfoFile = URIUtils::AddFileToFolder(strPath, "artist.nfo");
+ if (settings.m_overwrite || !CFile::Exists(nfoFile))
+ {
+ if (!xmlDoc.SaveFile(nfoFile))
+ {
+ CLog::Log(LOGERROR, "CMusicDatabase::%s: Artist nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(20302), nfoFile);
+ iFailCount++;
+ }
+ }
+ }
+ if (settings.m_artwork)
+ {
+ if (GetArtForItem(artist.idArtist, MediaTypeArtist, artwork))
+ {
+ std::string savedThumb = URIUtils::AddFileToFolder(strPath, "folder.jpg");
+ std::string savedFanart = URIUtils::AddFileToFolder(strPath, "fanart.jpg");
+ if (artwork.find("thumb") != artwork.end() && (settings.m_overwrite || !CFile::Exists(savedThumb)))
+ CTextureCache::GetInstance().Export(artwork["thumb"], savedThumb);
+ if (artwork.find("fanart") != artwork.end() && (settings.m_overwrite || !CFile::Exists(savedFanart)))
+ CTextureCache::GetInstance().Export(artwork["fanart"], savedFanart);
+ }
+ }
+ xmlDoc.Clear();
+ TiXmlDeclaration decl("1.0", "UTF-8", "yes");
+ xmlDoc.InsertEndChild(decl);
}
- xmlDoc.Clear();
- TiXmlDeclaration decl("1.0", "UTF-8", "yes");
- xmlDoc.InsertEndChild(decl);
}
- }
-
- if ((current % 50) == 0 && progress)
- {
- progress->SetLine(1, CVariant{artist.strArtist});
- progress->SetPercentage(current * 100 / total);
- progress->Progress();
- if (progress->IsCanceled())
+ if ((current % 50) == 0 && progressDialog != NULL)
{
- progress->Close();
- m_pDS->close();
- return;
+ progressDialog->SetLine(1, CVariant{ artist.strArtist });
+ progressDialog->SetPercentage(current * 100 / total);
}
+ current++;
}
- current++;
}
- xmlDoc.SaveFile(xmlFile);
-
- CVariant data;
- if (singleFile)
+ if (settings.IsSingleFile())
{
- data["file"] = xmlFile;
- if (iFailCount > 0)
- data["failcount"] = iFailCount;
+ std::string xmlFile = URIUtils::AddFileToFolder(strFolder, "kodi_musicdb" + CDateTime::GetCurrentDateTime().GetAsDBDate() + ".xml");
+ if (!settings.m_overwrite && CFile::Exists(xmlFile))
+ xmlFile = URIUtils::AddFileToFolder(strFolder, "kodi_musicdb" + CDateTime::GetCurrentDateTime().GetAsSaveString() + ".xml");
+ xmlDoc.SaveFile(xmlFile);
+
+ CVariant data;
+ if (settings.IsSingleFile())
+ {
+ data["file"] = xmlFile;
+ if (iFailCount > 0)
+ data["failcount"] = iFailCount;
+ }
+ ANNOUNCEMENT::CAnnouncementManager::GetInstance().Announce(ANNOUNCEMENT::AudioLibrary, "xbmc", "OnExport", data);
}
- ANNOUNCEMENT::CAnnouncementManager::GetInstance().Announce(ANNOUNCEMENT::AudioLibrary, "xbmc", "OnExport", data);
}
catch (...)
{
- CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
+ CLog::Log(LOGERROR, "CMusicDatabase::%s failed", __FUNCTION__);
iFailCount++;
}
- if (progress)
- progress->Close();
+ if (progressDialog)
+ progressDialog->Close();
if (iFailCount > 0)
HELPERS::ShowOKDialogLines(CVariant{20196}, CVariant{StringUtils::Format(g_localizeStrings.Get(15011).c_str(), iFailCount)});
@@ -6483,7 +6856,9 @@ void CMusicDatabase::ImportFromXML(const std::string &xmlFile)
CArtist importedArtist;
importedArtist.Load(entry);
strTitle = importedArtist.strArtist;
- int idArtist = GetArtistByName(importedArtist.strArtist);
+
+ // Match by mbid first (that is definatively unique), then name (no mbid), finally by just name
+ int idArtist = GetArtistByMatch(importedArtist);
if (idArtist > -1)
{
CArtist artist;
@@ -6491,7 +6866,8 @@ void CMusicDatabase::ImportFromXML(const std::string &xmlFile)
artist.MergeScrapedArtist(importedArtist, true);
UpdateArtist(artist);
}
-
+ else
+ CLog::Log(LOGDEBUG, "%s - Not import additional artist data as %s not found", __FUNCTION__, importedArtist.strArtist.c_str());
current++;
}
else if (strnicmp(entry->Value(), "album", 5) == 0)
@@ -6499,7 +6875,8 @@ void CMusicDatabase::ImportFromXML(const std::string &xmlFile)
CAlbum importedAlbum;
importedAlbum.Load(entry);
strTitle = importedAlbum.strAlbum;
- int idAlbum = GetAlbumByName(importedAlbum.strAlbum, importedAlbum.GetAlbumArtistString());
+ // Match by mbid first (that is definatively unique), then title and artist desc (no mbid), finally by just name and artist
+ int idAlbum = GetAlbumByMatch(importedAlbum);
if (idAlbum > -1)
{
CAlbum album;
@@ -6507,6 +6884,8 @@ void CMusicDatabase::ImportFromXML(const std::string &xmlFile)
album.MergeScrapedAlbum(importedAlbum, true);
UpdateAlbum(album); //Will replace song artists if present in xml
}
+ else
+ CLog::Log(LOGDEBUG, "%s - Not import additional album data as %s not found", __FUNCTION__, importedAlbum.strAlbum.c_str());
current++;
}
diff --git a/xbmc/music/MusicDatabase.h b/xbmc/music/MusicDatabase.h
index c244f5d896..278ea8aaa4 100644
--- a/xbmc/music/MusicDatabase.h
+++ b/xbmc/music/MusicDatabase.h
@@ -29,6 +29,7 @@
#include "Album.h"
#include "dbwrappers/Database.h"
#include "MusicDbUrl.h"
+#include "settings/LibExportSettings.h"
#include "utils/SortUtils.h"
class CArtist;
@@ -245,7 +246,6 @@ public:
\return true if the album is retrieved, false otherwise.
*/
bool GetAlbum(int idAlbum, CAlbum& album, bool getSongs = true);
- int UpdateAlbum(int idAlbum, const CAlbum &album);
int UpdateAlbum(int idAlbum,
const std::string& strAlbum, const std::string& strMusicBrainzAlbumID,
const std::string& strReleaseGroupMBID,
@@ -278,6 +278,7 @@ public:
bool GetAlbumFromSong(int idSong, CAlbum &album);
int GetAlbumByName(const std::string& strAlbum, const std::string& strArtist="");
int GetAlbumByName(const std::string& strAlbum, const std::vector<std::string>& artist);
+ int GetAlbumByMatch(const CAlbum &album);
std::string GetAlbumById(int id);
bool SetAlbumUserrating(const int idAlbum, int userrating);
@@ -290,6 +291,7 @@ public:
int AddArtist(const std::string& strArtist, const std::string& strMusicBrainzArtistID, bool bScrapedMBID = false);
bool GetArtist(int idArtist, CArtist& artist, bool fetchAll = true);
bool GetArtistExists(int idArtist);
+ int GetLastArtist();
int UpdateArtist(int idArtist,
const std::string& strArtist, const std::string& strSortName,
const std::string& strMusicBrainzArtistID, bool bScrapedMBID,
@@ -309,6 +311,7 @@ public:
std::string GetArtistById(int id);
int GetArtistByName(const std::string& strArtist);
+ int GetArtistByMatch(const CArtist& artist);
std::string GetRoleById(int id);
/*! \brief Propagate artist sort name into the concatenated artist sort name strings
@@ -325,8 +328,12 @@ public:
bool GetPaths(std::set<std::string> &paths);
bool SetPathHash(const std::string &path, const std::string &hash);
bool GetPathHash(const std::string &path, std::string &hash);
- bool GetAlbumPath(int idAlbum, std::string &path);
- bool GetArtistPath(int idArtist, std::string &path);
+ bool GetAlbumPath(int idAlbum, std::string &basePath);
+ bool GetOldArtistPath(int idArtist, std::string &path);
+ bool GetArtistPath(const CArtist& artist, std::string &path);
+ bool GetAlbumFolder(const CAlbum& album, const std::string &strAlbumPath, std::string &strFolder);
+ bool GetArtistFolderName(const CArtist& artist, std::string &strFolder);
+ bool GetArtistFolderName(const std::string &strArtist, const std::string &strMusicBrainzArtistID, std::string &strFolder);
/////////////////////////////////////////////////
// Genres
@@ -445,7 +452,7 @@ public:
/////////////////////////////////////////////////
// XML
/////////////////////////////////////////////////
- void ExportToXML(const std::string &xmlFile, bool singleFile = false, bool images=false, bool overwrite=false);
+ void ExportToXML(const CLibExportSettings& settings, CGUIDialogProgress* progressDialog = NULL);
void ImportFromXML(const std::string &xmlFile);
/////////////////////////////////////////////////
diff --git a/xbmc/music/MusicLibraryQueue.cpp b/xbmc/music/MusicLibraryQueue.cpp
new file mode 100644
index 0000000000..7b5030bcf6
--- /dev/null
+++ b/xbmc/music/MusicLibraryQueue.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2017 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "MusicLibraryQueue.h"
+
+#include <utility>
+
+#include "dialogs/GUIDialogProgress.h"
+#include "guilib/GUIWindowManager.h"
+#include "GUIUserMessages.h"
+#include "music/jobs/MusicLibraryExportJob.h"
+#include "music/jobs/MusicLibraryJob.h"
+#include "threads/SingleLock.h"
+#include "Util.h"
+#include "utils/Variant.h"
+
+CMusicLibraryQueue::CMusicLibraryQueue()
+ : CJobQueue(false, 1, CJob::PRIORITY_LOW),
+ m_jobs(),
+ m_modal(false),
+ m_exporting(false)
+{ }
+
+CMusicLibraryQueue::~CMusicLibraryQueue()
+{
+ CSingleLock lock(m_critical);
+ m_jobs.clear();
+}
+
+CMusicLibraryQueue& CMusicLibraryQueue::GetInstance()
+{
+ static CMusicLibraryQueue s_instance;
+ return s_instance;
+}
+
+void CMusicLibraryQueue::ExportLibrary(const CLibExportSettings& settings, bool showDialog /* = false */)
+{
+ CGUIDialogProgress* progress = NULL;
+ if (showDialog)
+ {
+ progress = g_windowManager.GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS);
+ if (progress)
+ {
+ progress->SetHeading(CVariant{ 20196 }); //"Export music library"
+ progress->SetText(CVariant{ 650 }); //"Exporting"
+ progress->SetPercentage(0);
+ progress->Open();
+ progress->ShowProgressBar(true);
+ }
+ }
+
+ CMusicLibraryExportJob* exportJob = new CMusicLibraryExportJob(settings, progress);
+ if (showDialog)
+ {
+ AddJob(exportJob);
+
+ if (progress)
+ {
+ // Render and wait for export to complete or be cancelled
+ while (progress->IsActive() && !progress->IsCanceled())
+ progress->Progress();
+ // Finally close progress dialog
+ if (progress->IsActive())
+ progress->Close();
+ }
+ }
+ else
+ {
+ m_modal = true;
+ exportJob->DoWork();
+
+ delete exportJob;
+ m_modal = false;
+ Refresh();
+ }
+}
+
+void CMusicLibraryQueue::AddJob(CMusicLibraryJob *job)
+{
+ if (job == NULL)
+ return;
+
+ CSingleLock lock(m_critical);
+ if (!CJobQueue::AddJob(job))
+ return;
+
+ // add the job to our list of queued/running jobs
+ std::string jobType = job->GetType();
+ MusicLibraryJobMap::iterator jobsIt = m_jobs.find(jobType);
+ if (jobsIt == m_jobs.end())
+ {
+ MusicLibraryJobs jobs;
+ jobs.insert(job);
+ m_jobs.insert(std::make_pair(jobType, jobs));
+ }
+ else
+ jobsIt->second.insert(job);
+}
+
+void CMusicLibraryQueue::CancelJob(CMusicLibraryJob *job)
+{
+ if (job == NULL)
+ return;
+
+ CSingleLock lock(m_critical);
+ // remember the job type needed later because the job might be deleted
+ // in the call to CJobQueue::CancelJob()
+ std::string jobType;
+ if (job->GetType() != NULL)
+ jobType = job->GetType();
+
+ // check if the job supports cancellation and cancel it
+ if (job->CanBeCancelled())
+ job->Cancel();
+
+ // remove the job from the job queue
+ CJobQueue::CancelJob(job);
+
+ // remove the job from our list of queued/running jobs
+ MusicLibraryJobMap::iterator jobsIt = m_jobs.find(jobType);
+ if (jobsIt != m_jobs.end())
+ jobsIt->second.erase(job);
+}
+
+void CMusicLibraryQueue::CancelAllJobs()
+{
+ CSingleLock lock(m_critical);
+ CJobQueue::CancelJobs();
+
+ // remove all scanning jobs
+ m_jobs.clear();
+}
+
+bool CMusicLibraryQueue::IsRunning() const
+{
+ return CJobQueue::IsProcessing() || m_modal;
+}
+
+void CMusicLibraryQueue::Refresh()
+{
+ CUtil::DeleteMusicDatabaseDirectoryCache();
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE);
+ g_windowManager.SendThreadMessage(msg);
+}
+
+void CMusicLibraryQueue::OnJobComplete(unsigned int jobID, bool success, CJob *job)
+{
+ if (success)
+ {
+ if (QueueEmpty())
+ Refresh();
+ }
+
+ {
+ CSingleLock lock(m_critical);
+ // remove the job from our list of queued/running jobs
+ MusicLibraryJobMap::iterator jobsIt = m_jobs.find(job->GetType());
+ if (jobsIt != m_jobs.end())
+ jobsIt->second.erase(static_cast<CMusicLibraryJob*>(job));
+ }
+
+ return CJobQueue::OnJobComplete(jobID, success, job);
+}
diff --git a/xbmc/music/MusicLibraryQueue.h b/xbmc/music/MusicLibraryQueue.h
new file mode 100644
index 0000000000..27ecf7f3d2
--- /dev/null
+++ b/xbmc/music/MusicLibraryQueue.h
@@ -0,0 +1,100 @@
+#pragma once
+/*
+ * Copyright (C) 2017 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <map>
+#include <set>
+
+#include "FileItem.h"
+#include "settings/LibExportSettings.h"
+#include "threads/CriticalSection.h"
+#include "utils/JobManager.h"
+
+class CGUIDialogProgressBarHandle;
+class CMusicLibraryJob;
+class CGUIDialogProgress;
+
+/*!
+ \brief Queue for music library jobs.
+
+ The queue can only process a single job at any time and every job will be
+ executed at the lowest priority.
+ */
+class CMusicLibraryQueue : protected CJobQueue
+{
+public:
+ ~CMusicLibraryQueue() override;
+
+ /*!
+ \brief Gets the singleton instance of the music library queue.
+ */
+ static CMusicLibraryQueue& GetInstance();
+
+ /*!
+ \brief Enqueue a music library export job.
+ \param[in] settings Library export settings
+ \param[in] showDialog Show a progress dialog while (asynchronously) exporting, otherwise export in synchronous
+ */
+ void ExportLibrary(const CLibExportSettings& settings, bool showDialog = false);
+
+ /*!
+ \brief Adds the given job to the queue.
+ \param[in] job Music library job to be queued.
+ */
+ void AddJob(CMusicLibraryJob *job);
+
+ /*!
+ \brief Cancels the given job and removes it from the queue.
+ \param[in] job Music library job to be canceled and removed from the queue.
+ */
+ void CancelJob(CMusicLibraryJob *job);
+
+ /*!
+ \brief Cancels all running and queued jobs.
+ */
+ void CancelAllJobs();
+
+ /*!
+ \brief Whether any jobs are running or not.
+ */
+ bool IsRunning() const;
+
+protected:
+ // implementation of IJobCallback
+ void OnJobComplete(unsigned int jobID, bool success, CJob *job) override;
+
+ /*!
+ \brief Notifies all to refresh the current listings.
+ */
+ void Refresh();
+
+private:
+ CMusicLibraryQueue();
+ CMusicLibraryQueue(const CMusicLibraryQueue&);
+ CMusicLibraryQueue const& operator=(CMusicLibraryQueue const&);
+
+ typedef std::set<CMusicLibraryJob*> MusicLibraryJobs;
+ typedef std::map<std::string, MusicLibraryJobs> MusicLibraryJobMap;
+ MusicLibraryJobMap m_jobs;
+ CCriticalSection m_critical;
+
+ bool m_modal;
+ bool m_exporting;
+};
diff --git a/xbmc/music/dialogs/GUIDialogMusicInfo.cpp b/xbmc/music/dialogs/GUIDialogMusicInfo.cpp
index a199ff962a..1ec592f4fd 100644
--- a/xbmc/music/dialogs/GUIDialogMusicInfo.cpp
+++ b/xbmc/music/dialogs/GUIDialogMusicInfo.cpp
@@ -380,17 +380,39 @@ void CGUIDialogMusicInfo::OnGetThumb()
// local thumb
std::string localThumb;
+ bool existsThumb = false;
if (m_bArtistInfo)
{
CMusicDatabase database;
database.Open();
- std::string strArtistPath;
- if (database.GetArtistPath(m_artist.idArtist,strArtistPath))
+ // First look for thumb in the artists folder, the primary location
+ std::string strArtistPath = m_artist.strPath;
+ // Get path when don't already have it.
+ bool artistpathfound = !strArtistPath.empty();
+ if (!artistpathfound)
+ artistpathfound = database.GetArtistPath(m_artist, strArtistPath);
+ if (artistpathfound)
+ {
localThumb = URIUtils::AddFileToFolder(strArtistPath, "folder.jpg");
+ existsThumb = CFile::Exists(localThumb);
+ }
+ // If not there fall back local to music files (historic location for those album artists with a unique folder)
+ if (!existsThumb)
+ {
+ artistpathfound = database.GetOldArtistPath(m_artist.idArtist, strArtistPath);
+ if (artistpathfound)
+ {
+ localThumb = URIUtils::AddFileToFolder(strArtistPath, "folder.jpg");
+ existsThumb = CFile::Exists(localThumb);
+ }
+ }
}
else
+ {
localThumb = m_albumItem->GetUserMusicThumb();
- if (CFile::Exists(localThumb))
+ existsThumb = CFile::Exists(localThumb);
+ }
+ if (existsThumb)
{
CFileItemPtr item(new CFileItem("thumb://Local", false));
item->SetArt("thumb", localThumb);
@@ -478,13 +500,32 @@ void CGUIDialogMusicInfo::OnGetFanart()
items.Add(item);
}
- // Grab a local thumb
+ // Grab a local fanart
+ std::string strLocal;
CMusicDatabase database;
database.Open();
- std::string strArtistPath;
- database.GetArtistPath(m_artist.idArtist,strArtistPath);
- CFileItem item(strArtistPath,true);
- std::string strLocal = item.GetLocalFanart();
+ // First look for fanart in the artists folder, the primary location
+ std::string strArtistPath = m_artist.strPath;
+ // Get path when don't already have it.
+ bool artistpathfound = !strArtistPath.empty();
+ if (!artistpathfound)
+ artistpathfound = database.GetArtistPath(m_artist, strArtistPath);
+ if (artistpathfound)
+ {
+ CFileItem item(strArtistPath, true);
+ strLocal = item.GetLocalFanart();
+ }
+ // If not there fall back local to music files (historic location for those album artists with a unique folder)
+ if (strLocal.empty())
+ {
+ artistpathfound = database.GetOldArtistPath(m_artist.idArtist, strArtistPath);
+ if (artistpathfound)
+ {
+ CFileItem item(strArtistPath, true);
+ strLocal = item.GetLocalFanart();
+ }
+ }
+
if (!strLocal.empty())
{
CFileItemPtr itemLocal(new CFileItem("fanart://Local",false));
diff --git a/xbmc/music/infoscanner/MusicInfoScanner.cpp b/xbmc/music/infoscanner/MusicInfoScanner.cpp
index 82d971a83d..7ac2f511b7 100644
--- a/xbmc/music/infoscanner/MusicInfoScanner.cpp
+++ b/xbmc/music/infoscanner/MusicInfoScanner.cpp
@@ -153,9 +153,15 @@ void CMusicInfoScanner::Process()
bool scancomplete = DoScan(*it);
if (scancomplete)
- {// Finally download additional album and artist information for the recently added albums
- if ((m_flags & SCAN_ONLINE) && m_albumsAdded.size() > 0)
- ScrapeInfoAddedAlbums();
+ {
+ if (m_albumsAdded.size() > 0)
+ {
+ if (m_flags & SCAN_ONLINE)
+ // Download additional album and artist information for the recently added albums.
+ // This also identifies any local artist thumb and fanart if it exitsts, and gives it priority,
+ // otherwise it is set to the first available from the remote thumbs and fanart that was scraped.
+ ScrapeInfoAddedAlbums();
+ }
}
else
{
@@ -236,7 +242,7 @@ void CMusicInfoScanner::Process()
CArtist artist;
m_musicDatabase.GetArtist(params.GetArtistId(), artist);
- m_musicDatabase.GetArtistPath(params.GetArtistId(), artist.strPath);
+ m_musicDatabase.GetArtistPath(artist, artist.strPath);
if (m_handle)
{
@@ -287,6 +293,7 @@ void CMusicInfoScanner::Start(const std::string& strDirectory, int flags)
m_pathsToScan.clear();
m_seenPaths.clear();
m_albumsAdded.clear();
+ m_artistsArt.clear();
m_flags = flags;
if (strDirectory.empty())
@@ -809,6 +816,22 @@ void CMusicInfoScanner::FileItemsToAlbums(CFileItemList& items, VECALBUMS& album
}
}
+INFO_RET MUSIC_INFO::CMusicInfoScanner::UpdateAlbumInfo(CAlbum& album, const ADDON::ScraperPtr& scraper, bool bAllowSelection, CGUIDialogProgress* pDialog)
+{
+ m_musicDatabase.Open();
+ INFO_RET result = UpdateDatabaseAlbumInfo(album, scraper, bAllowSelection, pDialog);
+ m_musicDatabase.Close();
+ return result;
+}
+
+INFO_RET MUSIC_INFO::CMusicInfoScanner::UpdateArtistInfo(CArtist& artist, const ADDON::ScraperPtr& scraper, bool bAllowSelection, CGUIDialogProgress* pDialog)
+{
+ m_musicDatabase.Open();
+ INFO_RET result = UpdateDatabaseArtistInfo(artist, scraper, bAllowSelection, pDialog);
+ m_musicDatabase.Close();
+ return result;
+}
+
int CMusicInfoScanner::RetrieveMusicInfo(const std::string& strDirectory, CFileItemList& items)
{
MAPSONGS songsMap;
@@ -839,7 +862,7 @@ int CMusicInfoScanner::RetrieveMusicInfo(const std::string& strDirectory, CFileI
int numAdded = 0;
- // Add all albums to the library
+ // Add all albums to the library, and hence any new song or album artists or other contributors
for (VECALBUMS::iterator album = albums.begin(); album != albums.end(); ++album)
{
if (m_bStop)
@@ -853,22 +876,36 @@ int CMusicInfoScanner::RetrieveMusicInfo(const std::string& strDirectory, CFileI
m_musicDatabase.AddAlbum(*album);
m_albumsAdded.emplace_back(album->idAlbum);
- // Yuk - this is a kludgy way to do what we want to do, but it will work to sort
- // out artist fanart until we can restructure the artist fanart to work more
- // like the album fanart. This has to be done after we've added the album so
- // we have the artist IDs to update, but before we call UpdateDatabaseArtistInfo.
- if (albums.size() == 1 &&
- !album->artistCredits.empty() &&
- !StringUtils::EqualsNoCase(album->artistCredits[0].GetArtist(), "various artists") &&
- !StringUtils::EqualsNoCase(album->artistCredits[0].GetArtist(), "various"))
+ /*
+ Make the first attempt (during scanning) to get local album artist art looking for thumbs and
+ fanart in the folder immediately above the album folder. This is for backwards compatibility.
+ It can only do this if the folder being processed contains only one album, and can only do so for
+ the first album artist if the album is a collaboration e.g. composer, conductor, orchestra, or by
+ several pop artists in their own right.
+ It avoids repeatedly processing the same artist by maintaining a set. Adding the album may have added
+ new artists, or provide art for an existing (song) artist, but does not replace any artwork already set.
+ Hence once art has been found for an album artist, art is not searched for in other folders.
+
+ It will find art for "various artists", if artwork is located above the folder containing compilatons.
+ */
+ if (albums.size() == 1 && !album->artistCredits.empty())
{
- CArtist artist;
- if (m_musicDatabase.GetArtist(album->artistCredits[0].GetArtistId(), artist))
+ if (m_artistsArt.find(album->artistCredits[0].GetArtistId()) == m_artistsArt.end())
{
- artist.strPath = URIUtils::GetParentPath(strDirectory);
- m_musicDatabase.SetArtForItem(artist.idArtist, MediaTypeArtist, GetArtistArtwork(artist));
+ m_artistsArt.insert(album->artistCredits[0].GetArtistId()); // Artist processed
+ std::map<std::string, std::string> art;
+ if (!m_musicDatabase.GetArtForItem(album->artistCredits[0].GetArtistId(), MediaTypeArtist, art))
+ {
+ // Artist does not already have art, so try to find some.
+ // Do not have URL of other available art before scraping, so only ID and path needed
+ CArtist artist;
+ artist.idArtist = album->artistCredits[0].GetArtistId();
+ artist.strPath = URIUtils::GetParentPath(album->strPath);
+ m_musicDatabase.SetArtForItem(album->artistCredits[0].GetArtistId(), MediaTypeArtist, GetArtistArtwork(artist, 1));
+ }
}
}
+
numAdded += album->songs.size();
}
@@ -978,6 +1015,78 @@ void MUSIC_INFO::CMusicInfoScanner::ScrapeInfoAddedAlbums()
}
}
+void MUSIC_INFO::CMusicInfoScanner::RetrieveArtistArt()
+{
+ bool albumartistsonly = !CServiceBroker::GetSettings().GetBool(CSettings::SETTING_MUSICLIBRARY_SHOWCOMPILATIONARTISTS);
+ std::set<int> artists;
+ for (auto i = 0u; i < m_albumsAdded.size(); ++i)
+ {
+ if (m_bStop)
+ break;
+ int albumId = m_albumsAdded[i];
+ CAlbum album;
+ // Fetch album artist(s) ids
+ m_musicDatabase.GetAlbum(albumId, album, false);
+ if (m_handle)
+ {
+ float percentage = static_cast<float>(i * 100) / static_cast<float>(m_albumsAdded.size());
+ m_handle->SetText(album.GetAlbumArtistString() + " - " + album.strAlbum);
+ m_handle->SetPercentage(percentage);
+ }
+
+ // Set art for album artists that have not been processed before, avoiding repeating
+ // unsuccessful attempts for every album by that artist.
+ for (const auto &artistCredit : album.artistCredits)
+ {
+ if (m_bStop)
+ break;
+ if (artists.find(artistCredit.GetArtistId()) == artists.end())
+ {
+ artists.insert(artistCredit.GetArtistId()); // Artist processed
+ std::map<std::string, std::string> art;
+ if (!m_musicDatabase.GetArtForItem(artistCredit.GetArtistId(), MediaTypeArtist, art))
+ {
+ CArtist artist;
+ //Get artist details including available art returned by scraping
+ m_musicDatabase.GetArtist(artistCredit.GetArtistId(), artist);
+ // Get path for artist in the artists folder (not done in GetArtist)
+ m_musicDatabase.GetArtistPath(artist, artist.strPath);
+ m_musicDatabase.SetArtForItem(artist.idArtist, MediaTypeArtist, GetArtistArtwork(artist, 1));
+ }
+ }
+ }
+ // Only fetch song artist art if they are being displayed in artists node by default
+ if (!albumartistsonly)
+ {
+ for (auto &song : album.songs)
+ {
+ if (m_bStop)
+ break;
+ for (const auto &artistCredit : song.artistCredits)
+ {
+ if (m_bStop)
+ break;
+
+ std::map<std::string, std::string> art;
+ if (artists.find(artistCredit.GetArtistId()) == artists.end())
+ {
+ artists.insert(artistCredit.GetArtistId()); // Artist processed
+ if (!m_musicDatabase.GetArtForItem(artistCredit.GetArtistId(), MediaTypeArtist, art))
+ {
+ CArtist artist;
+ //Get artist details including available art returned by scraping
+ m_musicDatabase.GetArtist(artistCredit.GetArtistId(), artist);
+ m_musicDatabase.GetArtistPath(artist, artist.strPath);
+ m_musicDatabase.SetArtForItem(artist.idArtist, MediaTypeArtist, GetArtistArtwork(artist, 1));
+ }
+ }
+ }
+ }
+ }
+
+ }
+}
+
void CMusicInfoScanner::FindArtForAlbums(VECALBUMS &albums, const std::string &path)
{
/*
@@ -1155,8 +1264,12 @@ loop:
artist.MergeScrapedArtist(artistInfo.GetArtist(), CServiceBroker::GetSettings().GetBool(CSettings::SETTING_MUSICLIBRARY_OVERRIDETAGS));
m_musicDatabase.Open();
m_musicDatabase.UpdateArtist(artist);
- m_musicDatabase.GetArtistPath(artist.idArtist, artist.strPath);
- m_musicDatabase.SetArtForItem(artist.idArtist, MediaTypeArtist, GetArtistArtwork(artist));
+ // If artist art has not been set from <art> tag then look in path or use first available from scraped list
+ if (artist.art.empty())
+ {
+ m_musicDatabase.GetArtistPath(artist, artist.strPath);
+ m_musicDatabase.SetArtForItem(artist.idArtist, MediaTypeArtist, GetArtistArtwork(artist, 1));
+ }
m_musicDatabase.Close();
artistInfo.SetLoaded();
}
@@ -1442,17 +1555,40 @@ INFO_RET CMusicInfoScanner::DownloadArtistInfo(const CArtist& artist, const ADDO
}
}
- // handle nfo files
- std::string path = artist.strPath;
- if (path.empty())
- m_musicDatabase.GetArtistPath(artist.idArtist, path);
-
- std::string strNfo = URIUtils::AddFileToFolder(path, "artist.nfo");
- CNfoFile::NFOResult result=CNfoFile::NO_NFO;
+ // Handle nfo files
+ CNfoFile::NFOResult result = CNfoFile::NO_NFO;
CNfoFile nfoReader;
- if (XFILE::CFile::Exists(strNfo))
+ std::string strNfo;
+ std::string path;
+ bool existsNFO = false;
+ // First look for nfo in the artists folder, the primary location
+ path = artist.strPath;
+ // Get path when don't already have it.
+ bool artistpathfound = !path.empty();
+ if (!artistpathfound)
+ artistpathfound = m_musicDatabase.GetArtistPath(artist, path);
+ if (artistpathfound)
{
- CLog::Log(LOGDEBUG,"Found matching nfo file: %s", CURL::GetRedacted(strNfo).c_str());
+ strNfo = URIUtils::AddFileToFolder(path, "artist.nfo");
+ existsNFO = XFILE::CFile::Exists(strNfo);
+ }
+
+ // If not there fall back local to music files (historic location for those album artists with a unique folder)
+ if (!existsNFO)
+ {
+ artistpathfound = m_musicDatabase.GetOldArtistPath(artist.idArtist, path);
+ if (artistpathfound)
+ {
+ strNfo = URIUtils::AddFileToFolder(path, "artist.nfo");
+ existsNFO = XFILE::CFile::Exists(strNfo);
+ }
+ else
+ CLog::Log(LOGDEBUG, "%s not have path, nfo file not possible", artist.strArtist.c_str());
+ }
+
+ if (existsNFO)
+ {
+ CLog::Log(LOGDEBUG, "Found matching nfo file: %s", CURL::GetRedacted(strNfo).c_str());
result = nfoReader.Create(strNfo, info);
if (result == CNfoFile::FULL_NFO)
{
@@ -1463,15 +1599,15 @@ INFO_RET CMusicInfoScanner::DownloadArtistInfo(const CArtist& artist, const ADDO
else if (result == CNfoFile::URL_NFO || result == CNfoFile::COMBINED_NFO)
{
CScraperUrl scrUrl(nfoReader.ScraperUrl());
- CMusicArtistInfo artistNfo("nfo",scrUrl);
+ CMusicArtistInfo artistNfo("nfo", scrUrl);
ADDON::ScraperPtr nfoReaderScraper = nfoReader.GetScraperInfo();
- CLog::Log(LOGDEBUG,"-- nfo-scraper: %s",nfoReaderScraper->Name().c_str());
- CLog::Log(LOGDEBUG,"-- nfo url: %s", scrUrl.m_url[0].m_url.c_str());
+ CLog::Log(LOGDEBUG, "-- nfo-scraper: %s", nfoReaderScraper->Name().c_str());
+ CLog::Log(LOGDEBUG, "-- nfo url: %s", scrUrl.m_url[0].m_url.c_str());
scraper.SetScraperInfo(nfoReaderScraper);
scraper.GetArtists().push_back(artistNfo);
}
else
- CLog::Log(LOGERROR,"Unable to find an url in nfo file: %s", strNfo.c_str());
+ CLog::Log(LOGERROR, "Unable to find an url in nfo file: %s", strNfo.c_str());
}
if (!scraper.GetArtistCount())
@@ -1618,17 +1754,19 @@ bool CMusicInfoScanner::ResolveMusicBrainz(const std::string &strMusicBrainzID,
return bMusicBrainz;
}
-std::map<std::string, std::string> CMusicInfoScanner::GetArtistArtwork(const CArtist& artist)
+std::map<std::string, std::string> CMusicInfoScanner::GetArtistArtwork(const CArtist& artist, unsigned int level /* = 3*/)
{
std::map<std::string, std::string> artwork;
+ std::string strFolder;
+ if (level > 3)
+ level = 3; //Don't go up more than 2 levels of folders
// check thumb
- std::string strFolder;
std::string thumb;
if (!artist.strPath.empty())
{
strFolder = artist.strPath;
- for (int i = 0; i < 3 && thumb.empty(); ++i)
+ for (unsigned int i = 0; i < level && thumb.empty(); ++i)
{
CFileItem item(strFolder, true);
thumb = item.GetUserMusicThumb(true);
@@ -1648,7 +1786,7 @@ std::map<std::string, std::string> CMusicInfoScanner::GetArtistArtwork(const CAr
if (!artist.strPath.empty())
{
strFolder = artist.strPath;
- for (int i = 0; i < 3 && fanart.empty(); ++i)
+ for (unsigned int i = 0; i < level && fanart.empty(); ++i)
{
CFileItem item(strFolder, true);
fanart = item.GetLocalFanart();
diff --git a/xbmc/music/infoscanner/MusicInfoScanner.h b/xbmc/music/infoscanner/MusicInfoScanner.h
index 6948f1bb1d..83ba5c7b61 100644
--- a/xbmc/music/infoscanner/MusicInfoScanner.h
+++ b/xbmc/music/infoscanner/MusicInfoScanner.h
@@ -77,25 +77,29 @@ public:
*/
static void FileItemsToAlbums(CFileItemList& items, VECALBUMS& albums, MAPSONGS* songsMap = NULL);
- /*! \brief Fixup albums and songs
-
- If albumartist is not available in a song, we determine it from the
- common portion of each song's artist list.
-
- eg the common artist for
- Bob Dylan / Tom Petty / Roy Orbison
- Bob Dylan / Tom Petty
- would be "Bob Dylan / Tom Petty".
-
- If all songs that share an album
- 1. have a non-empty album name
- 2. have at least two different primary artists
- 3. have no album artist set
- 4. and no track numbers overlap
- we assume it is a various artists album, and set the albumartist field accordingly.
-
- */
- static void FixupAlbums(VECALBUMS &albums);
+ /*! \brief Scrape additional album information and update the music database with it.
+ Given an album, search for it using the given scraper.
+ If info is found, update the database and artwork with the new
+ information.
+ \param album [in/out] the album to update
+ \param scraper [in] the album scraper to use
+ \param bAllowSelection [in] should we allow the user to manually override the info with a GUI if the album is not found?
+ \param pDialog [in] a progress dialog which this and downstream functions can update with status, if required
+ */
+ INFO_RET UpdateAlbumInfo(CAlbum& album, const ADDON::ScraperPtr& scraper, bool bAllowSelection, CGUIDialogProgress* pDialog = NULL);
+
+ /*! \brief Scrape additional artist information and update the music database with it.
+ Given an artist, search for it using the given scraper.
+ If info is found, update the database and artwork with the new
+ information.
+ \param artist [in/out] the artist to update
+ \param scraper [in] the artist scraper to use
+ \param bAllowSelection [in] should we allow the user to manually override the info with a GUI if the album is not found?
+ \param pDialog [in] a progress dialog which this and downstream functions can update with status, if required
+ */
+ INFO_RET UpdateArtistInfo(CArtist& artist, const ADDON::ScraperPtr& scraper, bool bAllowSelection, CGUIDialogProgress* pDialog = NULL);
+
+protected:
/*! \brief Find art for albums
Based on the albums in the folder, finds whether we have unique album art
@@ -114,8 +118,8 @@ public:
*/
static void FindArtForAlbums(VECALBUMS &albums, const std::string &path);
- /*! \brief Update the database information for a MusicDB album
- Given an album, search and update its info with the given scraper.
+ /*! \brief Scrape additional album information and update the database.
+ Search for the given album using the given scraper.
If info is found, update the database and artwork with the new
information.
\param album [in/out] the album to update
@@ -125,8 +129,8 @@ public:
*/
INFO_RET UpdateDatabaseAlbumInfo(CAlbum& album, const ADDON::ScraperPtr& scraper, bool bAllowSelection, CGUIDialogProgress* pDialog = NULL);
- /*! \brief Update the database information for a MusicDB artist
- Given an artist, search and update its info with the given scraper.
+ /*! \brief Scrape additional artist information and update the database.
+ Search for the given artist using the given scraper.
If info is found, update the database and artwork with the new
information.
\param artist [in/out] the artist to update
@@ -162,13 +166,16 @@ public:
*/
INFO_RET DownloadArtistInfo(const CArtist& artist, const ADDON::ScraperPtr& scraper, MUSIC_GRABBER::CMusicArtistInfo& artistInfo, bool bUseScrapedMBID, CGUIDialogProgress* pDialog = NULL);
- /*! \brief Search for art for an artist
- Look for art for an artist. Checks the artist structure for thumbs, and checks
- the artist path (if non-empty) for artist/folder tbns, etc.
+ /*! \brief Get art for an artist
+ Checks for thumb and fanart in given folder, and in parent folders back up the artist path (if non-empty).
+ If none is found there then it tries to use the first available thumb and fanart from those listed in the
+ artist structure. Images found are cached.
\param artist [in] an artist
+ \param level [in] how many levels of folders to search in. 1 => just the folder
+ \return set of art type and file location (URL or path) pairs
*/
- std::map<std::string, std::string> GetArtistArtwork(const CArtist& artist);
-protected:
+ std::map<std::string, std::string> GetArtistArtwork(const CArtist& artist, unsigned int level = 3);
+
void Process() override;
/*! \brief Scan in the ID3/Ogg/FLAC tags for a bunch of FileItems
@@ -182,6 +189,7 @@ protected:
int RetrieveMusicInfo(const std::string& strDirectory, CFileItemList& items);
void ScrapeInfoAddedAlbums();
+ void RetrieveArtistArt();
/*! \brief Scan in the ID3/Ogg/FLAC tags for a bunch of FileItems
Given a list of FileItems, scan in the tags for those FileItems
@@ -222,6 +230,7 @@ protected:
CMusicDatabase m_musicDatabase;
std::vector<int> m_albumsAdded;
+ std::set<int> m_artistsArt;
std::set<std::string> m_pathsToScan;
std::set<std::string> m_seenPaths;
diff --git a/xbmc/music/jobs/CMakeLists.txt b/xbmc/music/jobs/CMakeLists.txt
new file mode 100644
index 0000000000..8b317a95bd
--- /dev/null
+++ b/xbmc/music/jobs/CMakeLists.txt
@@ -0,0 +1,9 @@
+set(SOURCES MusicLibraryJob.cpp
+ MusicLibraryProgressJob.cpp
+ MusicLibraryExportJob.cpp)
+
+set(HEADERS MusicLibraryJob.h
+ MusicLibraryProgressJob.h
+ MusicLibraryExportJob.h)
+
+core_add_library(music_jobs)
diff --git a/xbmc/music/jobs/MusicLibraryExportJob.cpp b/xbmc/music/jobs/MusicLibraryExportJob.cpp
new file mode 100644
index 0000000000..f8d05fb46f
--- /dev/null
+++ b/xbmc/music/jobs/MusicLibraryExportJob.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "MusicLibraryExportJob.h"
+#include "dialogs/GUIDialogProgress.h"
+#include "music/MusicDatabase.h"
+#include "settings/LibExportSettings.h"
+
+CMusicLibraryExportJob::CMusicLibraryExportJob(const CLibExportSettings& settings, CGUIDialogProgress* progressDialog)
+ : CMusicLibraryProgressJob(NULL),
+ m_settings(settings)
+{
+ if (progressDialog)
+ SetProgressIndicators(NULL, progressDialog);
+}
+
+CMusicLibraryExportJob::~CMusicLibraryExportJob() = default;
+
+bool CMusicLibraryExportJob::operator==(const CJob* job) const
+{
+ if (strcmp(job->GetType(), GetType()) != 0)
+ return false;
+
+ const CMusicLibraryExportJob* exportJob = dynamic_cast<const CMusicLibraryExportJob*>(job);
+ if (exportJob == NULL)
+ return false;
+
+ return !(m_settings != exportJob->m_settings);
+}
+
+bool CMusicLibraryExportJob::Work(CMusicDatabase &db)
+{
+ db.ExportToXML(m_settings, GetProgressDialog());
+
+ return true;
+}
diff --git a/xbmc/music/jobs/MusicLibraryExportJob.h b/xbmc/music/jobs/MusicLibraryExportJob.h
new file mode 100644
index 0000000000..9b52d24712
--- /dev/null
+++ b/xbmc/music/jobs/MusicLibraryExportJob.h
@@ -0,0 +1,53 @@
+#pragma once
+/*
+ * Copyright (C) 2017 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "MusicLibraryProgressJob.h"
+#include "settings/LibExportSettings.h"
+
+class CGUIDialogProgress;
+
+/*!
+ \brief Music library job implementation for exporting the music library.
+*/
+class CMusicLibraryExportJob : public CMusicLibraryProgressJob
+{
+public:
+ /*!
+ \brief Creates a new music library export job for the given paths.
+
+ \param[in] settings Library export settings
+ \param[in] progressDialog Progress dialog to be used to display the export progress
+ */
+ CMusicLibraryExportJob(const CLibExportSettings& settings, CGUIDialogProgress* progressDialog);
+
+ ~CMusicLibraryExportJob() override;
+
+ // specialization of CJob
+ const char *GetType() const override { return "MusicLibraryExportJob"; }
+ bool operator==(const CJob* job) const override;
+
+protected:
+ // implementation of CMusicLibraryJob
+ bool Work(CMusicDatabase &db) override;
+
+private:
+ CLibExportSettings m_settings;
+};
diff --git a/xbmc/music/jobs/MusicLibraryJob.cpp b/xbmc/music/jobs/MusicLibraryJob.cpp
new file mode 100644
index 0000000000..9f940bfb34
--- /dev/null
+++ b/xbmc/music/jobs/MusicLibraryJob.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "MusicLibraryJob.h"
+#include "music/MusicDatabase.h"
+
+CMusicLibraryJob::CMusicLibraryJob() = default;
+
+CMusicLibraryJob::~CMusicLibraryJob() = default;
+
+bool CMusicLibraryJob::DoWork()
+{
+ CMusicDatabase db;
+ if (!db.Open())
+ return false;
+
+ return Work(db);
+}
diff --git a/xbmc/music/jobs/MusicLibraryJob.h b/xbmc/music/jobs/MusicLibraryJob.h
new file mode 100644
index 0000000000..d8aec1bc01
--- /dev/null
+++ b/xbmc/music/jobs/MusicLibraryJob.h
@@ -0,0 +1,61 @@
+#pragma once
+/*
+ * Copyright (C) 2017 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/Job.h"
+
+class CMusicDatabase;
+
+/*!
+ \brief Basic implementation/interface of a CJob which interacts with the
+ music database.
+ */
+class CMusicLibraryJob : public CJob
+{
+public:
+ ~CMusicLibraryJob() 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 "MusicLibraryJob"; }
+ bool operator==(const CJob* job) const override { return false; }
+
+protected:
+ CMusicLibraryJob();
+
+ /*!
+ \brief Worker method to be implemented by an actual implementation.
+
+ \param[in] db Already open music database to be used for interaction
+ \return True if the process succeeded, false otherwise
+ */
+ virtual bool Work(CMusicDatabase &db) = 0;
+};
diff --git a/xbmc/music/jobs/MusicLibraryProgressJob.cpp b/xbmc/music/jobs/MusicLibraryProgressJob.cpp
new file mode 100644
index 0000000000..64b0c6d982
--- /dev/null
+++ b/xbmc/music/jobs/MusicLibraryProgressJob.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "MusicLibraryProgressJob.h"
+
+CMusicLibraryProgressJob::CMusicLibraryProgressJob(CGUIDialogProgressBarHandle* progressBar)
+ : CProgressJob(progressBar)
+{ }
+
+CMusicLibraryProgressJob::~CMusicLibraryProgressJob() = default;
+
+bool CMusicLibraryProgressJob::DoWork()
+{
+ bool result = CMusicLibraryJob::DoWork();
+
+ MarkFinished();
+
+ return result;
+}
diff --git a/xbmc/music/jobs/MusicLibraryProgressJob.h b/xbmc/music/jobs/MusicLibraryProgressJob.h
new file mode 100644
index 0000000000..a8dd69ce33
--- /dev/null
+++ b/xbmc/music/jobs/MusicLibraryProgressJob.h
@@ -0,0 +1,40 @@
+#pragma once
+/*
+ * Copyright (C) 2017 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/ProgressJob.h"
+#include "music/jobs/MusicLibraryJob.h"
+
+/*!
+ \brief Combined base implementation of a music library job with a progress bar.
+ */
+class CMusicLibraryProgressJob : public CProgressJob, public CMusicLibraryJob
+{
+public:
+ ~CMusicLibraryProgressJob() override;
+
+ // implementation of CJob
+ bool DoWork() override;
+ const char *GetType() const override { return "CMusicLibraryProgressJob"; }
+ bool operator==(const CJob* job) const override { return false; }
+
+protected:
+ explicit CMusicLibraryProgressJob(CGUIDialogProgressBarHandle* progressBar);
+};
diff --git a/xbmc/music/windows/GUIWindowMusicBase.cpp b/xbmc/music/windows/GUIWindowMusicBase.cpp
index 3a96125106..bc3c0faea0 100644
--- a/xbmc/music/windows/GUIWindowMusicBase.cpp
+++ b/xbmc/music/windows/GUIWindowMusicBase.cpp
@@ -23,6 +23,7 @@
#include "GUIUserMessages.h"
#include "GUIWindowMusicBase.h"
#include "dialogs/GUIDialogMediaSource.h"
+#include "dialogs/GUIDialogFileBrowser.h"
#include "music/dialogs/GUIDialogMusicInfo.h"
#include "playlists/PlayListFactory.h"
#include "Util.h"
@@ -49,6 +50,7 @@
#include "dialogs/GUIDialogProgress.h"
#include "FileItem.h"
#include "filesystem/File.h"
+#include "messaging/helpers/DialogHelper.h"
#include "messaging/helpers/DialogOKHelper.h"
#include "profiles/ProfilesManager.h"
#include "storage/MediaManager.h"
@@ -79,6 +81,7 @@ using namespace PLAYLIST;
using namespace MUSIC_GRABBER;
using namespace MUSIC_INFO;
using namespace KODI::MESSAGING;
+using KODI::MESSAGING::HELPERS::DialogResponse;
#define CONTROL_BTNVIEWASICONS 2
#define CONTROL_BTNSORTBY 3
@@ -368,8 +371,20 @@ void CGUIWindowMusicBase::ShowArtistInfo(const CFileItem *pItem, bool bShowInfo
CArtist artist;
if (!m_musicdatabase.GetArtist(params.GetArtistId(), artist))
- return;
- m_musicdatabase.GetArtistPath(params.GetArtistId(), artist.strPath);
+ return;
+ // Get the *name* of the folder for this artist within the Artist Info folder (may not exist).
+ // If there is no Artist Info folder specififed in settings this will be blank
+ bool artistpathfound = m_musicdatabase.GetArtistPath(artist, artist.strPath);
+
+ // Set up path for *item folder when browsing for art, by default this is in the Artist Info Folder
+ std::string artistItemPath = artist.strPath;
+ if (!artistpathfound || !CDirectory::Exists(artist.strPath))
+ // Fall back local to music files (historic location for those album artists with a unique folder)
+ // although there may not be such a unique folder for the arist
+ if (!m_musicdatabase.GetOldArtistPath(artist.idArtist, artistItemPath))
+ // Fall back further to browse the Artist Info Folder itself
+ artistItemPath = CServiceBroker::GetSettings().GetString(CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER);
+
bool refresh = false;
while (1)
{
@@ -396,7 +411,7 @@ void CGUIWindowMusicBase::ShowArtistInfo(const CFileItem *pItem, bool bShowInfo
}
CMusicInfoScanner scanner;
- if (scanner.UpdateDatabaseArtistInfo(artist, scraper, bShowInfo, m_dlgProgress) != INFO_ADDED)
+ if (scanner.UpdateArtistInfo(artist, scraper, bShowInfo, m_dlgProgress) != INFO_ADDED)
{
HELPERS::ShowOKDialogText(CVariant{21889}, CVariant{20199});
break;
@@ -409,7 +424,7 @@ void CGUIWindowMusicBase::ShowArtistInfo(const CFileItem *pItem, bool bShowInfo
CGUIDialogMusicInfo *pDlgArtistInfo = g_windowManager.GetWindow<CGUIDialogMusicInfo>(WINDOW_DIALOG_MUSIC_INFO);
if (pDlgArtistInfo)
{
- pDlgArtistInfo->SetArtist(artist, artist.strPath);
+ pDlgArtistInfo->SetArtist(artist, artistItemPath);
pDlgArtistInfo->Open();
if (pDlgArtistInfo->NeedRefresh())
@@ -475,7 +490,7 @@ bool CGUIWindowMusicBase::ShowAlbumInfo(const CFileItem *pItem, bool bShowInfo /
}
CMusicInfoScanner scanner;
- if (scanner.UpdateDatabaseAlbumInfo(album, scraper, bShowInfo, m_dlgProgress) != INFO_ADDED)
+ if (scanner.UpdateAlbumInfo(album, scraper, bShowInfo, m_dlgProgress) != INFO_ADDED)
{
HELPERS::ShowOKDialogText(CVariant{185}, CVariant{500});
if (m_dlgProgress)
@@ -921,7 +936,7 @@ bool CGUIWindowMusicBase::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
return true;
case CONTEXT_BUTTON_SCAN:
- OnScan(itemNumber);
+ OnScan(itemNumber, true);
return true;
case CONTEXT_BUTTON_CDDB:
@@ -1325,8 +1340,6 @@ void CGUIWindowMusicBase::OnInitWindow()
if (CServiceBroker::GetSettings().GetBool(CSettings::SETTING_MUSICLIBRARY_DOWNLOADINFO))
if (CGUIDialogYesNo::ShowAndGetInput(CVariant{799}, CVariant{38061}))
flags |= CMusicInfoScanner::SCAN_ONLINE;
- if (CServiceBroker::GetSettings().GetBool(CSettings::SETTING_MUSICLIBRARY_BACKGROUNDUPDATE))
- flags |= CMusicInfoScanner::SCAN_BACKGROUND;
g_application.StartMusicScan("", true, flags);
m_musicdatabase.SetMusicTagScanVersion(); // once is enough (user may interrupt, but that's up to them)
}
@@ -1349,7 +1362,7 @@ std::string CGUIWindowMusicBase::GetStartFolder(const std::string &dir)
return CGUIMediaWindow::GetStartFolder(dir);
}
-void CGUIWindowMusicBase::OnScan(int iItem)
+void CGUIWindowMusicBase::OnScan(int iItem, bool bPromptRescan /*= false*/)
{
std::string strPath;
if (iItem < 0 || iItem >= m_vecItems->Size())
@@ -1361,10 +1374,15 @@ void CGUIWindowMusicBase::OnScan(int iItem)
//! This will require changes to the info scanner, which assumes we're running on a folder
strPath = m_vecItems->GetPath();
}
- DoScan(strPath);
+ // Ask for full rescan of music files when scan item from file view context menu
+ bool doRescan = false;
+ if (bPromptRescan)
+ doRescan = CGUIDialogYesNo::ShowAndGetInput(CVariant{ 799 }, CVariant{ 38062 });
+
+ DoScan(strPath, doRescan);
}
-void CGUIWindowMusicBase::DoScan(const std::string &strPath)
+void CGUIWindowMusicBase::DoScan(const std::string &strPath, bool bRescan /*= false*/)
{
if (g_application.IsMusicScanning())
{
@@ -1374,7 +1392,12 @@ void CGUIWindowMusicBase::DoScan(const std::string &strPath)
// Start background loader
int iControl=GetFocusedControlID();
- g_application.StartMusicScan(strPath);
+ int flags = 0;
+ if (bRescan)
+ flags = CMusicInfoScanner::SCAN_RESCAN;
+ if (CServiceBroker::GetSettings().GetBool(CSettings::SETTING_MUSICLIBRARY_DOWNLOADINFO))
+ flags |= CMusicInfoScanner::SCAN_ONLINE;
+ g_application.StartMusicScan(strPath, true, flags);
SET_CONTROL_FOCUS(iControl, 0);
UpdateButtons();
}
@@ -1404,10 +1427,37 @@ void CGUIWindowMusicBase::OnPrepareFileItems(CFileItemList &items)
void CGUIWindowMusicBase::OnAssignContent(const std::string &path)
{
- // Add content selection logic here, if music is ready for that some day
+ // Music scrapers are not source specific, so unlike video there is no content selection logic here.
+ // Called on having added a music source, this starts scanning items into library when required
- // This won't ask you to clean/delete your content, when you change the scraper to none (if music gets this), might ne nice in the future
+ // "Do you want to add the media from this source to your library?"
if (CGUIDialogYesNo::ShowAndGetInput(CVariant{ 20444 }, CVariant{ 20447 }))
+ {
+ if (CServiceBroker::GetSettings().GetBool(CSettings::SETTING_MUSICLIBRARY_DOWNLOADINFO))
+ {
+ // Scraping as well so check artist info folder setting before scanning, may want to scrape artist info from nfo files
+ std::string folder = CServiceBroker::GetSettings().GetString(CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER);
+ if (folder.empty())
+ {
+ //"Do you have local artist information (NFO) and art files that you want to fetch? Set the location of these artist folders now"
+ //Settings (YES) button allows user to enter the artist info folder
+ if (HELPERS::ShowYesNoDialogText(20223, 38320, 186, 10004) == DialogResponse::YES)
+ {
+ // Choose artist info folder
+ VECSOURCES shares;
+ g_mediaManager.GetLocalDrives(shares);
+ g_mediaManager.GetNetworkLocations(shares);
+ g_mediaManager.GetRemovableDrives(shares);
+ std::string strDirectory = "default location";
+ if (CGUIDialogFileBrowser::ShowAndGetDirectory(shares, g_localizeStrings.Get(20223), strDirectory, true))
+ {
+ if (!strDirectory.empty())
+ CServiceBroker::GetSettings().SetString(CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER, strDirectory);
+ }
+ }
+ }
+ }
g_application.StartMusicScan(path, true);
+ }
}
diff --git a/xbmc/music/windows/GUIWindowMusicBase.h b/xbmc/music/windows/GUIWindowMusicBase.h
index 2cfa245bdc..3e02f17560 100644
--- a/xbmc/music/windows/GUIWindowMusicBase.h
+++ b/xbmc/music/windows/GUIWindowMusicBase.h
@@ -50,7 +50,7 @@ public:
void OnItemInfo(CFileItem *pItem, bool bShowInfo = false);
- void DoScan(const std::string &strPath);
+ void DoScan(const std::string &strPath, bool bRescan = false);
/*! \brief Prompt the user if he wants to start a scan for this folder
\param path the path to assign content for
@@ -79,7 +79,7 @@ protected:
std::string GetStartFolder(const std::string &dir) override;
void OnItemLoaded(CFileItem* pItem) override {}
- virtual void OnScan(int iItem);
+ virtual void OnScan(int iItem, bool bPromptRescan = false);
bool CheckFilterAdvanced(CFileItemList &items) const override;
bool CanContainFilter(const std::string &strDirectory) const override;
diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp
index cb7867c9ed..8359c4c617 100644
--- a/xbmc/settings/AdvancedSettings.cpp
+++ b/xbmc/settings/AdvancedSettings.cpp
@@ -273,7 +273,6 @@ void CAdvancedSettings::Initialize()
m_bMusicLibraryAllItemsOnBottom = false;
m_bMusicLibraryCleanOnUpdate = false;
- m_bMusicLibraryPromptFullTagScan = false;
m_bMusicLibraryArtistSortOnUpdate = false;
m_iMusicLibraryRecentlyAddedItems = 25;
m_strMusicLibraryAlbumFormat = "";
@@ -725,7 +724,6 @@ void CAdvancedSettings::ParseSettingsFile(const std::string &file)
XMLUtils::GetBoolean(pElement, "prioritiseapetags", m_prioritiseAPEv2tags);
XMLUtils::GetBoolean(pElement, "allitemsonbottom", m_bMusicLibraryAllItemsOnBottom);
XMLUtils::GetBoolean(pElement, "cleanonupdate", m_bMusicLibraryCleanOnUpdate);
- XMLUtils::GetBoolean(pElement, "promptfulltagscan", m_bMusicLibraryPromptFullTagScan);
XMLUtils::GetBoolean(pElement, "artistsortonupdate", m_bMusicLibraryArtistSortOnUpdate);
XMLUtils::GetBoolean(pElement, "useartistsortname", m_musicUseArtistSortName);
XMLUtils::GetString(pElement, "albumformat", m_strMusicLibraryAlbumFormat);
diff --git a/xbmc/settings/AdvancedSettings.h b/xbmc/settings/AdvancedSettings.h
index 672667f0cf..6883023e73 100644
--- a/xbmc/settings/AdvancedSettings.h
+++ b/xbmc/settings/AdvancedSettings.h
@@ -255,7 +255,6 @@ class CAdvancedSettings : public ISettingCallback, public ISettingsHandler
int m_iMusicLibraryDateAdded;
bool m_bMusicLibraryAllItemsOnBottom;
bool m_bMusicLibraryCleanOnUpdate;
- bool m_bMusicLibraryPromptFullTagScan;
bool m_bMusicLibraryArtistSortOnUpdate;
std::string m_strMusicLibraryAlbumFormat;
bool m_prioritiseAPEv2tags;
diff --git a/xbmc/settings/CMakeLists.txt b/xbmc/settings/CMakeLists.txt
index 155720f3a6..0e9f7f7a5a 100644
--- a/xbmc/settings/CMakeLists.txt
+++ b/xbmc/settings/CMakeLists.txt
@@ -1,6 +1,7 @@
set(SOURCES AdvancedSettings.cpp
DisplaySettings.cpp
GameSettings.cpp
+ LibExportSettings.cpp
MediaSettings.cpp
MediaSourceSettings.cpp
SettingAddon.cpp
@@ -18,6 +19,7 @@ set(HEADERS AdvancedSettings.h
DiscSettings.h
DisplaySettings.h
GameSettings.h
+ LibExportSettings.h
MediaSettings.h
MediaSourceSettings.h
SettingAddon.h
diff --git a/xbmc/settings/LibExportSettings.cpp b/xbmc/settings/LibExportSettings.cpp
new file mode 100644
index 0000000000..374ce3939f
--- /dev/null
+++ b/xbmc/settings/LibExportSettings.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 Team KODI
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with KODI; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+// LibExportSettings.cpp: implementation of the CLibExportSettings class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "LibExportSettings.h"
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+CLibExportSettings::CLibExportSettings()
+{
+ m_exporttype = ELIBEXPORT_SINGLEFILE;
+ m_itemstoexport = ELIBEXPORT_ALBUMS + ELIBEXPORT_ALBUMARTISTS;
+ m_overwrite = false;
+ m_artwork = false;
+ m_unscraped = false;
+ m_skipnfo = false;
+}
+
+bool CLibExportSettings::operator!=(const CLibExportSettings &right) const
+{
+ if (m_exporttype != right.m_exporttype)
+ return true;
+ if (m_strPath != right.m_strPath)
+ return true;
+ if (m_overwrite != right.m_overwrite)
+ return true;
+ if (m_itemstoexport != right.m_itemstoexport)
+ return true;
+
+ if (m_artwork != right.m_artwork)
+ return true;
+ if (m_unscraped != right.m_unscraped)
+ return true;
+ if (m_skipnfo != right.m_skipnfo)
+ return true;
+
+ return false;
+}
+
+bool CLibExportSettings::IsItemExported(ELIBEXPORTOPTIONS item) const
+{
+ return (m_itemstoexport & item);
+}
+
+std::vector<int> CLibExportSettings::GetExportItems() const
+{
+ std::vector<int> values;
+ if (IsItemExported(ELIBEXPORT_ALBUMS))
+ values.emplace_back(ELIBEXPORT_ALBUMS);
+ if (IsItemExported(ELIBEXPORT_ALBUMARTISTS))
+ values.emplace_back(ELIBEXPORT_ALBUMARTISTS);
+ if (IsItemExported(ELIBEXPORT_SONGARTISTS))
+ values.emplace_back(ELIBEXPORT_SONGARTISTS);
+ if (IsItemExported(ELIBEXPORT_OTHERARTISTS))
+ values.emplace_back(ELIBEXPORT_OTHERARTISTS);
+ if (IsItemExported(ELIBEXPORT_ACTORTHUMBS))
+ values.emplace_back(ELIBEXPORT_ACTORTHUMBS);
+
+ return values;
+}
+
+bool CLibExportSettings::IsSingleFile() const
+{
+ return (m_exporttype == ELIBEXPORT_SINGLEFILE);
+}
+
+bool CLibExportSettings::IsSeparateFiles() const
+{
+ return (m_exporttype == ELIBEXPORT_SEPARATEFILES);
+}
+
+bool CLibExportSettings::IsToLibFolders() const
+{
+ return (m_exporttype == ELIBEXPORT_TOLIBRARYFOLDER);
+}
diff --git a/xbmc/settings/LibExportSettings.h b/xbmc/settings/LibExportSettings.h
new file mode 100644
index 0000000000..2827c55201
--- /dev/null
+++ b/xbmc/settings/LibExportSettings.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 Team KODI
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with KODI; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+// LibExportSettings.h: interface for the CLibExportSettings class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#pragma once
+
+#include <string>
+#include "settings/lib/Setting.h"
+
+// Enumeration of library export options (possibly OR'd together)
+enum ELIBEXPORTOPTIONS
+{
+ ELIBEXPORT_SINGLEFILE = 0x0000,
+ ELIBEXPORT_SEPARATEFILES = 0x0001,
+ ELIBEXPORT_TOLIBRARYFOLDER = 0x0002,
+ ELIBEXPORT_OVERWRITE = 0x0004,
+ ELIBEXPORT_UNSCRAPED = 0x0008,
+ ELIBEXPORT_ALBUMS = 0x0010,
+ ELIBEXPORT_ALBUMARTISTS = 0x0020,
+ ELIBEXPORT_SONGARTISTS = 0x0040,
+ ELIBEXPORT_OTHERARTISTS = 0x0080,
+ ELIBEXPORT_ARTWORK = 0x0100,
+ ELIBEXPORT_NFOFILES = 0x0200,
+ ELIBEXPORT_ACTORTHUMBS = 0x0400
+};
+
+class CLibExportSettings
+{
+public:
+ CLibExportSettings();
+ ~CLibExportSettings() = default;
+
+ bool operator!=(const CLibExportSettings &right) const;
+ bool IsItemExported(ELIBEXPORTOPTIONS item) const;
+ std::vector<int> GetExportItems() const;
+ void ClearItems() { m_itemstoexport = 0; }
+ void AddItem(ELIBEXPORTOPTIONS item) { m_itemstoexport += item; }
+ unsigned int GetItemsToExport() { return m_itemstoexport; }
+ void SetItemsToExport(int itemstoexport) { m_itemstoexport = static_cast<unsigned int>(itemstoexport); }
+ unsigned int GetExportType() { return m_exporttype; }
+ void SetExportType(int exporttype) { m_exporttype = static_cast<unsigned int>(exporttype); }
+ bool IsSingleFile() const;
+ bool IsSeparateFiles() const;
+ bool IsToLibFolders() const;
+
+ std::string m_strPath;
+ bool m_overwrite;
+ bool m_artwork;
+ bool m_unscraped;
+ bool m_skipnfo;
+private:
+ unsigned int m_exporttype; //singlefile, separate files, to library folder
+ unsigned int m_itemstoexport;
+};
diff --git a/xbmc/settings/MediaSettings.cpp b/xbmc/settings/MediaSettings.cpp
index 2d75aa78e6..e801c52aff 100644
--- a/xbmc/settings/MediaSettings.cpp
+++ b/xbmc/settings/MediaSettings.cpp
@@ -27,9 +27,11 @@
#include "PlayListPlayer.h"
#include "dialogs/GUIDialogContextMenu.h"
#include "dialogs/GUIDialogFileBrowser.h"
+#include "settings/dialogs/GUIDialogLibExportSettings.h"
#include "guilib/LocalizeStrings.h"
#include "interfaces/builtins/Builtins.h"
#include "music/MusicDatabase.h"
+#include "music/MusicLibraryQueue.h"
#include "messaging/ApplicationMessenger.h"
#include "messaging/helpers/DialogHelper.h"
#include "profiles/ProfilesManager.h"
@@ -294,7 +296,14 @@ void CMediaSettings::OnSettingAction(std::shared_ptr<const CSetting> setting)
g_application.StartMusicCleanup(true);
}
else if (settingId == CSettings::SETTING_MUSICLIBRARY_EXPORT)
- CBuiltins::GetInstance().Execute("exportlibrary(music)");
+ {
+ CLibExportSettings m_musicExportSettings;
+ if (CGUIDialogLibExportSettings::Show(m_musicExportSettings))
+ {
+ // Export music library showing progress dialog
+ CMusicLibraryQueue::GetInstance().ExportLibrary(m_musicExportSettings, true);
+ }
+ }
else if (settingId == CSettings::SETTING_MUSICLIBRARY_IMPORT)
{
std::string path;
diff --git a/xbmc/settings/MediaSettings.h b/xbmc/settings/MediaSettings.h
index 0a89536bd4..978b135877 100644
--- a/xbmc/settings/MediaSettings.h
+++ b/xbmc/settings/MediaSettings.h
@@ -26,7 +26,7 @@
#include "settings/lib/ISettingsHandler.h"
#include "settings/lib/ISubSettings.h"
#include "settings/GameSettings.h"
-#include "cores/VideoSettings.h"
+#include "settings/LibExportSettings.h"
#include "threads/CriticalSection.h"
#define VOLUME_DRC_MINIMUM 0 // 0dB
diff --git a/xbmc/settings/Settings.cpp b/xbmc/settings/Settings.cpp
index 4d640e990e..587ebcef81 100644
--- a/xbmc/settings/Settings.cpp
+++ b/xbmc/settings/Settings.cpp
@@ -260,6 +260,7 @@ const std::string CSettings::SETTING_PVRCLIENT_MENUHOOK = "pvrclient.menuhook";
const std::string CSettings::SETTING_PVRTIMERS_HIDEDISABLEDTIMERS = "pvrtimers.hidedisabledtimers";
const std::string CSettings::SETTING_MUSICLIBRARY_SHOWCOMPILATIONARTISTS = "musiclibrary.showcompilationartists";
const std::string CSettings::SETTING_MUSICLIBRARY_DOWNLOADINFO = "musiclibrary.downloadinfo";
+const std::string CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER = "musiclibrary.artistsfolder";
const std::string CSettings::SETTING_MUSICLIBRARY_ALBUMSSCRAPER = "musiclibrary.albumsscraper";
const std::string CSettings::SETTING_MUSICLIBRARY_ARTISTSSCRAPER = "musiclibrary.artistsscraper";
const std::string CSettings::SETTING_MUSICLIBRARY_OVERRIDETAGS = "musiclibrary.overridetags";
@@ -268,6 +269,13 @@ const std::string CSettings::SETTING_MUSICLIBRARY_UPDATEONSTARTUP = "musiclibrar
const std::string CSettings::SETTING_MUSICLIBRARY_BACKGROUNDUPDATE = "musiclibrary.backgroundupdate";
const std::string CSettings::SETTING_MUSICLIBRARY_CLEANUP = "musiclibrary.cleanup";
const std::string CSettings::SETTING_MUSICLIBRARY_EXPORT = "musiclibrary.export";
+const std::string CSettings::SETTING_MUSICLIBRARY_EXPORT_FILETYPE = "musiclibrary.exportfiletype";
+const std::string CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER = "musiclibrary.exportfolder";
+const std::string CSettings::SETTING_MUSICLIBRARY_EXPORT_ITEMS = "musiclibrary.exportitems";
+const std::string CSettings::SETTING_MUSICLIBRARY_EXPORT_UNSCRAPED = "musiclibrary.exportunscraped";
+const std::string CSettings::SETTING_MUSICLIBRARY_EXPORT_OVERWRITE = "musiclibrary.exportoverwrite";
+const std::string CSettings::SETTING_MUSICLIBRARY_EXPORT_ARTWORK = "musiclibrary.exportartwork";
+const std::string CSettings::SETTING_MUSICLIBRARY_EXPORT_SKIPNFO = "musiclibrary.exportskipnfo";
const std::string CSettings::SETTING_MUSICLIBRARY_IMPORT = "musiclibrary.import";
const std::string CSettings::SETTING_MUSICPLAYER_AUTOPLAYNEXTITEM = "musicplayer.autoplaynextitem";
const std::string CSettings::SETTING_MUSICPLAYER_QUEUEBYDEFAULT = "musicplayer.queuebydefault";
diff --git a/xbmc/settings/Settings.h b/xbmc/settings/Settings.h
index b76f35fa16..c3decd38ec 100644
--- a/xbmc/settings/Settings.h
+++ b/xbmc/settings/Settings.h
@@ -206,6 +206,7 @@ public:
static const std::string SETTING_PVRTIMERS_HIDEDISABLEDTIMERS;
static const std::string SETTING_MUSICLIBRARY_SHOWCOMPILATIONARTISTS;
static const std::string SETTING_MUSICLIBRARY_DOWNLOADINFO;
+ static const std::string SETTING_MUSICLIBRARY_ARTISTSFOLDER;
static const std::string SETTING_MUSICLIBRARY_ALBUMSSCRAPER;
static const std::string SETTING_MUSICLIBRARY_ARTISTSSCRAPER;
static const std::string SETTING_MUSICLIBRARY_OVERRIDETAGS;
@@ -214,6 +215,13 @@ public:
static const std::string SETTING_MUSICLIBRARY_BACKGROUNDUPDATE;
static const std::string SETTING_MUSICLIBRARY_CLEANUP;
static const std::string SETTING_MUSICLIBRARY_EXPORT;
+ static const std::string SETTING_MUSICLIBRARY_EXPORT_FILETYPE;
+ static const std::string SETTING_MUSICLIBRARY_EXPORT_FOLDER;
+ static const std::string SETTING_MUSICLIBRARY_EXPORT_ITEMS;
+ static const std::string SETTING_MUSICLIBRARY_EXPORT_UNSCRAPED;
+ static const std::string SETTING_MUSICLIBRARY_EXPORT_OVERWRITE;
+ static const std::string SETTING_MUSICLIBRARY_EXPORT_ARTWORK;
+ static const std::string SETTING_MUSICLIBRARY_EXPORT_SKIPNFO;
static const std::string SETTING_MUSICLIBRARY_IMPORT;
static const std::string SETTING_MUSICPLAYER_AUTOPLAYNEXTITEM;
static const std::string SETTING_MUSICPLAYER_QUEUEBYDEFAULT;
diff --git a/xbmc/settings/dialogs/CMakeLists.txt b/xbmc/settings/dialogs/CMakeLists.txt
index 1ee3a76306..59ab1c831a 100644
--- a/xbmc/settings/dialogs/CMakeLists.txt
+++ b/xbmc/settings/dialogs/CMakeLists.txt
@@ -1,11 +1,13 @@
set(SOURCES GUIDialogAudioDSPManager.cpp
GUIDialogContentSettings.cpp
+ GUIDialogLibExportSettings.cpp
GUIDialogSettingsBase.cpp
GUIDialogSettingsManagerBase.cpp
GUIDialogSettingsManualBase.cpp)
set(HEADERS GUIDialogAudioDSPManager.h
GUIDialogContentSettings.h
+ GUIDialogLibExportSettings.h
GUIDialogSettingsBase.h
GUIDialogSettingsManagerBase.h
GUIDialogSettingsManualBase.h)
diff --git a/xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp b/xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
new file mode 100644
index 0000000000..f9ee2040bc
--- /dev/null
+++ b/xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
@@ -0,0 +1,368 @@
+/*
+* Copyright (C) 2005-2014 Team XBMC
+* http://xbmc.org
+*
+* This Program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+*
+* This Program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with XBMC; see the file COPYING. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+*/
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <limits.h>
+
+#include "GUIDialogLibExportSettings.h"
+#include "dialogs/GUIDialogFileBrowser.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "messaging/helpers/DialogHelper.h"
+#include "messaging/helpers/DialogOKHelper.h"
+#include "ServiceBroker.h"
+#include "settings/SettingUtils.h"
+#include "settings/lib/Setting.h"
+#include "settings/Settings.h"
+#include "settings/windows/GUIControlSettings.h"
+#include "storage/MediaManager.h"
+#include "Util.h"
+#include "utils/log.h"
+#include "utils/URIUtils.h"
+#include "filesystem/Directory.h"
+
+using namespace ADDON;
+using namespace KODI::MESSAGING;
+
+using KODI::MESSAGING::HELPERS::DialogResponse;
+
+CGUIDialogLibExportSettings::CGUIDialogLibExportSettings()
+ : CGUIDialogSettingsManualBase(WINDOW_DIALOG_LIBEXPORT_SETTINGS, "DialogSettings.xml"),
+ m_destinationChecked(false)
+{ }
+
+bool CGUIDialogLibExportSettings::Show(CLibExportSettings& settings)
+{
+ CGUIDialogLibExportSettings *dialog = g_windowManager.GetWindow<CGUIDialogLibExportSettings>(WINDOW_DIALOG_LIBEXPORT_SETTINGS);
+ if (!dialog)
+ return false;
+
+ // Get current export settings from service broker
+ dialog->m_settings.SetExportType(CServiceBroker::GetSettings().GetInt(CSettings::SETTING_MUSICLIBRARY_EXPORT_FILETYPE));
+ dialog->m_settings.m_strPath = CServiceBroker::GetSettings().GetString(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER);
+ dialog->m_settings.SetItemsToExport(CServiceBroker::GetSettings().GetInt(CSettings::SETTING_MUSICLIBRARY_EXPORT_ITEMS));
+ dialog->m_settings.m_unscraped = CServiceBroker::GetSettings().GetBool(CSettings::SETTING_MUSICLIBRARY_EXPORT_UNSCRAPED);
+ dialog->m_settings.m_artwork = CServiceBroker::GetSettings().GetBool(CSettings::SETTING_MUSICLIBRARY_EXPORT_ARTWORK);
+ dialog->m_settings.m_skipnfo = CServiceBroker::GetSettings().GetBool(CSettings::SETTING_MUSICLIBRARY_EXPORT_SKIPNFO);
+ dialog->m_settings.m_overwrite = CServiceBroker::GetSettings().GetBool(CSettings::SETTING_MUSICLIBRARY_EXPORT_OVERWRITE);
+
+ dialog->m_destinationChecked = false;
+ dialog->Open();
+
+ bool confirmed = dialog->IsConfirmed();
+ if (confirmed)
+ {
+ // Return the new settings (saved by service broker but avoids re-reading)
+ settings = dialog->m_settings;
+ }
+ return confirmed;
+}
+
+void CGUIDialogLibExportSettings::OnInitWindow()
+{
+ CGUIDialogSettingsManualBase::OnInitWindow();
+}
+
+void CGUIDialogLibExportSettings::OnSettingChanged(std::shared_ptr<const CSetting> setting)
+{
+ if (!setting)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingChanged(setting);
+
+ const std::string &settingId = setting->GetId();
+
+ if (settingId == CSettings::SETTING_MUSICLIBRARY_EXPORT_FILETYPE)
+ {
+ m_settings.SetExportType(std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
+ SetupView();
+ SetFocus(CSettings::SETTING_MUSICLIBRARY_EXPORT_FILETYPE);
+ }
+ else if (settingId == CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER)
+ {
+ m_settings.m_strPath = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
+ UpdateButtons();
+ }
+ else if (settingId == CSettings::SETTING_MUSICLIBRARY_EXPORT_OVERWRITE)
+ m_settings.m_overwrite = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ else if (settingId == CSettings::SETTING_MUSICLIBRARY_EXPORT_ITEMS)
+ m_settings.SetItemsToExport(GetExportItemsFromSetting(setting));
+ else if (settingId == CSettings::SETTING_MUSICLIBRARY_EXPORT_ARTWORK)
+ {
+ m_settings.m_artwork = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_EXPORT_SKIPNFO, m_settings.m_artwork);
+ }
+ else if (settingId == CSettings::SETTING_MUSICLIBRARY_EXPORT_UNSCRAPED)
+ m_settings.m_unscraped = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ else if (settingId == CSettings::SETTING_MUSICLIBRARY_EXPORT_SKIPNFO)
+ m_settings.m_skipnfo = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+}
+
+void CGUIDialogLibExportSettings::OnSettingAction(std::shared_ptr<const CSetting> setting)
+{
+ if (setting == NULL)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingAction(setting);
+
+ const std::string &settingId = setting->GetId();
+
+ if (settingId == CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER)
+ {
+ VECSOURCES shares;
+ g_mediaManager.GetLocalDrives(shares);
+ g_mediaManager.GetNetworkLocations(shares);
+ g_mediaManager.GetRemovableDrives(shares);
+ std::string strDirectory = m_settings.m_strPath;
+ if (!strDirectory.empty())
+ {
+ URIUtils::AddSlashAtEnd(strDirectory);
+ bool bIsSource;
+ if (CUtil::GetMatchingSource(strDirectory, shares, bIsSource) < 0) // path is outside shares - add it as a separate one
+ {
+ CMediaSource share;
+ share.strName = g_localizeStrings.Get(13278);
+ share.strPath = strDirectory;
+ shares.push_back(share);
+ }
+ }
+ else
+ strDirectory = "default location";
+
+ if (CGUIDialogFileBrowser::ShowAndGetDirectory(shares, g_localizeStrings.Get(661), strDirectory, true))
+ {
+ if (!strDirectory.empty())
+ {
+ m_destinationChecked = true;
+ m_settings.m_strPath = strDirectory;
+ SetLabel2(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, strDirectory);
+ SetFocus(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER);
+ }
+ }
+ UpdateButtons();
+ }
+}
+
+bool CGUIDialogLibExportSettings::OnMessage(CGUIMessage& message)
+{
+ switch (message.GetMessage())
+ {
+ case GUI_MSG_CLICKED:
+ {
+ int iControl = message.GetSenderId();
+ if (iControl == CONTROL_SETTINGS_OKAY_BUTTON)
+ {
+ OnOK();
+ return true;
+ }
+ }
+ break;
+ }
+ return CGUIDialogSettingsManualBase::OnMessage(message);
+}
+
+void CGUIDialogLibExportSettings::OnOK()
+{
+ // Validate destination folder
+ if (m_settings.IsToLibFolders())
+ {
+ // Check artist info folder setting
+ std::string path = CServiceBroker::GetSettings().GetString(CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER);
+ if (path.empty())
+ {
+ //"Unable to export to library folders as the system artist information folder setting is empty"
+ //Settings (YES) button takes user to enter the artist info folder setting
+ if (HELPERS::ShowYesNoDialogText(20223, 38317, 186, 10004) == DialogResponse::YES)
+ {
+ m_confirmed = false;
+ Close();
+ g_windowManager.ActivateWindow(WINDOW_SETTINGS_MEDIA, CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER);
+ }
+ return;
+ }
+ }
+ else if (!m_destinationChecked)
+ {
+ // ELIBEXPORT_SINGLEFILE or LIBEXPORT_SEPARATEFILES
+ // Check that destination folder exists
+ if (!XFILE::CDirectory::Exists(m_settings.m_strPath))
+ {
+ HELPERS::ShowOKDialogText(CVariant{ 38300 }, CVariant{ 38318 });
+ return;
+ }
+ }
+ m_confirmed = true;
+ Save();
+ Close();
+}
+
+void CGUIDialogLibExportSettings::Save()
+{
+ CLog::Log(LOGINFO, "CGUIDialogMusicExportSettings: Save() called");
+ CServiceBroker::GetSettings().SetInt(CSettings::SETTING_MUSICLIBRARY_EXPORT_FILETYPE, m_settings.GetExportType());
+ CServiceBroker::GetSettings().SetString(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, m_settings.m_strPath);
+ CServiceBroker::GetSettings().SetInt(CSettings::SETTING_MUSICLIBRARY_EXPORT_ITEMS, m_settings.GetItemsToExport());
+ CServiceBroker::GetSettings().SetBool(CSettings::SETTING_MUSICLIBRARY_EXPORT_UNSCRAPED, m_settings.m_unscraped);
+ CServiceBroker::GetSettings().SetBool(CSettings::SETTING_MUSICLIBRARY_EXPORT_OVERWRITE, m_settings.m_overwrite);
+ CServiceBroker::GetSettings().SetBool(CSettings::SETTING_MUSICLIBRARY_EXPORT_ARTWORK, m_settings.m_artwork);
+ CServiceBroker::GetSettings().SetBool(CSettings::SETTING_MUSICLIBRARY_EXPORT_SKIPNFO, m_settings.m_skipnfo);
+ CServiceBroker::GetSettings().Save();
+}
+
+void CGUIDialogLibExportSettings::SetupView()
+{
+ CGUIDialogSettingsManualBase::SetupView();
+ SetHeading(38300);
+
+ SET_CONTROL_HIDDEN(CONTROL_SETTINGS_CUSTOM_BUTTON);
+ SET_CONTROL_LABEL(CONTROL_SETTINGS_OKAY_BUTTON, 38319);
+ SET_CONTROL_LABEL(CONTROL_SETTINGS_CANCEL_BUTTON, 222);
+
+ SetLabel2(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, m_settings.m_strPath);
+
+ if (m_settings.IsSingleFile())
+ {
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, true);
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_EXPORT_OVERWRITE, false);
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_EXPORT_ARTWORK, false);
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_EXPORT_SKIPNFO, false);
+ }
+ else if (m_settings.IsSeparateFiles())
+ {
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, true);
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_EXPORT_OVERWRITE, true);
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_EXPORT_ARTWORK, true);
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_EXPORT_SKIPNFO, m_settings.m_artwork);
+ }
+ else // To library folders
+ {
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, false);
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_EXPORT_OVERWRITE, true);
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_EXPORT_ARTWORK, true);
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_EXPORT_SKIPNFO, m_settings.m_artwork);
+ }
+ UpdateButtons();
+}
+
+void CGUIDialogLibExportSettings::UpdateButtons()
+{
+ // Enable Export button when destination folder has a path (but may not exist)
+ bool enableExport(true);
+ if (m_settings.IsSingleFile() ||
+ m_settings.IsSeparateFiles())
+ enableExport = !m_settings.m_strPath.empty();
+
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_SETTINGS_OKAY_BUTTON, enableExport);
+ if (!enableExport)
+ SetFocus(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER);
+}
+
+void CGUIDialogLibExportSettings::InitializeSettings()
+{
+ CGUIDialogSettingsManualBase::InitializeSettings();
+
+ std::shared_ptr<CSettingCategory> category = AddCategory("exportsettings", -1);
+ if (!category)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogLibExportSettings: unable to setup settings");
+ return;
+ }
+
+ std::shared_ptr<CSettingGroup> groupDetails = AddGroup(category);
+ if (!groupDetails)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogLibExportSettings: unable to setup settings");
+ return;
+ }
+
+ TranslatableIntegerSettingOptions entries;
+
+ entries.push_back(std::make_pair(38301, ELIBEXPORT_SINGLEFILE));
+ entries.push_back(std::make_pair(38302, ELIBEXPORT_SEPARATEFILES));
+ entries.push_back(std::make_pair(38303, ELIBEXPORT_TOLIBRARYFOLDER));
+ AddList(groupDetails, CSettings::SETTING_MUSICLIBRARY_EXPORT_FILETYPE, 38304, SettingLevel::Basic, m_settings.GetExportType(), entries, 38304); // "Choose kind of export output"
+ AddButton(groupDetails, CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, 38305, SettingLevel::Basic);
+
+ entries.clear();
+ entries.push_back(std::make_pair(132, ELIBEXPORT_ALBUMS)); //ablums
+ entries.push_back(std::make_pair(38043, ELIBEXPORT_ALBUMARTISTS)); //album artists
+ entries.push_back(std::make_pair(38312, ELIBEXPORT_SONGARTISTS)); //song artists
+ entries.push_back(std::make_pair(38313, ELIBEXPORT_OTHERARTISTS)); //other artists
+ AddList(groupDetails, CSettings::SETTING_MUSICLIBRARY_EXPORT_ITEMS, 38306, SettingLevel::Basic, m_settings.GetExportItems(), entries, 133, 1);
+
+ AddToggle(groupDetails, CSettings::SETTING_MUSICLIBRARY_EXPORT_UNSCRAPED, 38308, SettingLevel::Basic, m_settings.m_unscraped);
+ AddToggle(groupDetails, CSettings::SETTING_MUSICLIBRARY_EXPORT_ARTWORK, 38307, SettingLevel::Basic, m_settings.m_artwork);
+ AddToggle(groupDetails, CSettings::SETTING_MUSICLIBRARY_EXPORT_SKIPNFO, 38309, SettingLevel::Basic, m_settings.m_skipnfo);
+ AddToggle(groupDetails, CSettings::SETTING_MUSICLIBRARY_EXPORT_OVERWRITE, 38310, SettingLevel::Basic, m_settings.m_overwrite);
+}
+
+void CGUIDialogLibExportSettings::SetLabel2(const std::string &settingid, const std::string &label)
+{
+ BaseSettingControlPtr settingControl = GetSettingControl(settingid);
+ if (settingControl != NULL && settingControl->GetControl() != NULL)
+ SET_CONTROL_LABEL2(settingControl->GetID(), label);
+}
+
+
+void CGUIDialogLibExportSettings::ToggleState(const std::string & settingid, bool enabled)
+{
+ BaseSettingControlPtr settingControl = GetSettingControl(settingid);
+ if (settingControl != NULL && settingControl->GetControl() != NULL)
+ {
+ if (enabled)
+ CONTROL_ENABLE(settingControl->GetID());
+ else
+ CONTROL_DISABLE(settingControl->GetID());
+ }
+}
+
+void CGUIDialogLibExportSettings::SetFocus(const std::string &settingid)
+{
+ BaseSettingControlPtr settingControl = GetSettingControl(settingid);
+ if (settingControl != NULL && settingControl->GetControl() != NULL)
+ SET_CONTROL_FOCUS(settingControl->GetID(), 0);
+}
+
+int CGUIDialogLibExportSettings::GetExportItemsFromSetting(SettingConstPtr setting)
+{
+ std::shared_ptr<const CSettingList> settingList = std::static_pointer_cast<const CSettingList>(setting);
+ if (settingList->GetElementType() != SettingType::Integer)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogLibExportSettings::%s - wrong items element type", __FUNCTION__);
+ return 0;
+ }
+ int exportitems = 0;
+ std::vector<CVariant> list = CSettingUtils::GetList(settingList);
+ for (const auto &value : list)
+ {
+ if (!value.isInteger())
+ {
+ CLog::Log(LOGERROR, "CGUIDialogLibExportSettings::%s - wrong items value type", __FUNCTION__);
+ return 0;
+ }
+ exportitems += value.asInteger();
+ }
+ return exportitems;
+}
diff --git a/xbmc/settings/dialogs/GUIDialogLibExportSettings.h b/xbmc/settings/dialogs/GUIDialogLibExportSettings.h
new file mode 100644
index 0000000000..958e8ff209
--- /dev/null
+++ b/xbmc/settings/dialogs/GUIDialogLibExportSettings.h
@@ -0,0 +1,66 @@
+#pragma once
+/*
+ * Copyright (C) 2017 Team KODI
+ * http://kodi.tv
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with KODI; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <map>
+
+#include "settings/dialogs/GUIDialogSettingsManualBase.h"
+#include "settings/LibExportSettings.h"
+
+class CGUIDialogLibExportSettings : public CGUIDialogSettingsManualBase
+{
+public:
+ CGUIDialogLibExportSettings();
+
+ // specialization of CGUIWindow
+ bool HasListItems() const override { return true; };
+ static bool Show(CLibExportSettings& settings);
+
+protected:
+ // specializations of CGUIWindow
+ void OnInitWindow() override;
+
+ // implementations of ISettingCallback
+ void OnSettingChanged(std::shared_ptr<const CSetting> setting) override;
+ void OnSettingAction(std::shared_ptr<const CSetting> setting) override;
+
+ // specialization of CGUIDialogSettingsBase
+ bool OnMessage(CGUIMessage& message) override;
+ bool AllowResettingSettings() const override { return false; }
+ void Save() override;
+ void SetupView() override;
+
+ // specialization of CGUIDialogSettingsManualBase
+ void InitializeSettings() override;
+
+ void OnOK();
+ void UpdateButtons();
+
+private:
+ void SetLabel2(const std::string &settingid, const std::string &label);
+ void ToggleState(const std::string &settingid, bool enabled);
+
+ using CGUIDialogSettingsManualBase::SetFocus;
+ void SetFocus(const std::string &settingid);
+ static int GetExportItemsFromSetting(SettingConstPtr setting);
+
+ CLibExportSettings m_settings;
+ bool m_destinationChecked;
+};
diff --git a/xbmc/video/dialogs/GUIDialogVideoInfo.cpp b/xbmc/video/dialogs/GUIDialogVideoInfo.cpp
index 2e31462ced..c4b2bfdd48 100644
--- a/xbmc/video/dialogs/GUIDialogVideoInfo.cpp
+++ b/xbmc/video/dialogs/GUIDialogVideoInfo.cpp
@@ -1703,15 +1703,22 @@ bool CGUIDialogVideoInfo::ManageVideoItemArtwork(const CFileItemPtr &item, const
std::string currentThumb;
int idArtist = -1;
std::string artistPath;
+ std::string artistOldPath;
std::string artType = "thumb";
if (type == MediaTypeArtist)
{
CMusicDatabase musicdb;
if (musicdb.Open())
{
- idArtist = musicdb.GetArtistByName(item->GetLabel());
- if (idArtist >= 0 && musicdb.GetArtistPath(idArtist, artistPath))
+ idArtist = musicdb.GetArtistByName(item->GetLabel()); // Fails when name not unique
+ if (idArtist >= 0 )
{
+ // Get artist paths - possible locations for thumb - while music db open
+ musicdb.GetOldArtistPath(idArtist, artistOldPath); // Old artist path, local to music files
+ CArtist artist;
+ musicdb.GetArtist(idArtist, artist); // Need name and mbid for artist folder name
+ musicdb.GetArtistPath(artist, artistPath); // Artist path in artist info folder
+
currentThumb = musicdb.GetArtForItem(idArtist, MediaTypeArtist, "thumb");
if (currentThumb.empty())
currentThumb = videodb.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artType);
@@ -1808,9 +1815,23 @@ bool CGUIDialogVideoInfo::ManageVideoItemArtwork(const CFileItemPtr &item, const
noneitem->SetIconImage("DefaultVideo.png");
}
else
- {
- std::string strThumb = URIUtils::AddFileToFolder(artistPath, "folder.jpg");
- if (XFILE::CFile::Exists(strThumb))
+ {
+ std::string strThumb;
+ bool existsThumb = false;
+ // First look for artist thumb in the primary location
+ if (!artistPath.empty())
+ {
+ strThumb = URIUtils::AddFileToFolder(artistPath, "folder.jpg");
+ existsThumb = CFile::Exists(strThumb);
+ }
+ // If not there fall back local to music files (historic location for those album artists with a unique folder)
+ if (!existsThumb && !artistOldPath.empty())
+ {
+ strThumb = URIUtils::AddFileToFolder(artistOldPath, "folder.jpg");
+ existsThumb = CFile::Exists(strThumb);
+ }
+
+ if (existsThumb)
{
CFileItemPtr pItem(new CFileItem(strThumb, false));
pItem->SetLabel(g_localizeStrings.Get(13514));