diff --git a/system/library/music/boxsets.xml b/system/library/music/boxsets.xml
new file mode 100644
index 0000000000000..3a7b8c98f748f
--- /dev/null
+++ b/system/library/music/boxsets.xml
@@ -0,0 +1,6 @@
+
+
+ Boxsets
+ DefaultSets.png
+ musicdb://boxsets/
+
diff --git a/xbmc/GUIInfoManager.cpp b/xbmc/GUIInfoManager.cpp
index 5f0fac8fdb570..669b13f7d2c04 100644
--- a/xbmc/GUIInfoManager.cpp
+++ b/xbmc/GUIInfoManager.cpp
@@ -2520,6 +2520,14 @@ const infomap musicpartymode[] = {{ "enabled", MUSICPM_ENABLED },
/// @skinning_v17 **[New Infolabel]** \link MusicPlayer_DBID `MusicPlayer.DBID`\endlink
///
/// }
+/// \table_row3{ `MusicPlayer.DiscTitle` ,
+/// \anchor MusicPlayer_DiscTitle
+/// _string_,
+/// @return The title of the disc currently playing.
+///
+/// @skinning_v19 **[New Infolabel]** \link MusicPlayer_DiscTitle `MusicPlayer.DiscTitle`\endlink
+///
+/// }
/// \table_end
///
/// -----------------------------------------------------------------------------
@@ -2540,6 +2548,8 @@ const infomap musicplayer[] = {{ "title", MUSICPLAYER_TITLE },
{ "samplerate", MUSICPLAYER_SAMPLERATE },
{ "codec", MUSICPLAYER_CODEC },
{ "discnumber", MUSICPLAYER_DISC_NUMBER },
+ { "disctitle", MUSICPLAYER_DISC_TITLE },
+ { "isboxset", MUSICPLAYER_IS_BOXSET },
{ "rating", MUSICPLAYER_RATING },
{ "ratingandvotes", MUSICPLAYER_RATING_AND_VOTES },
{ "userrating", MUSICPLAYER_USER_RATING },
@@ -5717,6 +5727,14 @@ const infomap container_str[] = {{ "property", CONTAINER_PROPERTY },
/// @skinning_v19 **[New Infolabel]** \link ListItem_CurrentItem `ListItem.CurrentItem`\endlink
///
/// }
+/// \table_row3{ `ListItem.DiscTitle` ,
+/// \anchor ListItem_DiscTitle
+/// _string_,
+/// @return The disc title of the currently selected album or song.
+///
+/// @skinning_v19 **[New Infolabel]** \link ListItem_DiscTitle `ListItem.DiscTitle`\endlink
+///
+/// }
/// \table_end
///
/// -----------------------------------------------------------------------------
@@ -5736,6 +5754,8 @@ const infomap listitem_labels[]= {{ "thumb", LISTITEM_THUMB },
{ "contributors", LISTITEM_CONTRIBUTORS },
{ "contributorandrole", LISTITEM_CONTRIBUTOR_AND_ROLE },
{ "director", LISTITEM_DIRECTOR },
+ { "disctitle", LISTITEM_DISC_TITLE },
+ { "isboxset", LISTITEM_IS_BOXSET },
{ "filename", LISTITEM_FILENAME },
{ "filenameandpath", LISTITEM_FILENAME_AND_PATH },
{ "fileextension", LISTITEM_FILE_EXTENSION },
@@ -8455,6 +8475,14 @@ const infomap slideshow[] = {{ "ispaused", SLIDESHOW_ISPAUSED
/// @skinning_v17 **[New Boolean Condition]** \link Library_HasContent_Role_Mixer `Library.HasContent(Role.Mixer)`\endlink
///
/// }
+/// \table_row3{ `Library.HasContent(boxsets)` ,
+/// \anchor Library_HasContent_Boxsets
+/// _boolean_,
+/// @return **True** if there are albums in the library which are boxsets.
+///
+/// @skinning_v19 **[New Boolean Condition]** \link Library_HasContent_Boxsets `Library.HasContent(boxsets)`\endlink
+///
+/// }
/// \table_end
///
/// -----------------------------------------------------------------------------
@@ -8869,6 +8897,8 @@ int CGUIInfoManager::TranslateSingleString(const std::string &strCondition, bool
return LIBRARY_HAS_SINGLES;
else if (cat == "compilations")
return LIBRARY_HAS_COMPILATIONS;
+ else if (cat == "boxsets")
+ return LIBRARY_HAS_BOXSETS;
else if (cat == "role" && prop.num_params() > 1)
return AddMultiInfo(CGUIInfo(LIBRARY_HAS_ROLE, prop.param(1), 0));
}
diff --git a/xbmc/filesystem/MusicDatabaseDirectory.cpp b/xbmc/filesystem/MusicDatabaseDirectory.cpp
index 68f32530df559..fc539730dd294 100644
--- a/xbmc/filesystem/MusicDatabaseDirectory.cpp
+++ b/xbmc/filesystem/MusicDatabaseDirectory.cpp
@@ -121,7 +121,6 @@ bool CMusicDatabaseDirectory::IsAllItem(const std::string& strDirectory)
bool CMusicDatabaseDirectory::GetLabel(const std::string& strDirectory, std::string& strLabel)
{
strLabel = "";
-
std::string path = CLegacyPathTranslation::TranslateMusicDbPath(strDirectory);
std::unique_ptr pNode(CDirectoryNode::ParseURL(path));
if (!pNode.get())
@@ -207,6 +206,9 @@ bool CMusicDatabaseDirectory::GetLabel(const std::string& strDirectory, std::str
case NODE_TYPE_ALBUM_COMPILATIONS_SONGS:
strLabel = g_localizeStrings.Get(521);
break;
+ case NODE_TYPE_BOXSETS:
+ strLabel = g_localizeStrings.Get(38074); // box-sets
+ break;
case NODE_TYPE_OVERVIEW:
strLabel = "";
break;
@@ -214,7 +216,6 @@ bool CMusicDatabaseDirectory::GetLabel(const std::string& strDirectory, std::str
return false;
}
}
-
return true;
}
@@ -229,6 +230,7 @@ bool CMusicDatabaseDirectory::ContainsSongs(const std::string &path)
if (type == MUSICDATABASEDIRECTORY::NODE_TYPE_ALBUM_TOP100_SONGS) return true;
if (type == MUSICDATABASEDIRECTORY::NODE_TYPE_SONG_TOP100) return true;
if (type == MUSICDATABASEDIRECTORY::NODE_TYPE_YEAR_SONG) return true;
+ if (type == MUSICDATABASEDIRECTORY::NODE_TYPE_BOXSET_DISC_SONGS) return true;
return false;
}
@@ -290,6 +292,8 @@ std::string CMusicDatabaseDirectory::GetIcon(const std::string &strDirectory)
return "DefaultMusicTop100Songs.png";
case NODE_TYPE_YEAR:
return "DefaultMusicYears.png";
+ case NODE_TYPE_BOXSETS:
+ return "DefaultSets.png";
case NODE_TYPE_ALBUM_COMPILATIONS:
return "DefaultMusicCompilations.png";
default:
diff --git a/xbmc/filesystem/MusicDatabaseDirectory/CMakeLists.txt b/xbmc/filesystem/MusicDatabaseDirectory/CMakeLists.txt
index 6de6b45ab1625..8844871380d66 100644
--- a/xbmc/filesystem/MusicDatabaseDirectory/CMakeLists.txt
+++ b/xbmc/filesystem/MusicDatabaseDirectory/CMakeLists.txt
@@ -8,6 +8,9 @@ set(SOURCES DirectoryNodeAlbumCompilations.cpp
DirectoryNodeAlbumTop100.cpp
DirectoryNodeAlbumTop100Song.cpp
DirectoryNodeArtist.cpp
+ DirectoryNodeBoxsets.cpp
+ DirectoryNodeBoxsetDiscs.cpp
+ DirectoryNodeBoxsetDiscSongs.cpp
DirectoryNode.cpp
DirectoryNodeGrouped.cpp
DirectoryNodeOverview.cpp
@@ -31,6 +34,9 @@ set(HEADERS DirectoryNode.h
DirectoryNodeAlbumTop100.h
DirectoryNodeAlbumTop100Song.h
DirectoryNodeArtist.h
+ DirectoryNodeBoxsets.h
+ DirectoryNodeBoxsetDiscs.h
+ DirectoryNodeBoxsetDiscSongs.h
DirectoryNodeGrouped.h
DirectoryNodeOverview.h
DirectoryNodeRoot.h
diff --git a/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNode.cpp b/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNode.cpp
index 73224c89fea32..30504a4cc184e 100644
--- a/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNode.cpp
+++ b/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNode.cpp
@@ -18,6 +18,9 @@
#include "DirectoryNodeAlbumTop100.h"
#include "DirectoryNodeAlbumTop100Song.h"
#include "DirectoryNodeArtist.h"
+#include "DirectoryNodeBoxsets.h"
+#include "DirectoryNodeBoxsetDiscs.h"
+#include "DirectoryNodeBoxsetDiscSongs.h"
#include "DirectoryNodeGrouped.h"
#include "DirectoryNodeOverview.h"
#include "DirectoryNodeRoot.h"
@@ -102,6 +105,12 @@ CDirectoryNode* CDirectoryNode::CreateNode(NODE_TYPE Type, const std::string& st
case NODE_TYPE_ROLE:
case NODE_TYPE_YEAR:
return new CDirectoryNodeGrouped(Type, strName, pParent);
+ case NODE_TYPE_BOXSETS:
+ return new CDirectoryNodeBoxsets(strName, pParent);
+ case NODE_TYPE_BOXSET_DISCS:
+ return new CDirectoryNodeBoxsetDiscs(strName, pParent);
+ case NODE_TYPE_BOXSET_DISC_SONGS:
+ return new CDirectoryNodeBoxsetDiscSongs(strName, pParent);
case NODE_TYPE_ARTIST:
return new CDirectoryNodeArtist(strName, pParent);
case NODE_TYPE_ALBUM:
diff --git a/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNode.h b/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNode.h
index 016aef49a5b6f..deb46c7a51d98 100644
--- a/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNode.h
+++ b/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNode.h
@@ -42,7 +42,10 @@ namespace XFILE
NODE_TYPE_YEAR,
NODE_TYPE_YEAR_ALBUM,
NODE_TYPE_YEAR_SONG,
- NODE_TYPE_SINGLES
+ NODE_TYPE_SINGLES,
+ NODE_TYPE_BOXSETS,
+ NODE_TYPE_BOXSET_DISCS,
+ NODE_TYPE_BOXSET_DISC_SONGS
} NODE_TYPE;
typedef struct {
@@ -90,5 +93,3 @@ namespace XFILE
};
}
}
-
-
diff --git a/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeBoxsetDiscSongs.cpp b/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeBoxsetDiscSongs.cpp
new file mode 100644
index 0000000000000..9828ba3372050
--- /dev/null
+++ b/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeBoxsetDiscSongs.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "DirectoryNodeBoxsetDiscSongs.h"
+
+#include "QueryParams.h"
+#include "guilib/LocalizeStrings.h"
+#include "music/MusicDatabase.h"
+
+using namespace XFILE::MUSICDATABASEDIRECTORY;
+
+CDirectoryNodeBoxsetDiscSongs::CDirectoryNodeBoxsetDiscSongs(const std::string& strName, CDirectoryNode* pParent)
+ : CDirectoryNode(NODE_TYPE_BOXSET_DISC_SONGS, strName, pParent)
+{
+
+}
+
+NODE_TYPE CDirectoryNodeBoxsetDiscSongs::GetChildType() const
+{
+ return NODE_TYPE_BOXSET_DISC_SONGS;
+}
+std::string CDirectoryNodeBoxsetDiscSongs::GetLocalizedName() const
+{
+
+ if (GetID() == -1)
+ return g_localizeStrings.Get(15102); // All Albums
+ return "";
+}
+
+bool CDirectoryNodeBoxsetDiscSongs::GetContent(CFileItemList& items) const
+{
+ CMusicDatabase musicdatabase;
+ if (!musicdatabase.Open())
+ return false;
+
+ CQueryParams params;
+ CollectQueryParams(params);
+
+ bool bSuccess=musicdatabase.GetBoxsetDiscSongs(BuildPath(), items);
+
+ musicdatabase.Close();
+
+ return bSuccess;
+}
diff --git a/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeBoxsetDiscSongs.h b/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeBoxsetDiscSongs.h
new file mode 100644
index 0000000000000..f196fab3e4d29
--- /dev/null
+++ b/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeBoxsetDiscSongs.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "DirectoryNode.h"
+
+namespace XFILE
+{
+ namespace MUSICDATABASEDIRECTORY
+ {
+ class CDirectoryNodeBoxsetDiscSongs : public CDirectoryNode
+ {
+ public:
+ CDirectoryNodeBoxsetDiscSongs(const std::string& strName, CDirectoryNode* pParent);
+ protected:
+ NODE_TYPE GetChildType() const override;
+ bool GetContent(CFileItemList& items) const override;
+ std::string GetLocalizedName() const override;
+ };
+ }
+}
diff --git a/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeBoxsetDiscs.cpp b/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeBoxsetDiscs.cpp
new file mode 100644
index 0000000000000..48b0535f3b3c4
--- /dev/null
+++ b/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeBoxsetDiscs.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "DirectoryNodeBoxsetDiscs.h"
+
+#include "QueryParams.h"
+#include "guilib/LocalizeStrings.h"
+#include "music/MusicDatabase.h"
+
+using namespace XFILE::MUSICDATABASEDIRECTORY;
+
+CDirectoryNodeBoxsetDiscs::CDirectoryNodeBoxsetDiscs(const std::string& strName, CDirectoryNode* pParent)
+ : CDirectoryNode(NODE_TYPE_BOXSET_DISCS, strName, pParent)
+{
+
+}
+
+NODE_TYPE CDirectoryNodeBoxsetDiscs::GetChildType() const
+{
+ return NODE_TYPE_BOXSET_DISC_SONGS;
+}
+
+std::string CDirectoryNodeBoxsetDiscs::GetLocalizedName() const
+{
+
+ if (GetID() == -1)
+ return g_localizeStrings.Get(15102); // All Albums
+ return "";
+}
+
+bool CDirectoryNodeBoxsetDiscs::GetContent(CFileItemList& items) const
+{
+ CMusicDatabase musicdatabase;
+ if (!musicdatabase.Open())
+ return false;
+
+ CQueryParams params;
+ CollectQueryParams(params);
+
+ bool bSuccess=musicdatabase.GetBoxsetDiscs(BuildPath(), items, params.GetAlbumId());
+
+ musicdatabase.Close();
+
+ return bSuccess;
+}
diff --git a/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeBoxsetDiscs.h b/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeBoxsetDiscs.h
new file mode 100644
index 0000000000000..4ad43c800cbb2
--- /dev/null
+++ b/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeBoxsetDiscs.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "DirectoryNode.h"
+
+namespace XFILE
+{
+ namespace MUSICDATABASEDIRECTORY
+ {
+ class CDirectoryNodeBoxsetDiscs : public CDirectoryNode
+ {
+ public:
+ CDirectoryNodeBoxsetDiscs(const std::string& strName, CDirectoryNode* pParent);
+ protected:
+ NODE_TYPE GetChildType() const override;
+ bool GetContent(CFileItemList& items) const override;
+ std::string GetLocalizedName() const override;
+ };
+ }
+}
diff --git a/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeBoxsets.cpp b/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeBoxsets.cpp
new file mode 100644
index 0000000000000..b768ab0b8b0ac
--- /dev/null
+++ b/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeBoxsets.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "DirectoryNodeBoxsets.h"
+
+#include "QueryParams.h"
+#include "guilib/LocalizeStrings.h"
+#include "music/MusicDatabase.h"
+
+using namespace XFILE::MUSICDATABASEDIRECTORY;
+
+CDirectoryNodeBoxsets::CDirectoryNodeBoxsets(const std::string& strName, CDirectoryNode* pParent)
+ : CDirectoryNode(NODE_TYPE_BOXSETS, strName, pParent)
+{
+
+}
+
+NODE_TYPE CDirectoryNodeBoxsets::GetChildType() const
+{
+ return NODE_TYPE_BOXSET_DISCS;
+}
+std::string CDirectoryNodeBoxsets::GetLocalizedName() const
+{
+ if (GetID() == -1)
+ return g_localizeStrings.Get(15102); // All Albums
+ CMusicDatabase db;
+ if (db.Open())
+ return db.GetAlbumById(GetID());
+ return "";
+}
+
+bool CDirectoryNodeBoxsets::GetContent(CFileItemList& items) const
+{
+ CMusicDatabase musicdatabase;
+ if (!musicdatabase.Open())
+ return false;
+
+ CQueryParams params;
+ CollectQueryParams(params);
+
+ bool bSuccess=musicdatabase.GetBoxsetsAlbums(BuildPath(), items);
+
+ musicdatabase.Close();
+
+ return bSuccess;
+}
diff --git a/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeBoxsets.h b/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeBoxsets.h
new file mode 100644
index 0000000000000..3d929c4abde83
--- /dev/null
+++ b/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeBoxsets.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "DirectoryNode.h"
+
+namespace XFILE
+{
+ namespace MUSICDATABASEDIRECTORY
+ {
+ class CDirectoryNodeBoxsets : public CDirectoryNode
+ {
+ public:
+ CDirectoryNodeBoxsets(const std::string& strName, CDirectoryNode* pParent);
+ protected:
+ NODE_TYPE GetChildType() const override;
+ bool GetContent(CFileItemList& items) const override;
+ std::string GetLocalizedName() const override;
+ };
+ }
+}
diff --git a/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeOverview.cpp b/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeOverview.cpp
index 25072e426e9a9..4732a8f43db13 100644
--- a/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeOverview.cpp
+++ b/xbmc/filesystem/MusicDatabaseDirectory/DirectoryNodeOverview.cpp
@@ -30,7 +30,8 @@ namespace XFILE
{ NODE_TYPE_ALBUM_COMPILATIONS, "compilations", 521 },
{ NODE_TYPE_ROLE, "roles", 38033 },
{ NODE_TYPE_SOURCE, "sources", 39031 },
- };
+ { NODE_TYPE_BOXSETS, "boxsets", 38074 },
+ };
};
};
@@ -65,13 +66,15 @@ bool CDirectoryNodeOverview::GetContent(CFileItemList& items) const
bool hasSingles = (musicDatabase.GetSinglesCount() > 0);
bool hasCompilations = (musicDatabase.GetCompilationAlbumsCount() > 0);
-
+ bool hasBoxsets = (musicDatabase.GetBoxsetsCount() > 0);
for (unsigned int i = 0; i < sizeof(OverviewChildren) / sizeof(Node); ++i)
{
if (i == 3 && !hasSingles)
continue;
if (i == 9 && !hasCompilations)
continue;
+ if (i == 12 && !hasBoxsets)
+ continue;
CFileItemPtr pItem(new CFileItem(g_localizeStrings.Get(OverviewChildren[i].label)));
std::string strDir = StringUtils::Format("%s/", OverviewChildren[i].id.c_str());
diff --git a/xbmc/filesystem/MusicDatabaseDirectory/QueryParams.cpp b/xbmc/filesystem/MusicDatabaseDirectory/QueryParams.cpp
index 6c991863bfd6f..79a03a774e089 100644
--- a/xbmc/filesystem/MusicDatabaseDirectory/QueryParams.cpp
+++ b/xbmc/filesystem/MusicDatabaseDirectory/QueryParams.cpp
@@ -19,6 +19,8 @@ CQueryParams::CQueryParams()
m_idGenre=-1;
m_idSong=-1;
m_year=-1;
+ m_disc=-1;
+
}
void CQueryParams::SetQueryParam(NODE_TYPE NodeType, const std::string& strNodeName)
@@ -36,12 +38,16 @@ void CQueryParams::SetQueryParam(NODE_TYPE NodeType, const std::string& strNodeN
case NODE_TYPE_ARTIST:
m_idArtist=idDb;
break;
+ case NODE_TYPE_BOXSET_DISCS:
+ m_disc=idDb;
+ break;
case NODE_TYPE_ALBUM_RECENTLY_PLAYED:
case NODE_TYPE_ALBUM_RECENTLY_ADDED:
case NODE_TYPE_ALBUM_COMPILATIONS:
case NODE_TYPE_ALBUM_TOP100:
case NODE_TYPE_ALBUM:
case NODE_TYPE_YEAR_ALBUM:
+ case NODE_TYPE_BOXSETS:
m_idAlbum=idDb;
break;
case NODE_TYPE_ALBUM_RECENTLY_ADDED_SONGS:
@@ -51,6 +57,7 @@ void CQueryParams::SetQueryParam(NODE_TYPE NodeType, const std::string& strNodeN
case NODE_TYPE_YEAR_SONG:
case NODE_TYPE_SONG:
case NODE_TYPE_SONG_TOP100:
+ case NODE_TYPE_BOXSET_DISC_SONGS:
m_idSong=idDb;
default:
break;
diff --git a/xbmc/filesystem/MusicDatabaseDirectory/QueryParams.h b/xbmc/filesystem/MusicDatabaseDirectory/QueryParams.h
index 75f95986dcfde..5d9953a177b68 100644
--- a/xbmc/filesystem/MusicDatabaseDirectory/QueryParams.h
+++ b/xbmc/filesystem/MusicDatabaseDirectory/QueryParams.h
@@ -23,7 +23,7 @@ namespace XFILE
long GetGenreId() { return m_idGenre; }
long GetSongId() { return m_idSong; }
long GetYear() { return m_year; }
-
+ long GetDisc() { return m_idAlbum; }
protected:
void SetQueryParam(NODE_TYPE NodeType, const std::string& strNodeName);
@@ -34,8 +34,7 @@ namespace XFILE
long m_idGenre;
long m_idSong;
long m_year;
+ long m_disc;
};
}
}
-
-
diff --git a/xbmc/guilib/guiinfo/GUIInfoLabels.h b/xbmc/guilib/guiinfo/GUIInfoLabels.h
index de027949b97bb..8f9f31421c6b0 100644
--- a/xbmc/guilib/guiinfo/GUIInfoLabels.h
+++ b/xbmc/guilib/guiinfo/GUIInfoLabels.h
@@ -194,7 +194,8 @@
#define MUSICPLAYER_CONTRIBUTOR_AND_ROLE 240
#define MUSICPLAYER_DBID 241
#define MUSICPLAYER_PROPERTY 242
-
+#define MUSICPLAYER_DISC_TITLE 243
+#define MUSICPLAYER_IS_BOXSET 244
#define VIDEOPLAYER_AUDIO_BITRATE 248
#define VIDEOPLAYER_VIDEO_BITRATE 249
#define VIDEOPLAYER_TITLE 250
@@ -411,6 +412,7 @@
#define LIBRARY_IS_SCANNING_VIDEO 729
#define LIBRARY_IS_SCANNING_MUSIC 730
#define LIBRARY_HAS_ROLE 735
+#define LIBRARY_HAS_BOXSETS 736
#define SYSTEM_PLATFORM_LINUX 741
#define SYSTEM_PLATFORM_WINDOWS 742
@@ -877,7 +879,8 @@
#define LISTITEM_ISPLAYABLE (LISTITEM_START + 186)
#define LISTITEM_FILENAME_NO_EXTENSION (LISTITEM_START + 187)
#define LISTITEM_CURRENTITEM (LISTITEM_START + 188)
-
+#define LISTITEM_DISC_TITLE (LISTITEM_START + 188)
+#define LISTITEM_IS_BOXSET (LISTITEM_START + 189)
#define LISTITEM_END (LISTITEM_START + 2500)
#define CONDITIONAL_LABEL_START (LISTITEM_END + 1) // 37501
diff --git a/xbmc/guilib/guiinfo/LibraryGUIInfo.cpp b/xbmc/guilib/guiinfo/LibraryGUIInfo.cpp
index 510c05b21562b..167fcdb08dfd5 100644
--- a/xbmc/guilib/guiinfo/LibraryGUIInfo.cpp
+++ b/xbmc/guilib/guiinfo/LibraryGUIInfo.cpp
@@ -54,6 +54,9 @@ void CLibraryGUIInfo::SetLibraryBool(int condition, bool value)
case LIBRARY_HAS_COMPILATIONS:
m_libraryHasCompilations = value ? 1 : 0;
break;
+ case LIBRARY_HAS_BOXSETS:
+ m_libraryHasBoxsets = value ? 1 : 0;
+ break;
default:
break;
}
@@ -68,6 +71,7 @@ void CLibraryGUIInfo::ResetLibraryBools()
m_libraryHasMovieSets = -1;
m_libraryHasSingles = -1;
m_libraryHasCompilations = -1;
+ m_libraryHasBoxsets = -1;
m_libraryRoleCounts.clear();
}
@@ -191,6 +195,20 @@ bool CLibraryGUIInfo::GetBool(bool& value, const CGUIListItem *gitem, int contex
value = m_libraryHasCompilations > 0;
return true;
}
+ case LIBRARY_HAS_BOXSETS:
+ {
+ if (m_libraryHasBoxsets < 0)
+ {
+ CMusicDatabase db;
+ if (db.Open())
+ {
+ m_libraryHasBoxsets = (db.GetBoxsetsCount() > 0) ? 1 : 0;
+ db.Close();
+ }
+ }
+ value = m_libraryHasBoxsets > 0;
+ return true;
+ }
case LIBRARY_HAS_VIDEO:
{
return (GetBool(value, gitem, contextWindow, CGUIInfo(LIBRARY_HAS_MOVIES)) ||
diff --git a/xbmc/guilib/guiinfo/LibraryGUIInfo.h b/xbmc/guilib/guiinfo/LibraryGUIInfo.h
index d068d37ea7eec..a9d47fc13a66a 100644
--- a/xbmc/guilib/guiinfo/LibraryGUIInfo.h
+++ b/xbmc/guilib/guiinfo/LibraryGUIInfo.h
@@ -47,6 +47,7 @@ class CLibraryGUIInfo : public CGUIInfoProvider
mutable int m_libraryHasMovieSets;
mutable int m_libraryHasSingles;
mutable int m_libraryHasCompilations;
+ mutable int m_libraryHasBoxsets;
//Count of artists in music library contributing to song by role e.g. composers, conductors etc.
//For checking visibility of custom nodes for a role.
diff --git a/xbmc/guilib/guiinfo/MusicGUIInfo.cpp b/xbmc/guilib/guiinfo/MusicGUIInfo.cpp
index 84b61babe412f..8ad343c27bea4 100644
--- a/xbmc/guilib/guiinfo/MusicGUIInfo.cpp
+++ b/xbmc/guilib/guiinfo/MusicGUIInfo.cpp
@@ -142,6 +142,10 @@ bool CMusicGUIInfo::GetLabel(std::string& value, const CFileItem *item, int cont
return true;
}
break;
+ case MUSICPLAYER_DISC_TITLE:
+ case LISTITEM_DISC_TITLE:
+ value = tag->GetDiscSubtitle();
+ return true;
case MUSICPLAYER_ARTIST:
case LISTITEM_ARTIST:
value = tag->GetArtistString();
diff --git a/xbmc/interfaces/json-rpc/AudioLibrary.cpp b/xbmc/interfaces/json-rpc/AudioLibrary.cpp
index bcad21ee60570..b7a926f7db3a7 100644
--- a/xbmc/interfaces/json-rpc/AudioLibrary.cpp
+++ b/xbmc/interfaces/json-rpc/AudioLibrary.cpp
@@ -180,6 +180,9 @@ JSONRPC_STATUS CAudioLibrary::GetAlbums(const std::string &method, ITransportLay
if (parameterObject["includesingles"].asBoolean())
musicUrl.AddOption("show_singles", true);
+
+ if(parameterObject["boxset"].asBoolean())
+ musicUrl.AddOption("boxset", true);
bool allroles = false;
if (parameterObject["allroles"].isBoolean())
@@ -220,9 +223,9 @@ JSONRPC_STATUS CAudioLibrary::GetAlbums(const std::string &method, ITransportLay
std::set fields;
if (parameterObject.isMember("properties") && parameterObject["properties"].isArray())
{
- for (CVariant::const_iterator_array field = parameterObject["properties"].begin_array();
- field != parameterObject["properties"].end_array(); field++)
- fields.insert(field->asString());
+ for (CVariant::const_iterator_array field = parameterObject["properties"].begin_array();
+ field != parameterObject["properties"].end_array(); field++)
+ fields.insert(field->asString());
}
if (!musicdatabase.GetAlbumsByWhereJSON(fields, musicUrl.ToString(), result, total, sorting))
diff --git a/xbmc/interfaces/json-rpc/schema/methods.json b/xbmc/interfaces/json-rpc/schema/methods.json
index 833c3b026a30f..0c6a02ebc83f9 100644
--- a/xbmc/interfaces/json-rpc/schema/methods.json
+++ b/xbmc/interfaces/json-rpc/schema/methods.json
@@ -785,7 +785,8 @@
]
},
{ "name": "includesingles", "type": "boolean", "default": false },
- { "name": "allroles", "type": "boolean", "default":false, "description": "Whether or not to include all roles when filtering by artist, rather than the default of excluding other contributions. When true it overrides any role filter value." }
+ { "name": "allroles", "type": "boolean", "default":false, "description": "Whether or not to include all roles when filtering by artist, rather than the default of excluding other contributions. When true it overrides any role filter value." },
+ { "name": "boxset", "type": "boolean", "default": false, "description": "When true only returns albums that are also boxsets"}
],
"returns": {
"type": "object",
diff --git a/xbmc/interfaces/json-rpc/schema/types.json b/xbmc/interfaces/json-rpc/schema/types.json
index 17282f61c8594..0d3e579089344 100644
--- a/xbmc/interfaces/json-rpc/schema/types.json
+++ b/xbmc/interfaces/json-rpc/schema/types.json
@@ -505,7 +505,7 @@
"description": "Requesting the songgenres, artistid and/or sourceid fields will result in increased response times",
"enum": [ "title", "description", "artist", "genre",
"theme", "mood", "style", "type", "albumlabel",
- "rating", "votes", "userrating","year", "musicbrainzalbumid",
+ "rating", "votes", "userrating","year", "boxset", "musicbrainzalbumid",
"musicbrainzalbumartistid", "fanart", "thumbnail",
"playcount", "artistid", "displayartist",
"compilation", "releasetype", "dateadded",
@@ -581,6 +581,7 @@
"title": { "type": "string" },
"artist": { "$ref": "Array.String" },
"year": { "type": "integer" },
+ "boxset" : {"type": "integer" },
"rating": { "type": "number" },
"musicbrainzalbumartistid": { "$ref": "Array.String" },
"artistid": { "$ref": "Array.Integer" },
@@ -628,6 +629,7 @@
"albumlabel": { "type": "string" },
"playcount": { "type": "integer" },
"compilation": { "type": "boolean" },
+ "boxset": {"type": "boolean" },
"releasetype": { "$ref": "Audio.Album.ReleaseType" },
"musicbrainzreleasegroupid": { "type": "string" },
"musicbrainzalbumid": { "type": "string" },
@@ -1476,6 +1478,7 @@
"specialsortseason": { "type": "integer" },
"specialsortepisode": { "type": "integer" },
"compilation": { "type": "boolean" },
+ "boxset": { "type": "boolean" },
"releasetype": { "$ref": "Audio.Album.ReleaseType" },
"albumreleasetype": { "$ref": "Audio.Album.ReleaseType" },
"contributors": { "$ref": "Audio.Contributors" },
@@ -1503,7 +1506,7 @@
"description", "theme", "mood", "style", "albumlabel", "sorttitle",
"episodeguide", "uniqueid", "dateadded", "channel", "channeltype", "hidden",
"locked", "channelnumber", "starttime", "endtime", "specialsortseason",
- "specialsortepisode", "compilation", "releasetype", "albumreleasetype",
+ "specialsortepisode", "compilation", "boxset", "releasetype", "albumreleasetype",
"contributors", "displaycomposer", "displayconductor", "displayorchestra", "displaylyricist",
"userrating", "votes", "sortartist", "musicbrainzreleasegroupid", "mediapath", "dynpath"]
}
@@ -1523,7 +1526,7 @@
"List.Fields.Files": {
"extends": "Item.Fields.Base",
"items": { "type": "string",
- "enum": [ "title", "artist", "albumartist", "genre", "year", "rating",
+ "enum": [ "title", "artist", "albumartist", "genre", "year", "boxset", "rating",
"album", "track", "duration", "comment", "lyrics", "musicbrainztrackid",
"musicbrainzartistid", "musicbrainzalbumid", "musicbrainzalbumartistid",
"playcount", "fanart", "director", "trailer", "tagline", "plot",
diff --git a/xbmc/music/Album.cpp b/xbmc/music/Album.cpp
index 9e9c1cc876415..f720d3e949ce4 100644
--- a/xbmc/music/Album.cpp
+++ b/xbmc/music/Album.cpp
@@ -54,6 +54,8 @@ CAlbum::CAlbum(const CFileItem& item)
strType = tag.GetMusicBrainzReleaseType();
bCompilation = tag.GetCompilation();
iTimesPlayed = 0;
+ bBoxedSet = tag.GetBoxset();
+
dateAdded.Reset();
lastPlayed.Reset();
releaseType = tag.GetAlbumReleaseType();
@@ -292,6 +294,8 @@ void CAlbum::MergeScrapedAlbum(const CAlbum& source, bool override /* = true */)
if (override)
bCompilation = source.bCompilation;
// iTimesPlayed = source.iTimesPlayed; // times played is derived from songs
+ if (override)
+ bBoxedSet = source.bBoxedSet;
if ((override && !source.strArtistSort.empty()) || strArtistSort.empty())
strArtistSort = source.strArtistSort;
@@ -480,6 +484,7 @@ bool CAlbum::Load(const TiXmlElement *album, bool append, bool prioritise)
XMLUtils::GetStringArray(album, "mood", moods, prioritise, itemSeparator);
XMLUtils::GetStringArray(album, "theme", themes, prioritise, itemSeparator);
XMLUtils::GetBoolean(album, "compilation", bCompilation);
+ XMLUtils::GetBoolean(album, "boxset", bBoxedSet);
XMLUtils::GetString(album,"review",strReview);
XMLUtils::GetString(album,"releasedate",m_strDateOfRelease);
@@ -594,6 +599,7 @@ bool CAlbum::Save(TiXmlNode *node, const std::string &tag, const std::string& st
XMLUtils::SetStringArray(album, "mood", moods);
XMLUtils::SetStringArray(album, "theme", themes);
XMLUtils::SetBoolean(album, "compilation", bCompilation);
+ XMLUtils::SetBoolean(album, "boxset", bBoxedSet);
XMLUtils::SetString(album, "review", strReview);
XMLUtils::SetString(album, "type", strType);
@@ -637,4 +643,3 @@ bool CAlbum::Save(TiXmlNode *node, const std::string &tag, const std::string& st
return true;
}
-
diff --git a/xbmc/music/Album.h b/xbmc/music/Album.h
index 875addf26d318..35bb20eab3b4c 100644
--- a/xbmc/music/Album.h
+++ b/xbmc/music/Album.h
@@ -55,6 +55,7 @@ class CAlbum
iUserrating = -1;
iVotes = -1;
iYear = -1;
+ bBoxedSet = false;
bCompilation = false;
iTimesPlayed = 0;
dateAdded.Reset();
@@ -155,6 +156,7 @@ class CAlbum
int iUserrating = -1;
int iVotes = -1;
int iYear = -1;
+ bool bBoxedSet = false;
bool bCompilation = false;
int iTimesPlayed = 0;
CDateTime dateAdded;
diff --git a/xbmc/music/GUIViewStateMusic.cpp b/xbmc/music/GUIViewStateMusic.cpp
index 67b89a0ec5a98..b76df3142d887 100644
--- a/xbmc/music/GUIViewStateMusic.cpp
+++ b/xbmc/music/GUIViewStateMusic.cpp
@@ -162,6 +162,7 @@ CGUIViewStateMusicDatabase::CGUIViewStateMusicDatabase(const CFileItemList& item
case NODE_TYPE_ALBUM_COMPILATIONS:
case NODE_TYPE_ALBUM:
case NODE_TYPE_YEAR_ALBUM:
+ case NODE_TYPE_BOXSETS:
{
// album
AddSortMethod(SortByAlbum, sortAttribute, 558, LABEL_MASKS("%F", "", strAlbum, "%A")); // Filename, empty | Userdefined (default=%B), Artist
@@ -255,6 +256,7 @@ CGUIViewStateMusicDatabase::CGUIViewStateMusicDatabase(const CFileItemList& item
case NODE_TYPE_ALBUM_TOP100_SONGS:
case NODE_TYPE_YEAR_SONG:
case NODE_TYPE_SONG:
+ case NODE_TYPE_BOXSET_DISC_SONGS:
{
AddSortMethod(SortByTrackNumber, 554, LABEL_MASKS(strTrack, "%D")); // Userdefined, Duration| empty, empty
AddSortMethod(SortByTitle, sortAttribute, 556, LABEL_MASKS("%T - %A", "%D")); // Title, Artist, Duration| empty, empty
diff --git a/xbmc/music/MusicDatabase.cpp b/xbmc/music/MusicDatabase.cpp
index 4e6d4d5a7540d..2c67b2704e9df 100644
--- a/xbmc/music/MusicDatabase.cpp
+++ b/xbmc/music/MusicDatabase.cpp
@@ -142,6 +142,7 @@ void CMusicDatabase::CreateTables()
" strReleaseGroupMBID text, "
" strArtistDisp text, strArtistSort text, strGenres text, "
" iYear integer, "
+ " bBoxedSet INTEGER NOT NULL DEFAULT 0, "
" bCompilation integer not null default '0', "
" strMoods text, strStyles text, strThemes text, "
" strReview text, strImage text, strLabel text, "
@@ -183,7 +184,7 @@ void CMusicDatabase::CreateTables()
" idAlbum integer, idPath integer, "
" strArtistDisp text, strArtistSort text, strGenres text, strTitle varchar(512), "
" iTrack integer, iDuration integer, iYear integer, "
- " strFileName text, strMusicBrainzTrackID text, "
+ " strDiscSubtitle text, strFileName text, strMusicBrainzTrackID text, "
" iTimesPlayed integer, iStartOffset integer, iEndOffset integer, "
" lastplayed varchar(20) default NULL, "
" rating FLOAT NOT NULL DEFAULT 0, votes INTEGER NOT NULL DEFAULT 0, "
@@ -300,6 +301,7 @@ void CMusicDatabase::CreateViews()
" strTitle, "
" iTrack, iDuration, "
" song.iYear AS iYear, "
+ " song.strDiscSubtitle as strDiscSubtitle, "
" strFileName, "
" strMusicBrainzTrackID, "
" iTimesPlayed, iStartOffset, iEndOffset, "
@@ -312,6 +314,7 @@ void CMusicDatabase::CreateViews()
" strAlbum, "
" strPath, "
" album.bCompilation AS bCompilation,"
+ " album.bBoxedSet AS bBoxedSet, "
" album.strArtistDisp AS strAlbumArtists,"
" album.strArtistSort AS strAlbumArtistSort,"
" album.strReleaseType AS strAlbumReleaseType,"
@@ -334,6 +337,7 @@ void CMusicDatabase::CreateViews()
" album.strArtistSort AS strArtistSort, "
" album.strGenres AS strGenres, "
" album.iYear AS iYear, "
+ " album.bBoxedSet AS bBoxedSet, "
" album.strMoods AS strMoods, "
" album.strStyles AS strStyles, "
" strThemes, "
@@ -423,6 +427,7 @@ bool CMusicDatabase::AddAlbum(CAlbum& album, int idSource)
album.GetAlbumArtistSort(),
album.GetGenreString(),
album.iYear,
+ album.bBoxedSet,
album.strLabel, album.strType,
album.bCompilation, album.releaseType);
@@ -450,6 +455,7 @@ bool CMusicDatabase::AddAlbum(CAlbum& album, int idSource)
song->GetArtistSort(),
song->genre,
song->iTrack, song->iDuration, song->iYear,
+ song->strDiscSubtitle,
song->iTimesPlayed, song->iStartOffset,
song->iEndOffset,
song->lastPlayed,
@@ -511,7 +517,8 @@ bool CMusicDatabase::UpdateAlbum(CAlbum& album)
album.strReview,
album.thumbURL.m_xml.c_str(),
album.strLabel, album.strType,
- album.fRating, album.iUserrating, album.iVotes, album.iYear, album.bCompilation, album.releaseType,
+ album.fRating, album.iUserrating, album.iVotes, album.iYear, album.bBoxedSet,
+ album.bCompilation, album.releaseType,
album.bScrapedMBID);
if (!album.bArtistSongMerge)
@@ -557,6 +564,7 @@ int CMusicDatabase::AddSong(const int idAlbum,
const std::string &artistDisp, const std::string &artistSort,
const std::vector& genres,
int iTrack, int iDuration, int iYear,
+ const std::string& strDiscSubtitle,
const int iTimesPlayed, int iStartOffset, int iEndOffset,
const CDateTime& dtLastPlayed, float rating, int userrating, int votes,
const ReplayGain& replayGain)
@@ -596,16 +604,17 @@ int CMusicDatabase::AddSong(const int idAlbum,
m_pDS->close();
strSQL=PrepareSQL("INSERT INTO song ("
"idSong,idAlbum,idPath,strArtistDisp,"
- "strTitle,iTrack,iDuration,iYear,strFileName,"
+ "strTitle,iTrack,iDuration,iYear, strDiscSubtitle, strFileName,"
"strMusicBrainzTrackID, strArtistSort, "
"iTimesPlayed,iStartOffset, "
"iEndOffset,lastplayed,rating,userrating,votes,comment,mood,strReplayGain"
- ") values (NULL, %i, %i, '%s', '%s', %i, %i, %i, '%s'",
+ ") values (NULL, %i, %i, '%s', '%s', %i, %i, %i,'%s', '%s'",
idAlbum,
idPath,
artistDisp.c_str(),
strTitle.c_str(),
iTrack, iDuration, iYear,
+ strDiscSubtitle.c_str(),
strFileName.c_str());
if (strMusicBrainzTrackID.empty())
@@ -632,7 +641,7 @@ int CMusicDatabase::AddSong(const int idAlbum,
idSong = m_pDS->fv("idSong").get_asInt();
m_pDS->close();
UpdateSong( idSong, strTitle, strMusicBrainzTrackID, strPathAndFileName, strComment, strMood, strThumb,
- artistDisp, artistSort, genres, iTrack, iDuration, iYear, iTimesPlayed, iStartOffset, iEndOffset,
+ artistDisp, artistSort, genres, iTrack, iDuration, iYear, strDiscSubtitle, iTimesPlayed, iStartOffset, iEndOffset,
dtLastPlayed, rating, userrating, votes, replayGain);
}
@@ -716,6 +725,7 @@ bool CMusicDatabase::UpdateSong(CSong& song, bool bArtists /*= true*/)
song.iTrack,
song.iDuration,
song.iYear,
+ song.strDiscSubtitle,
song.iTimesPlayed,
song.iStartOffset,
song.iEndOffset,
@@ -760,6 +770,7 @@ int CMusicDatabase::UpdateSong(int idSong,
const std::string &artistDisp, const std::string &artistSort,
const std::vector& genres,
int iTrack, int iDuration, int iYear,
+ const std::string& strDiscSubtitle,
int iTimesPlayed, int iStartOffset, int iEndOffset,
const CDateTime& dtLastPlayed, float rating, int userrating, int votes,
const ReplayGain& replayGain)
@@ -773,12 +784,13 @@ int CMusicDatabase::UpdateSong(int idSong,
int idPath = AddPath(strPath);
strSQL = PrepareSQL("UPDATE song SET idPath = %i, strArtistDisp = '%s', strGenres = '%s', "
- " strTitle = '%s', iTrack = %i, iDuration = %i, iYear = %i, strFileName = '%s'",
+ " strTitle = '%s', iTrack = %i, iDuration = %i, iYear = %i, strDiscSubtitle = '%s', strFileName = '%s'",
idPath,
artistDisp.c_str(),
StringUtils::Join(genres, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicItemSeparator).c_str(),
strTitle.c_str(),
iTrack, iDuration, iYear,
+ strDiscSubtitle.c_str(),
strFileName.c_str());
if (strMusicBrainzTrackID.empty())
strSQL += PrepareSQL(", strMusicBrainzTrackID = NULL");
@@ -810,7 +822,7 @@ int CMusicDatabase::UpdateSong(int idSong,
int CMusicDatabase::AddAlbum(const std::string& strAlbum, const std::string& strMusicBrainzAlbumID,
const std::string& strReleaseGroupMBID,
const std::string& strArtist, const std::string& strArtistSort,
- const std::string& strGenre, int year,
+ const std::string& strGenre, int year, bool bBoxedSet,
const std::string& strRecordLabel, const std::string& strType,
bool bCompilation, CAlbum::ReleaseType releaseType)
{
@@ -833,13 +845,14 @@ int CMusicDatabase::AddAlbum(const std::string& strAlbum, const std::string& str
{
m_pDS->close();
// doesnt exists, add it
- strSQL = PrepareSQL("INSERT INTO album (idAlbum, strAlbum, strArtistDisp, strGenres, iYear, "
+ strSQL = PrepareSQL("INSERT INTO album (idAlbum, strAlbum, strArtistDisp, strGenres, iYear, bBoxedSet, "
"strLabel, strType, bCompilation, strReleaseType, strMusicBrainzAlbumID, strReleaseGroupMBID, strArtistSort) "
- "values(NULL, '%s', '%s', '%s', %i, '%s', '%s', %i, '%s'",
+ "values(NULL, '%s', '%s', '%s', %i, %i, '%s', '%s', %i, '%s'",
strAlbum.c_str(),
strArtist.c_str(),
strGenre.c_str(),
year,
+ bBoxedSet,
strRecordLabel.c_str(),
strType.c_str(),
bCompilation,
@@ -888,11 +901,12 @@ int CMusicDatabase::AddAlbum(const std::string& strAlbum, const std::string& str
else
strSQL += PrepareSQL(" strArtistSort = '%s'", strArtistSort.c_str());
- strSQL += PrepareSQL(", strGenres = '%s', iYear=%i, strLabel = '%s', strType = '%s', "
+ strSQL += PrepareSQL(", strGenres = '%s', iYear=%i, bBoxedSet=%i, strLabel = '%s', strType = '%s', "
"bCompilation=%i, strReleaseType = '%s', lastScraped = NULL "
"WHERE idAlbum=%i",
strGenre.c_str(),
year,
+ bBoxedSet,
strRecordLabel.c_str(),
strType.c_str(),
bCompilation,
@@ -921,7 +935,8 @@ int CMusicDatabase::UpdateAlbum(int idAlbum,
const std::string& strThemes, const std::string& strReview,
const std::string& strImage, const std::string& strLabel,
const std::string& strType,
- float fRating, int iUserrating, int iVotes, int iYear, bool bCompilation,
+ float fRating, int iUserrating, int iVotes, int iYear, bool bBoxedSet,
+ bool bCompilation,
CAlbum::ReleaseType releaseType,
bool bScrapedMBID)
{
@@ -934,13 +949,13 @@ int CMusicDatabase::UpdateAlbum(int idAlbum,
" strMoods = '%s', strStyles = '%s', strThemes = '%s', "
" strReview = '%s', strImage = '%s', strLabel = '%s', "
" strType = '%s', fRating = %f, iUserrating = %i, iVotes = %i,"
- " iYear = %i, bCompilation = %i, strReleaseType = '%s', "
+ " iYear = %i, bBoxedSet = %i, bCompilation = %i, strReleaseType = '%s', "
" lastScraped = '%s', bScrapedMBID = %i",
strAlbum.c_str(), strArtist.c_str(), strGenre.c_str(),
strMoods.c_str(), strStyles.c_str(), strThemes.c_str(),
strReview.c_str(), strImage.c_str(), strLabel.c_str(),
strType.c_str(), fRating, iUserrating, iVotes,
- iYear, bCompilation,
+ iYear, bBoxedSet, bCompilation,
CAlbum::ReleaseTypeToString(releaseType).c_str(),
CDateTime::GetCurrentDateTime().GetAsDBDateTime().c_str(),
bScrapedMBID);
@@ -1923,6 +1938,35 @@ bool CMusicDatabase::GetArtistsBySong(int idSong, std::vector &artists)
return false;
}
+bool CMusicDatabase::GetSongByDiscSubtitleAndAlbum(const std::string& strDiscSubtitle, int idAlbum)
+{
+ try
+ {
+ std::string strSQL;
+ strSQL = PrepareSQL("SELECT idSong FROM song WHERE strDiscSubtitle = '%s' AND idAlbum = %i", strDiscSubtitle, idAlbum);
+ if (!m_pDS->query(strSQL))
+ return false;
+ if (m_pDS->num_rows() == 0)
+ {
+ m_pDS->close();
+ return false;
+ }
+
+ while (!m_pDS->eof())
+ {
+// songs.push_back(m_pDS->fv("idSong").get_asInt());
+ m_pDS->next();
+ }
+ m_pDS->close();
+ return true;
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "%s(%i, %s) failed", __FUNCTION__, idAlbum, strDiscSubtitle);
+ }
+ return false;
+}
+
bool CMusicDatabase::GetGenresByArtist(int idArtist, CFileItem* item)
{
try
@@ -2132,6 +2176,7 @@ CSong CMusicDatabase::GetSongFromDataset(const dbiplus::sql_record* const record
song.iTrack = record->at(offset + song_iTrack).get_asInt() ;
song.iDuration = record->at(offset + song_iDuration).get_asInt() ;
song.iYear = record->at(offset + song_iYear).get_asInt() ;
+ song.strDiscSubtitle = record->at(offset + song_strDiscSubtitle).get_asString();
song.strTitle = record->at(offset + song_strTitle).get_asString();
song.iTimesPlayed = record->at(offset + song_iTimesPlayed).get_asInt();
song.lastPlayed.SetFromDBDateTime(record->at(offset + song_lastplayed).get_asString());
@@ -2175,6 +2220,7 @@ void CMusicDatabase::GetFileItemFromDataset(const dbiplus::sql_record* const rec
stTime.wYear = static_cast(record->at(song_iYear).get_asInt());
item->GetMusicInfoTag()->SetReleaseDate(stTime);
item->GetMusicInfoTag()->SetTitle(record->at(song_strTitle).get_asString());
+ item->GetMusicInfoTag()->SetDiscSubtitle(record->at(song_strDiscSubtitle).get_asString());
item->SetLabel(record->at(song_strTitle).get_asString());
item->m_lStartOffset = record->at(song_iStartOffset).get_asInt64();
item->SetProperty("item_start", item->m_lStartOffset);
@@ -2191,6 +2237,7 @@ void CMusicDatabase::GetFileItemFromDataset(const dbiplus::sql_record* const rec
std::string strRealPath = URIUtils::AddFileToFolder(record->at(song_strPath).get_asString(), record->at(song_strFileName).get_asString());
item->GetMusicInfoTag()->SetURL(strRealPath);
item->GetMusicInfoTag()->SetCompilation(record->at(song_bCompilation).get_asInt() == 1);
+ item->GetMusicInfoTag()->SetBoxset(record->at(song_bBoxedSet).get_asInt() == 1);
// get the album artist string from songview (not the album_artist and artist tables)
item->GetMusicInfoTag()->SetAlbumArtist(record->at(song_strAlbumArtists).get_asString());
item->GetMusicInfoTag()->SetAlbumReleaseType(CAlbum::ReleaseTypeFromString(record->at(song_strAlbumReleaseType).get_asString()));
@@ -2270,6 +2317,7 @@ CAlbum CMusicDatabase::GetAlbumFromDataset(const dbiplus::sql_record* const reco
album.iUserrating = record->at(offset + album_iUserrating).get_asInt();
album.iVotes = record->at(offset + album_iVotes).get_asInt();
album.iYear = record->at(offset + album_iYear).get_asInt();
+ album.bBoxedSet = record->at(offset + album_bBoxedSet).get_asInt() == 1;
album.strReview = record->at(offset + album_strReview).get_asString();
album.styles = StringUtils::Split(record->at(offset + album_strStyles).get_asString(), itemSeparator);
album.moods = StringUtils::Split(record->at(offset + album_strMoods).get_asString(), itemSeparator);
@@ -5515,6 +5563,7 @@ static const translateJSONField JSONtoDBAlbum[] = {
{ "votes", "integer", true, "iVotes", "" },
{ "userrating", "unsigned", true, "iUserrating", "" },
{ "year", "integer", true, "iYear", "" },
+ { "boxset", "boolean", true, "bBoxedSet", "" },
{ "musicbrainzalbumid", "string", true, "strMusicBrainzAlbumID", "" },
{ "displayartist", "string", true, "strArtists", "" }, //strArtistDisp in album table
{ "compilation", "boolean", true, "bCompilation", "" },
@@ -7407,7 +7456,12 @@ void CMusicDatabase::UpdateTables(int version)
// and filled as part of scanning anyway so simply force full rescan.
MigrateSources();
}
-
+ if (version < 73)
+ {
+ // add bBoxedSet to album table, add strDiscSubtitles to song table
+ m_pDS->exec("ALTER TABLE album ADD bBoxedSet INTEGER \n");
+ m_pDS->exec("ALTER TABLE song ADD strDiscSubtitle TEXT \n");
+ }
// Set the verion of tag scanning required.
// Not every schema change requires the tags to be rescanned, set to the highest schema version
// that needs this. Forced rescanning (of music files that have not changed since they were
@@ -7416,9 +7470,9 @@ void CMusicDatabase::UpdateTables(int version)
// The original db version when the tags were scanned, and the minimal db version needed are
// later used to determine if a forced rescan should be prompted
- // The last schema change needing forced rescanning was 60.
- // Mostly because of the new tags processed by v17 rather than a schema change.
- SetMusicNeedsTagScan(60);
+ // The last schema change needing forced rescanning was 73.
+ // This is because Kodi can now read and process extra tags involved in the creation of box sets.
+ SetMusicNeedsTagScan(73);
// After all updates, store the original db version.
// This indicates the version of tag processing that was used to populate db
@@ -7427,7 +7481,7 @@ void CMusicDatabase::UpdateTables(int version)
int CMusicDatabase::GetSchemaVersion() const
{
- return 72;
+ return 73;
}
int CMusicDatabase::GetMusicNeedsTagScan()
@@ -8888,6 +8942,138 @@ bool CMusicDatabase::GetGenresJSON(CFileItemList& items, bool bSources)
return false;
}
+bool CMusicDatabase::GetBoxsetsAlbums(const std::string& strBaseDir, CFileItemList& items)
+{
+ CMusicDbUrl musicUrl;
+
+ if (!musicUrl.FromString(strBaseDir))
+ return false;
+
+ musicUrl.AddOption("boxset", true);
+
+ Filter filter;
+ return GetAlbumsByWhere(musicUrl.ToString(), filter, items);
+}
+
+bool CMusicDatabase::GetBoxsetDiscs(const std::string& strBaseDir, CFileItemList& items, int idAlbum, const SortDescription &sortDescription)
+{
+ // Need to get the single album - must only be one album holding a boxset
+ // then split the disc names from strDiscTitles, count how many there are
+ // and loop around that many times to create a view with 'i' number of boxes,
+ // titled by the subtitles
+
+ try
+ {
+ if (NULL == m_pDB.get()) return false;
+ if (NULL == m_pDS.get()) return false;
+ std::string strSQL;
+ strSQL=PrepareSQL("SELECT DISTINCT strDiscSubtitle FROM song where song.idAlbum = %i AND strDiscSubtitle != '' ORDER BY iTrack", idAlbum);
+ // run query
+ if (!m_pDS->query(strSQL)) return false;
+ int iRowsFound = m_pDS->num_rows();
+ if (iRowsFound < 2) // can't only be one disc title for a set
+ {
+ m_pDS->close();
+ return -1;
+ }
+ std::vector titlevector;
+ while (!m_pDS->eof())
+ {
+ titlevector.push_back(m_pDS->fv("strDiscSubtitle").get_asString().c_str());
+ m_pDS->next();
+ }
+ m_pDS->close();
+ items.SetProperty("total", iRowsFound);
+
+ CMusicDbUrl musicUrl;
+ std::string temp_path;
+ temp_path = "musicdb://boxsets/";
+ musicUrl.FromString(strBaseDir);
+ musicUrl.RemoveOption("boxset");
+ temp_path += StringUtils::Format("%i/", idAlbum);
+ strSQL=PrepareSQL("SELECT albumview.* FROM albumview WHERE albumview.idAlbum = %i", idAlbum);
+ if (!m_pDS->query(strSQL)) return false;
+ int iAlbumsFound = m_pDS->num_rows();
+ if (iAlbumsFound != 1) // must only be one album
+ {
+ m_pDS->close();
+ return -1;
+ }
+ DatabaseResults results;
+ results.reserve(iAlbumsFound);
+ if (!SortUtils::SortFromDataset(sortDescription, MediaTypeSong, m_pDS, results))
+ return false;
+ const dbiplus::sql_record* const record = m_pDS->get_sql_record();
+ CMusicDbUrl itemUrl;
+ new CFileItem(itemUrl.ToString(), GetAlbumFromDataset(record));
+
+ for (const auto &disctitle : titlevector) // loop to construct the discs for the gui
+ {
+ CFileItemPtr pItem(new CFileItem(itemUrl.ToString() , GetAlbumFromDataset(record)));
+ std::string path;
+ path = temp_path;
+ path += StringUtils::Format("%s/", disctitle.c_str());
+ musicUrl.FromString(path);
+ pItem->SetPath(path);
+ pItem->SetLabel(disctitle.c_str());
+ pItem->SetArt("icon", "DefaultAlbumCover.png");
+ items.Add(pItem);
+ }
+ }
+ catch (...)
+ {
+ m_pDS->close();
+ CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
+ }
+ m_pDS->close();
+ return true;
+}
+
+bool CMusicDatabase::GetBoxsetDiscSongs(const std::string& strBaseDir, CFileItemList& items)
+{
+ CMusicDbUrl musicUrl;
+ Filter filter;
+ SortDescription sorting;
+ std::string buildUrl;
+ std::string tmp;
+ std::string strDiscName;
+ unsigned int start;
+ unsigned int end;
+ int idAlbum;
+
+ if (!musicUrl.FromString(strBaseDir))
+ {
+ CLog::Log(LOGERROR, "%s - error getting musicUrl from [%s]", __FUNCTION__, strBaseDir.c_str());
+ return false;
+ }
+ start = strBaseDir.find("boxsets/");
+ if (start == std::string::npos)
+ return false;
+
+ end = strBaseDir.find("/", start + 9); // 9 is 'boxsets/' + 1
+ tmp = strBaseDir.substr(start + 8, end - 18); // 8 = 'boxsets/', 18 = 'musicdb://boxsets/'
+ idAlbum = atoi(tmp.c_str());
+ start = end + 1; // set start to end of albumID field + '/'
+ end = strBaseDir.find("/", start + 1);
+ strDiscName = strBaseDir.substr(start, (strBaseDir.length() -1) - start);
+ if (idAlbum == -1)
+ return false;
+
+ buildUrl = "musicdb://boxsets/";
+ buildUrl += StringUtils::Format("%i/", idAlbum);
+ buildUrl += StringUtils::Format("%s/", strDiscName);
+ musicUrl.FromString(buildUrl);
+ musicUrl.AddOption("disctitle", strDiscName.c_str());
+ GetFilter(musicUrl, filter, sorting);
+ GetSongsByWhere(musicUrl.ToString(), filter, items);
+ return true;
+}
+
+int CMusicDatabase::GetBoxsetsCount()
+{
+ return strtol(GetSingleValue("album", "count(idAlbum)", "bBoxedSet = 1").c_str(), NULL, 10);
+}
+
bool CMusicDatabase::GetCompilationAlbums(const std::string& strBaseDir, CFileItemList& items)
{
CMusicDbUrl musicUrl;
@@ -10366,6 +10552,8 @@ void CMusicDatabase::SetPropertiesFromAlbum(CFileItem& item, const CAlbum& album
item.SetProperty("album_userrating", album.iUserrating);
if (album.iVotes > 0)
item.SetProperty("album_votes", album.iVotes);
+if (album.bBoxedSet )
+ item.SetProperty("album_bBoxedSet", true);
item.SetProperty("album_releasetype", CAlbum::ReleaseTypeToString(album.releaseType));
}
@@ -10932,6 +11120,10 @@ bool CMusicDatabase::GetFilter(CDbUrl &musicUrl, Filter &filter, SortDescription
if (option != options.end())
filter.AppendWhere(PrepareSQL("albumview.bCompilation = %i", option->second.asBoolean() ? 1 : 0));
+ option = options.find("boxset");
+ if (option != options.end())
+ filter.AppendWhere(PrepareSQL("albumview.bBoxedSet = %i", option->second.asBoolean() ? 1 : 0));
+
if (idSource > 0)
filter.AppendWhere(PrepareSQL("EXISTS(SELECT 1 FROM album_source "
"WHERE album_source.idAlbum = albumview.idAlbum AND album_source.idSource = %i)", idSource));
@@ -11035,6 +11227,10 @@ bool CMusicDatabase::GetFilter(CDbUrl &musicUrl, Filter &filter, SortDescription
if (option != options.end())
filter.AppendWhere(PrepareSQL("songview.bCompilation = %i", option->second.asBoolean() ? 1 : 0));
+ option = options.find("disctitle");
+ if (option != options.end())
+ filter.AppendWhere(PrepareSQL("songview.strDiscSubtitle = '%s'", option->second.asString().c_str()));
+
if (idSong > 0)
filter.AppendWhere(PrepareSQL("songview.idSong = %i", idSong));
diff --git a/xbmc/music/MusicDatabase.h b/xbmc/music/MusicDatabase.h
index bcd9e4ccc3f50..a3fbbf91b00ac 100644
--- a/xbmc/music/MusicDatabase.h
+++ b/xbmc/music/MusicDatabase.h
@@ -128,6 +128,7 @@ class CMusicDatabase : public CDatabase
\param iTrack [in] the track number and disc number of the song
\param iDuration [in] the duration of the song
\param iYear [in] the year of the song
+ \param strDiscSubtitle [in] subtitle of a disc if it belongs to a box-set
\param iTimesPlayed [in] the number of times the song has been played
\param iStartOffset [in] the start offset of the song (when using a single audio file with a .cue)
\param iEndOffset [in] the end offset of the song (when using a single audio file with .cue)
@@ -148,6 +149,7 @@ class CMusicDatabase : public CDatabase
const std::string &artistDisp, const std::string &artistSort,
const std::vector& genres,
int iTrack, int iDuration, int iYear,
+ const std::string& strDiscSubtitle,
const int iTimesPlayed, int iStartOffset, int iEndOffset,
const CDateTime& dtLastPlayed, float rating, int userrating, int votes,
const ReplayGain& replayGain);
@@ -174,6 +176,7 @@ class CMusicDatabase : public CDatabase
\param iTrack [in] the track number and disc number of the song
\param iDuration [in] the duration of the song
\param iYear [in] the year of the song
+ \param strDiscSubtitle [in] subtitle of a disc if it belongs to a box-set
\param iTimesPlayed [in] the number of times the song has been played
\param iStartOffset [in] the start offset of the song (when using a single audio file with a .cue)
\param iEndOffset [in] the end offset of the song (when using a single audio file with .cue)
@@ -191,6 +194,7 @@ class CMusicDatabase : public CDatabase
const std::string &artistDisp, const std::string &artistSort,
const std::vector& genres,
int iTrack, int iDuration, int iYear,
+ const std::string& strDiscSubtitle,
int iTimesPlayed, int iStartOffset, int iEndOffset,
const CDateTime& dtLastPlayed, float rating, int userrating, int votes, const ReplayGain& replayGain);
@@ -202,6 +206,7 @@ class CMusicDatabase : public CDatabase
bool SetSongUserrating(const std::string &filePath, int userrating);
bool SetSongUserrating(int idSong, int userrating);
bool SetSongVotes(const std::string &filePath, int votes);
+ bool GetSongByDiscSubtitleAndAlbum(const std::string& strDiscSubtitle, int idAlbum);
int GetSongByArtistAndAlbumAndTitle(const std::string& strArtist, const std::string& strAlbum, const std::string& strTitle);
/////////////////////////////////////////////////
@@ -227,6 +232,7 @@ class CMusicDatabase : public CDatabase
\param strArtistSort the album artist name(s) sort string
\param strGenre the album genre(s)
\param year the year
+ \param bBoxedSet if the album is a boxset
\param strRecordLabel the recording label
\param strType album type (Musicbrainz release type e.g. "Broadcast, Soundtrack, live"),
\param bCompilation if the album is a compilation
@@ -236,9 +242,9 @@ class CMusicDatabase : public CDatabase
int AddAlbum(const std::string& strAlbum, const std::string& strMusicBrainzAlbumID,
const std::string& strReleaseGroupMBID,
const std::string& strArtist, const std::string& strArtistSort,
- const std::string& strGenre, int year,
+ const std::string& strGenre, int year, bool bBoxedSet,
const std::string& strRecordLabel, const std::string& strType,
- bool bCompilation, CAlbum::ReleaseType releaseType);
+ bool bCompilation, CAlbum::ReleaseType releaseType );
/*! \brief retrieve an album, optionally with all songs.
\param idAlbum the database id of the album.
@@ -256,7 +262,8 @@ class CMusicDatabase : public CDatabase
const std::string& strThemes, const std::string& strReview,
const std::string& strImage, const std::string& strLabel,
const std::string& strType,
- float fRating, int iUserrating, int iVotes, int iYear, bool bCompilation,
+ float fRating, int iUserrating, int iVotes, int iYear, bool bBoxedSet,
+ bool bCompilation,
CAlbum::ReleaseType releaseType,
bool bScrapedMBID);
bool ClearAlbumLastScrapedTime(int idAlbum);
@@ -283,6 +290,7 @@ class CMusicDatabase : public CDatabase
std::string GetAlbumById(int id);
bool SetAlbumUserrating(const int idAlbum, int userrating);
+
/////////////////////////////////////////////////
// Artist CRUD
/////////////////////////////////////////////////
@@ -418,9 +426,18 @@ class CMusicDatabase : public CDatabase
bool GetCompilationAlbums(const std::string& strBaseDir, CFileItemList& items);
bool GetCompilationSongs(const std::string& strBaseDir, CFileItemList& items);
int GetCompilationAlbumsCount();
-
int GetSinglesCount();
+ ////////////////////////////////////////////////
+ // Boxsets
+ ////////////////////////////////////////////////
+ bool GetBoxsetsAlbums(const std::string& strBaseDir, CFileItemList& items);
+ bool GetBoxsetDiscs(const std::string& strBaseDir, CFileItemList& items, int idAlbum = -1, const SortDescription &sortDescription = SortDescription());
+ bool GetBoxsetDiscSongs(const std::string& strBaseDir, CFileItemList& items);
+ int GetBoxsetsCount();
+ bool RemoveBoxset(int albumId);
+////////////////////////////////////////////////
+
int GetArtistCountForRole(int role);
int GetArtistCountForRole(const std::string& strRole);
@@ -464,7 +481,7 @@ class CMusicDatabase : public CDatabase
unsigned int GetRandomSongIDs(const Filter &filter, std::vector > &songIDs);
/////////////////////////////////////////////////
- // JSON-RPC
+ // JSON-RPC
/////////////////////////////////////////////////
bool GetGenresJSON(CFileItemList& items, bool bSources = false);
bool GetArtistsByWhereJSON(const std::set& fields, const std::string& baseDir,
@@ -664,7 +681,7 @@ void SetLibraryLastUpdated();
void GetFileItemFromDataset(CFileItem* item, const CMusicDbUrl &baseUrl);
void GetFileItemFromDataset(const dbiplus::sql_record* const record, CFileItem* item, const CMusicDbUrl &baseUrl);
void GetFileItemFromArtistCredits(VECARTISTCREDITS& artistCredits, CFileItem* item);
-
+
bool CleanupSongs(CGUIDialogProgress* progressDialog = nullptr);
bool CleanupSongsByIds(const std::string &strSongIds);
bool CleanupPaths();
@@ -690,26 +707,26 @@ void SetLibraryLastUpdated();
\param sortAttributes the sort attributes e.g. SortAttributeIgnoreArticle
\param strField original name or title field that articles could be removed from
\param strSortField sort name or title field to be used instead of original (when data not null)
- \return SQL string e.g.
- CASE WHEN strArtistSort IS NOT NULL THEN strArtistSort
+ \return SQL string e.g.
+ CASE WHEN strArtistSort IS NOT NULL THEN strArtistSort
WHEN strField LIKE 'the ' OR strField LIKE 'the_' ESCAPE '_' THEN SUBSTR(strArtist, 5)
ELSE strField
END AS strAlias
*/
- std::string SortnameBuildSQL(const std::string& strAlias, const SortAttribute& sortAttributes,
+ std::string SortnameBuildSQL(const std::string& strAlias, const SortAttribute& sortAttributes,
const std::string& strField, const std::string& strSortField);
/*! \brief Build SQL for sorting field naturally and case insensitvely (in SQLite).
\param strField field name
\param sortOrder the sort order
- \return SQL string e.g.
- CASE WHEN CAST(strTitle AS INTEGER) = 0 THEN 100000000
+ \return SQL string e.g.
+ CASE WHEN CAST(strTitle AS INTEGER) = 0 THEN 100000000
ELSE CAST(strTitle AS INTEGER) END DESC, strTitle COLLATE NOCASE DESC
*/
std::string AlphanumericSortSQL(const std::string& strField, const SortOrder& sortOrder);
/*! \brief Checks that source table matches sources.xml
- returns true when they do
+ returns true when they do
*/
bool CheckSources(VECSOURCES& sources);
@@ -733,6 +750,7 @@ void SetLibraryLastUpdated();
song_iTrack,
song_iDuration,
song_iYear,
+ song_strDiscSubtitle,
song_strFileName,
song_strMusicBrainzTrackID,
song_iTimesPlayed,
@@ -747,6 +765,7 @@ void SetLibraryLastUpdated();
song_strAlbum,
song_strPath,
song_bCompilation,
+ song_bBoxedSet,
song_strAlbumArtists,
song_strAlbumArtistSort,
song_strAlbumReleaseType,
@@ -768,6 +787,7 @@ void SetLibraryLastUpdated();
album_strArtistSort,
album_strGenres,
album_iYear,
+ album_bBoxedSet,
album_strMoods,
album_strStyles,
album_strThemes,
@@ -869,7 +889,7 @@ void SetLibraryLastUpdated();
// Fields fetched by GetSongsByWhereJSON, order same as in JSONtoDBSong
static enum _JoinToSongFields
{
- // Used by GetSongsByWhereJSON
+ // Used by GetSongsByWhereJSON
joinToSongs_idAlbumArtist = 0,
joinToSongs_strAlbumArtist,
joinToSongs_strAlbumArtistMBID,
diff --git a/xbmc/music/MusicDbUrl.cpp b/xbmc/music/MusicDbUrl.cpp
index 181bccd3a0146..6d3f3505eaa06 100644
--- a/xbmc/music/MusicDbUrl.cpp
+++ b/xbmc/music/MusicDbUrl.cpp
@@ -44,6 +44,8 @@ bool CMusicDbUrl::parse()
case NODE_TYPE_ALBUM_TOP100:
case NODE_TYPE_ALBUM_COMPILATIONS:
case NODE_TYPE_YEAR_ALBUM:
+ case NODE_TYPE_BOXSETS:
+ case NODE_TYPE_BOXSET_DISCS:
m_type = "albums";
break;
@@ -51,6 +53,7 @@ bool CMusicDbUrl::parse()
case NODE_TYPE_ALBUM_RECENTLY_PLAYED_SONGS:
case NODE_TYPE_ALBUM_TOP100_SONGS:
case NODE_TYPE_ALBUM_COMPILATIONS_SONGS:
+ case NODE_TYPE_BOXSET_DISC_SONGS:
case NODE_TYPE_SONG:
case NODE_TYPE_SONG_TOP100:
case NODE_TYPE_YEAR_SONG:
@@ -73,14 +76,19 @@ bool CMusicDbUrl::parse()
case NODE_TYPE_ALBUM_RECENTLY_PLAYED:
case NODE_TYPE_ALBUM_TOP100:
case NODE_TYPE_YEAR_ALBUM:
+ case NODE_TYPE_BOXSETS:
+ case NODE_TYPE_BOXSET_DISCS:
m_type = "albums";
break;
+
+
case NODE_TYPE_SONG:
case NODE_TYPE_ALBUM_RECENTLY_ADDED_SONGS:
case NODE_TYPE_ALBUM_RECENTLY_PLAYED_SONGS:
case NODE_TYPE_ALBUM_TOP100_SONGS:
case NODE_TYPE_ALBUM_COMPILATIONS_SONGS:
+ case NODE_TYPE_BOXSET_DISC_SONGS:
case NODE_TYPE_SONG_TOP100:
case NODE_TYPE_YEAR_SONG:
case NODE_TYPE_SINGLES:
diff --git a/xbmc/music/Song.cpp b/xbmc/music/Song.cpp
index c2d6146f8616d..89ac9c570238a 100644
--- a/xbmc/music/Song.cpp
+++ b/xbmc/music/Song.cpp
@@ -57,6 +57,7 @@ CSong::CSong(CFileItem& item)
userrating = tag.GetUserrating();
votes = tag.GetVotes();
iYear = stTime.wYear;
+ strDiscSubtitle = tag.GetDiscSubtitle();
iTrack = tag.GetTrackAndDiscNumber();
iDuration = tag.GetDuration();
strRecordLabel = tag.GetRecordLabel();
@@ -221,6 +222,7 @@ void CSong::Serialize(CVariant& value) const
value["duration"] = iDuration;
value["track"] = iTrack;
value["year"] = iYear;
+ value['discsubtitle'] = strDiscSubtitle;
value["musicbrainztrackid"] = strMusicBrainzTrackID;
value["comment"] = strComment;
value["mood"] = strMood;
@@ -254,6 +256,7 @@ void CSong::Clear()
iTrack = 0;
iDuration = 0;
iYear = 0;
+ strDiscSubtitle.clear();
iStartOffset = 0;
iEndOffset = 0;
idSong = -1;
@@ -349,3 +352,9 @@ bool CSong::ArtMatches(const CSong &right) const
return (right.strThumb == strThumb &&
embeddedArt.Matches(right.embeddedArt));
}
+const std::string CSong::GetDiscSubtitle() const
+{
+ if (!strDiscSubtitle.empty())
+ return strDiscSubtitle;
+ return "";
+}
diff --git a/xbmc/music/Song.h b/xbmc/music/Song.h
index 8dde36f1420fd..ea52b7ccc8536 100644
--- a/xbmc/music/Song.h
+++ b/xbmc/music/Song.h
@@ -142,6 +142,8 @@ class CSong final : public ISerializable
*/
bool ArtMatches(const CSong &right) const;
+ const std::string GetDiscSubtitle() const;
+
/*! \brief Set artist credits using the arrays of tag values.
If strArtistSort (as from ARTISTSORT tag) is already set then individual
artist sort names are also processed.
@@ -173,6 +175,7 @@ class CSong final : public ISerializable
int iTrack;
int iDuration;
int iYear;
+ std::string strDiscSubtitle;
int iTimesPlayed;
CDateTime lastPlayed;
CDateTime dateAdded;
diff --git a/xbmc/music/infoscanner/MusicInfoScanner.cpp b/xbmc/music/infoscanner/MusicInfoScanner.cpp
index 862b0e4e1dce5..1bbd859d16540 100644
--- a/xbmc/music/infoscanner/MusicInfoScanner.cpp
+++ b/xbmc/music/infoscanner/MusicInfoScanner.cpp
@@ -300,7 +300,7 @@ void CMusicInfoScanner::Start(const std::string& strDirectory, int flags)
}
else
{
- m_pathsToScan.insert(strDirectory);
+ m_pathsToScan.insert(strDirectory);
m_idSourcePath = m_musicDatabase.GetSourceFromPath(strDirectory);
}
m_musicDatabase.Close();
@@ -617,8 +617,6 @@ void CMusicInfoScanner::FileItemsToAlbums(CFileItemList& items, VECALBUMS& album
{
CMusicInfoTag& tag = *items[i]->GetMusicInfoTag();
CSong song(*items[i]);
-
- // keep the db-only fields intact on rescan...
if (songsMap != NULL)
{
MAPSONGS::iterator it = songsMap->find(items[i]->GetPath());
@@ -631,7 +629,6 @@ void CMusicInfoScanner::FileItemsToAlbums(CFileItemList& items, VECALBUMS& album
if (song.strThumb.empty()) song.strThumb = it->second.strThumb;
}
}
-
if (!tag.GetMusicBrainzAlbumID().empty())
{
VECALBUMS::iterator it;
@@ -657,6 +654,8 @@ void CMusicInfoScanner::FileItemsToAlbums(CFileItemList& items, VECALBUMS& album
In the case where the album artist is unknown, we use the primary artist
(i.e. first artist from each song).
*/
+
+
for (auto& songsByAlbumName : songsByAlbumNames)
{
VECSONGS& songs = songsByAlbumName.second;
@@ -667,7 +666,7 @@ void CMusicInfoScanner::FileItemsToAlbums(CFileItemList& items, VECALBUMS& album
bool tracksOverlap = false;
bool hasAlbumArtist = false;
bool isCompilation = true;
-
+ std::string old_DiscSubtitle;
std::map > artists;
for (VECSONGS::iterator song = songs.begin(); song != songs.end(); ++song)
{
@@ -677,6 +676,8 @@ void CMusicInfoScanner::FileItemsToAlbums(CFileItemList& items, VECALBUMS& album
if (!song->bCompilation)
isCompilation = false;
+ if (song->strDiscSubtitle != old_DiscSubtitle)
+ old_DiscSubtitle = song->strDiscSubtitle;
// get primary artist
std::string primary;
@@ -886,6 +887,7 @@ int CMusicInfoScanner::RetrieveMusicInfo(const std::string& strDirectory, CFileI
*/
FindArtForAlbums(albums, items.GetPath());
+ CheckBoxSets(albums);
/* Strategy: Having scanned tags and made a list of albums, add them to the library. Only then try
to scrape additional album and artist information. Music is often tagged to a mixed standard
- some albums have mbid tags, some don't. Once all the music files have been added to the library,
@@ -919,6 +921,56 @@ int CMusicInfoScanner::RetrieveMusicInfo(const std::string& strDirectory, CFileI
return numAdded;
}
+void CMusicInfoScanner::CheckBoxSets(VECALBUMS &albums)
+{
+ /* Strategy: Having scanned the tags of the albums to add to the library,
+ * check for disc subtitles. If an album only has one subtitled disc, we don't
+ * consider it a boxset as its most likely a 'live'version of the album or a 'bonus disc'.
+ * Single disc subtitles are still added to the database so they can be displayed by
+ * skins at appropriate times, If 'boxset' was added to the albumreleasetype then add the
+ * album regardless of any other rules. */
+
+ std::string m_oldDiscSubtitle;
+ int count;
+ int discno;
+ int old_discno = 0;
+ for(auto& album : albums)
+ {
+ if (album.bCompilation && !album.bBoxedSet)
+ continue; // skip compilations that haven't had the boxset tag added to them
+ //
+ count = 0;
+ for (auto& song : album.songs)
+ {
+ if (!song.strDiscSubtitle.empty())
+ {
+ if (song.strDiscSubtitle != "dummy")
+ {
+ if (song.strDiscSubtitle != m_oldDiscSubtitle)
+ {
+ m_oldDiscSubtitle = song.strDiscSubtitle;
+ count ++;
+ if (count > 2)
+ album.bBoxedSet = true;
+ }
+ }
+ else
+ {
+ discno = song.iTrack >> 16;
+ song.strDiscSubtitle = StringUtils::Format("Disc %i", discno); // create dummy titles
+ if (discno != old_discno)
+ {
+ old_discno = discno;
+ album.bBoxedSet = true;
+ }
+ }
+ }
+ if (count < 3 && !album.bBoxedSet) // must have more than 2 titled discs to be considered a boxset unless tag was set
+ album.bBoxedSet = false;
+ }
+ }
+}
+
void MUSIC_INFO::CMusicInfoScanner::ScrapeInfoAddedAlbums()
{
/* Strategy: Having scanned tags, make a list of albums and add them to the library, only then try
@@ -1454,7 +1506,7 @@ CMusicInfoScanner::DownloadAlbumInfo(const CAlbum& album,
CInfoScanner::INFO_TYPE result = CInfoScanner::NO_NFO;
CNfoFile nfoReader;
existsNFO = XFILE::CFile::Exists(strNfo);
- // When on GUI ask user if they want to ignore nfo and refresh from Internet
+ // When on GUI ask user if they want to ignore nfo and refresh from Internet
if (existsNFO && pDialog && CGUIDialogYesNo::ShowAndGetInput(10523, 20446))
{
existsNFO = false;
@@ -1741,7 +1793,7 @@ CMusicInfoScanner::DownloadArtistInfo(const CArtist& artist,
CLog::Log(LOGDEBUG, "%s not have path, nfo file not possible", artist.strArtist.c_str());
}
- // When on GUI ask user if they want to ignore nfo and refresh from Internet
+ // When on GUI ask user if they want to ignore nfo and refresh from Internet
if (existsNFO && pDialog && CGUIDialogYesNo::ShowAndGetInput(21891, 20446))
{
existsNFO = false;
diff --git a/xbmc/music/infoscanner/MusicInfoScanner.h b/xbmc/music/infoscanner/MusicInfoScanner.h
index ec6dff3f850b0..b84f0026c4432 100644
--- a/xbmc/music/infoscanner/MusicInfoScanner.h
+++ b/xbmc/music/infoscanner/MusicInfoScanner.h
@@ -95,6 +95,8 @@ class CMusicInfoScanner : public IRunnable, public CInfoScanner
*/
static void FindArtForAlbums(VECALBUMS &albums, const std::string &path);
+ static void CheckBoxSets(VECALBUMS &albums);
+
/*! \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
diff --git a/xbmc/music/tags/MusicInfoTag.cpp b/xbmc/music/tags/MusicInfoTag.cpp
index e03c1fe13e0ab..ac878a63a37d8 100644
--- a/xbmc/music/tags/MusicInfoTag.cpp
+++ b/xbmc/music/tags/MusicInfoTag.cpp
@@ -36,6 +36,7 @@ bool CMusicInfoTag::operator !=(const CMusicInfoTag& tag) const
if (m_albumArtist != tag.m_albumArtist) return true;
if (m_strAlbum != tag.m_strAlbum) return true;
if (m_iDuration != tag.m_iDuration) return true;
+ if (m_strDiscSubtitle != tag.m_strDiscSubtitle) return true;
if (m_iTrack != tag.m_iTrack) return true;
if (m_albumReleaseType != tag.m_albumReleaseType) return true;
return false;
@@ -101,6 +102,11 @@ const std::string& CMusicInfoTag::GetAlbum() const
return m_strAlbum;
}
+const std::string& CMusicInfoTag::GetDiscSubtitle() const
+{
+ return m_strDiscSubtitle;
+}
+
int CMusicInfoTag::GetAlbumId() const
{
return m_iAlbumId;
@@ -221,6 +227,11 @@ bool CMusicInfoTag::GetCompilation() const
return m_bCompilation;
}
+bool CMusicInfoTag::GetBoxset() const
+{
+ return m_bBoxset;
+}
+
const EmbeddedArtInfo &CMusicInfoTag::GetCoverArtInfo() const
{
return m_coverArt;
@@ -368,6 +379,11 @@ void CMusicInfoTag::SetDiscNumber(int iDiscNumber)
m_iTrack = (m_iTrack & 0xffff) | (iDiscNumber << 16);
}
+void CMusicInfoTag::SetDiscSubtitle(const std::string& strDiscSubtitle)
+{
+ m_strDiscSubtitle = strDiscSubtitle;
+}
+
void CMusicInfoTag::SetTrackAndDiscNumber(int iTrackAndDisc)
{
m_iTrack = iTrackAndDisc;
@@ -461,6 +477,11 @@ void CMusicInfoTag::SetCompilation(bool compilation)
m_bCompilation = compilation;
}
+void CMusicInfoTag::SetBoxset(bool boxset)
+{
+ m_bBoxset = boxset;
+}
+
void CMusicInfoTag::SetLoaded(bool bOnOff)
{
m_bLoaded = bOnOff;
@@ -614,6 +635,7 @@ void CMusicInfoTag::SetAlbum(const CAlbum& album)
SetCompilation(album.bCompilation);
SYSTEMTIME stTime;
stTime.wYear = album.iYear;
+ SetBoxset(album.bBoxedSet);
SetReleaseDate(stTime);
SetAlbumReleaseType(album.releaseType);
SetDateAdded(album.dateAdded);
@@ -910,6 +932,7 @@ void CMusicInfoTag::Clear()
m_lastPlayed.Reset();
m_dateAdded.Reset();
m_bCompilation = false;
+ m_bBoxset = false;
m_strComment.clear();
m_strMood.clear();
m_strRecordLabel.clear();
diff --git a/xbmc/music/tags/MusicInfoTag.h b/xbmc/music/tags/MusicInfoTag.h
index 8bd5053c7cd74..d40cde9e59831 100644
--- a/xbmc/music/tags/MusicInfoTag.h
+++ b/xbmc/music/tags/MusicInfoTag.h
@@ -50,6 +50,7 @@ class CMusicInfoTag final : public IArchivable, public ISerializable, public ISo
int GetYear() const;
int GetDatabaseId() const;
const std::string &GetType() const;
+ const std::string& GetDiscSubtitle() const;
void GetReleaseDate(SYSTEMTIME& dateTime) const;
std::string GetYearString() const;
@@ -69,6 +70,7 @@ class CMusicInfoTag final : public IArchivable, public ISerializable, public ISo
const CDateTime& GetLastPlayed() const;
const CDateTime& GetDateAdded() const;
bool GetCompilation() const;
+ bool GetBoxset() const;
float GetRating() const;
int GetUserrating() const;
int GetVotes() const;
@@ -127,10 +129,12 @@ class CMusicInfoTag final : public IArchivable, public ISerializable, public ISo
void SetDateAdded(const std::string& strDateAdded);
void SetDateAdded(const CDateTime& strDateAdded);
void SetCompilation(bool compilation);
+ void SetBoxset(bool boxset);
void SetCoverArtInfo(size_t size, const std::string &mimeType);
void SetReplayGain(const ReplayGain& aGain);
void SetAlbumReleaseType(CAlbum::ReleaseType releaseType);
void SetType(const MediaType mediaType);
+ void SetDiscSubtitle(const std::string& strDiscSubtitle);
/*! \brief Append a unique artist to the artist list
Checks if we have this artist already added, and if not adds it to the songs artist list.
@@ -198,6 +202,7 @@ class CMusicInfoTag final : public IArchivable, public ISerializable, public ISo
std::string m_strRecordLabel;
std::string m_strLyrics;
std::string m_cuesheet;
+ std::string m_strDiscSubtitle;
CDateTime m_lastPlayed;
CDateTime m_dateAdded;
bool m_bCompilation;
@@ -212,6 +217,7 @@ class CMusicInfoTag final : public IArchivable, public ISerializable, public ISo
int m_listeners;
int m_iTimesPlayed;
int m_iAlbumId;
+ bool m_bBoxset;
SYSTEMTIME m_dwReleaseDate;
CAlbum::ReleaseType m_albumReleaseType;
diff --git a/xbmc/music/tags/MusicInfoTagLoaderFFmpeg.cpp b/xbmc/music/tags/MusicInfoTagLoaderFFmpeg.cpp
index c61338df2957d..1175918157a20 100644
--- a/xbmc/music/tags/MusicInfoTagLoaderFFmpeg.cpp
+++ b/xbmc/music/tags/MusicInfoTagLoaderFFmpeg.cpp
@@ -135,6 +135,8 @@ bool CMusicInfoTagLoaderFFmpeg::Load(const std::string& strFileName, CMusicInfoT
strcasecmp(avtag->key, "TSOP") == 0) {}
else if (strcasecmp(avtag->key, "TSO2") == 0) {} // Album artist sort
else if (strcasecmp(avtag->key, "TSOC") == 0) {} // composer sort
+ else if (strcasecmp(avtag->key, "TSST") == 0)
+ tag.SetDiscSubtitle(avtag->value);
};
AVDictionaryEntry* avtag=nullptr;
diff --git a/xbmc/music/tags/TagLoaderTagLib.cpp b/xbmc/music/tags/TagLoaderTagLib.cpp
index 51e4be4417330..d6b421c6a9ed4 100644
--- a/xbmc/music/tags/TagLoaderTagLib.cpp
+++ b/xbmc/music/tags/TagLoaderTagLib.cpp
@@ -183,6 +183,8 @@ bool CTagLoaderTagLib::ParseTag(ASF::Tag *asf, EmbeddedArt *art, CMusicInfoTag&
{} // Known unsupported, suppress warnings
else if (it->first == "WM/Year")
tag.SetYear(atoi(it->second.front().toString().toCString(true)));
+ else if (it->first == "WM/SetSubTitle")
+ tag.SetDiscSubtitle(it->second.front().toString().to8Bit(true));
else if (it->first == "MusicBrainz/Artist Id")
tag.SetMusicBrainzArtistID(SplitMBID(GetASFStringList(it->second)));
else if (it->first == "MusicBrainz/Album Id")
@@ -308,6 +310,7 @@ bool CTagLoaderTagLib::ParseTag(ID3v2::Tag *id3v2, EmbeddedArt *art, MUSIC_INFO:
else if (it->first == "TDTG") {} // Tagging time
else if (it->first == "TLAN") {} // Languages
else if (it->first == "TMOO") tag.SetMood(it->second.front()->toString().to8Bit(true));
+ else if (it->first == "TSST") tag.SetDiscSubtitle(it->second.front()->toString().to8Bit(true));
else if (it->first == "USLT")
// Loop through any lyrics frames. Could there be multiple frames, how to choose?
for (ID3v2::FrameList::ConstIterator lt = it->second.begin(); lt != it->second.end(); ++lt)
@@ -500,6 +503,8 @@ bool CTagLoaderTagLib::ParseTag(APE::Tag *ape, EmbeddedArt *art, CMusicInfoTag&
tag.SetDiscNumber(it->second.toString().toInt());
else if (it->first == "YEAR")
tag.SetYear(it->second.toString().toInt());
+ else if (it->first == "DISCSUBTITLE")
+ tag.SetDiscSubtitle(it->second.toString().to8Bit(true));
else if (it->first == "GENRE")
SetGenre(tag, StringListToVectorString(it->second.toStringList()));
else if (it->first == "MOOD")
@@ -644,6 +649,8 @@ bool CTagLoaderTagLib::ParseTag(Ogg::XiphComment *xiph, EmbeddedArt *art, CMusic
tag.SetComment(it->second.front().to8Bit(true));
else if (it->first == "CUESHEET")
tag.SetCueSheet(it->second.front().to8Bit(true));
+ else if (it->first == "DISCSUBTITLE")
+ tag.SetDiscSubtitle(it->second.front().to8Bit(true));
else if (it->first == "ENCODEDBY")
{} // Known but unsupported, suppress warnings
else if (it->first == "COMPOSER")
@@ -844,6 +851,8 @@ bool CTagLoaderTagLib::ParseTag(MP4::Tag *mp4, EmbeddedArt *art, CMusicInfoTag&
//No MP4 standard tag for musician credits
else if (it->first == "----:com.apple.iTunes:LABEL")
tag.SetRecordLabel(it->second.toStringList().front().to8Bit(true));
+ else if (it->first == "----:com.apple.iTunes:DISCSUBTITLE")
+ tag.SetDiscSubtitle(it->second.toStringList().front().to8Bit(true));
else if (it->first == "cpil")
tag.SetCompilation(it->second.toBool());
else if (it->first == "trkn")
@@ -1048,10 +1057,27 @@ void CTagLoaderTagLib::SetGenre(CMusicInfoTag &tag, const std::vector &values)
{
+ tag.SetBoxset(false);
if (values.size() == 1)
+ {
+ if (values[0].find("boxset") != std::string::npos)
+ {
+ tag.SetBoxset(true);
+ if (tag.GetDiscSubtitle().empty()) // no disc title for the tracks on this boxset disc
+ tag.SetDiscSubtitle("dummy");
+ }
tag.SetMusicBrainzReleaseType(values[0]);
+ }
else
+ {
tag.SetMusicBrainzReleaseType(StringUtils::Join(values, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicItemSeparator));
+ if (tag.GetMusicBrainzReleaseType().find("boxset") != std::string::npos)
+ {
+ tag.SetBoxset(true);
+ if (tag.GetDiscSubtitle().empty())
+ tag.SetDiscSubtitle("dummy");
+ }
+ }
}
void CTagLoaderTagLib::AddArtistRole(CMusicInfoTag &tag, const std::string& strRole, const std::vector &values)
@@ -1062,6 +1088,14 @@ void CTagLoaderTagLib::AddArtistRole(CMusicInfoTag &tag, const std::string& strR
tag.AddArtistRole(strRole, values);
}
+void CTagLoaderTagLib::SetDiscSubtitle(CMusicInfoTag & tag, const::std::vector &values)
+{
+ if (values.size() == 1)
+ tag.SetDiscSubtitle(values[0]);
+ else
+ tag.SetDiscSubtitle(std::string());
+}
+
void CTagLoaderTagLib::AddArtistRole(CMusicInfoTag &tag, const std::vector &values)
{
// Values contains role, name pairs (as in ID3 standard for TIPL or TMCL tags)
diff --git a/xbmc/music/tags/TagLoaderTagLib.h b/xbmc/music/tags/TagLoaderTagLib.h
index febe8850e3e4c..c9adcb9a700ee 100644
--- a/xbmc/music/tags/TagLoaderTagLib.h
+++ b/xbmc/music/tags/TagLoaderTagLib.h
@@ -44,9 +44,9 @@ class CTagLoaderTagLib : public MUSIC_INFO::IMusicInfoTagLoader
static void AddArtistRole(MUSIC_INFO::CMusicInfoTag &tag, const std::string& strRole, const std::vector &values);
static void AddArtistRole(MUSIC_INFO::CMusicInfoTag &tag, const std::vector &values);
static void AddArtistInstrument(MUSIC_INFO::CMusicInfoTag &tag, const std::vector &values);
+ static void SetDiscSubtitle(MUSIC_INFO::CMusicInfoTag &tag, const std::vector &values);
static int POPMtoXBMC(int popm);
template
static bool ParseTag(T *tag, EmbeddedArt *art, MUSIC_INFO::CMusicInfoTag& infoTag);
};
-
diff --git a/xbmc/music/windows/GUIWindowMusicBase.cpp b/xbmc/music/windows/GUIWindowMusicBase.cpp
index 2a7e6385c1d16..0ccd47f9f125c 100644
--- a/xbmc/music/windows/GUIWindowMusicBase.cpp
+++ b/xbmc/music/windows/GUIWindowMusicBase.cpp
@@ -1128,16 +1128,16 @@ void CGUIWindowMusicBase::DoScan(const std::string &strPath, bool bRescan /*= fa
void CGUIWindowMusicBase::OnRemoveSource(int iItem)
{
-
+
//Remove music source from library, even when leaving songs
CMusicDatabase database;
database.Open();
database.RemoveSource(m_vecItems->Get(iItem)->GetLabel());
-
+
bool bCanceled;
if (CGUIDialogYesNo::ShowAndGetInput(CVariant{522}, CVariant{20340}, bCanceled, CVariant{""}, CVariant{""}, CGUIDialogYesNo::NO_TIMEOUT))
{
- MAPSONGS songs;
+ MAPSONGS songs;
database.RemoveSongsFromPath(m_vecItems->Get(iItem)->GetPath(), songs, false);
database.CleanupOrphanedItems();
CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetLibraryInfoProvider().ResetLibraryBools();
@@ -1180,4 +1180,3 @@ void CGUIWindowMusicBase::OnAssignContent(const std::string& oldName, const CMed
g_application.StartMusicScan(source.strPath, true);
}
-
diff --git a/xbmc/music/windows/GUIWindowMusicBase.h b/xbmc/music/windows/GUIWindowMusicBase.h
index 9d47f52a8357a..cb6d339d81a59 100644
--- a/xbmc/music/windows/GUIWindowMusicBase.h
+++ b/xbmc/music/windows/GUIWindowMusicBase.h
@@ -49,7 +49,7 @@ class CGUIWindowMusicBase : public CGUIMediaWindow, public IBackgroundLoaderObse
/*! \brief Once a music source is added, store source in library, and prompt
the user to scan this folder into the library
- \param oldName the original music source name
+ \param oldName the original music source name
\param source details of the music source (just added or edited)
*/
static void OnAssignContent(const std::string& oldName, const CMediaSource& source);
diff --git a/xbmc/music/windows/GUIWindowMusicNav.cpp b/xbmc/music/windows/GUIWindowMusicNav.cpp
index 8809b8a3801ab..fe255b9175985 100644
--- a/xbmc/music/windows/GUIWindowMusicNav.cpp
+++ b/xbmc/music/windows/GUIWindowMusicNav.cpp
@@ -241,6 +241,8 @@ std::string CGUIWindowMusicNav::GetQuickpathName(const std::string& strPath) con
return "Singles";
else if (path == "special://musicplaylists/")
return "Playlists";
+ else if (path == "musicdb://boxsets/")
+ return "Boxsets";
else
{
CLog::Log(LOGERROR, " CGUIWindowMusicNav::GetQuickpathName: Unknown parameter (%s)", strPath.c_str());
@@ -450,7 +452,9 @@ bool CGUIWindowMusicNav::GetDirectory(const std::string &strDirectory, CFileItem
node == NODE_TYPE_ALBUM_RECENTLY_PLAYED ||
node == NODE_TYPE_ALBUM_TOP100 ||
node == NODE_TYPE_ALBUM_COMPILATIONS ||
- node == NODE_TYPE_YEAR_ALBUM)
+ node == NODE_TYPE_YEAR_ALBUM ||
+ node == NODE_TYPE_BOXSETS ||
+ node == NODE_TYPE_BOXSET_DISCS)
items.SetContent("albums");
else if (node == NODE_TYPE_ARTIST)
items.SetContent("artists");
@@ -461,7 +465,8 @@ bool CGUIWindowMusicNav::GetDirectory(const std::string &strDirectory, CFileItem
node == NODE_TYPE_ALBUM_RECENTLY_PLAYED_SONGS ||
node == NODE_TYPE_ALBUM_COMPILATIONS_SONGS ||
node == NODE_TYPE_ALBUM_TOP100_SONGS ||
- node == NODE_TYPE_YEAR_SONG)
+ node == NODE_TYPE_YEAR_SONG ||
+ node == NODE_TYPE_BOXSET_DISC_SONGS)
items.SetContent("songs");
else if (node == NODE_TYPE_GENRE)
items.SetContent("genres");
@@ -622,8 +627,10 @@ void CGUIWindowMusicNav::GetContextButtons(int itemNumber, CContextButtons &butt
!item->IsPlugin() && !StringUtils::StartsWithNoCase(item->GetPath(), "musicsearch://"))
{
if (item->IsAlbum())
+ {
// enable query all albums button only in album view
buttons.Add(CONTEXT_BUTTON_INFO_ALL, 20059);
+ }
else if (dir.IsArtistDir(item->GetPath()))
// enable query all artist button only in artist view
buttons.Add(CONTEXT_BUTTON_INFO_ALL, 21884);
@@ -952,6 +959,8 @@ std::string CGUIWindowMusicNav::GetStartFolder(const std::string &dir)
return "musicdb://compilations/";
else if (lower == "years")
return "musicdb://years/";
+ else if (lower == "boxsets")
+ return "musicdb://boxsets/";
else if (lower == "files")
return "sources://music/";