Permalink
Browse files

Merge pull request #1128 from jmarshallnz/separate_db_update

Separate db update from CDatabase::Open()
  • Loading branch information...
2 parents cc57404 + 0d0eb55 commit d9ca8eeabc80068d091973053537fb2d76b7a7ad @jmarshallnz jmarshallnz committed Jul 11, 2012
@@ -29,6 +29,7 @@
7C0A7FCC13A9E76E00AFC2BD /* GUIWindowDebugInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0A7FCA13A9E76E00AFC2BD /* GUIWindowDebugInfo.cpp */; };
7C0B990A154B80200065A238 /* AEDeviceInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0B9908154B80200065A238 /* AEDeviceInfo.cpp */; };
7C1A89BB152671FB00C63311 /* TextureCacheJob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1A89B9152671FB00C63311 /* TextureCacheJob.cpp */; };
+ 7C1D698B15A8142F00658B65 /* DatabaseManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1D698915A8142F00658B65 /* DatabaseManager.cpp */; };
7C1F6F8C13ED17CC001726AB /* LibraryDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1F6F8A13ED17CC001726AB /* LibraryDirectory.cpp */; };
7C6EB586155E3EC80080368A /* ImageFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C6EB584155E3EC80080368A /* ImageFile.cpp */; };
7C6EB708155F3B160080368A /* HTTPImageHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C6EB706155F3B160080368A /* HTTPImageHandler.cpp */; };
@@ -1028,6 +1029,8 @@
7C1A494015A968D6004AF4A4 /* SaveFileStateJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SaveFileStateJob.h; sourceTree = "<group>"; };
7C1A89B9152671FB00C63311 /* TextureCacheJob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureCacheJob.cpp; sourceTree = "<group>"; };
7C1A89BA152671FB00C63311 /* TextureCacheJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextureCacheJob.h; sourceTree = "<group>"; };
+ 7C1D698915A8142F00658B65 /* DatabaseManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DatabaseManager.cpp; sourceTree = "<group>"; };
+ 7C1D698A15A8142F00658B65 /* DatabaseManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DatabaseManager.h; sourceTree = "<group>"; };
7C1F6F8A13ED17CC001726AB /* LibraryDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LibraryDirectory.cpp; sourceTree = "<group>"; };
7C1F6F8B13ED17CC001726AB /* LibraryDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LibraryDirectory.h; sourceTree = "<group>"; };
7C6EB584155E3EC80080368A /* ImageFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageFile.cpp; sourceTree = "<group>"; };
@@ -3573,6 +3576,8 @@
F56C77CB131EC154000AD0F6 /* config.h */,
F56C77D8131EC154000AD0F6 /* CueDocument.cpp */,
F56C77D9131EC154000AD0F6 /* CueDocument.h */,
+ 7C1D698915A8142F00658B65 /* DatabaseManager.cpp */,
+ 7C1D698A15A8142F00658B65 /* DatabaseManager.h */,
F56C77DC131EC154000AD0F6 /* DynamicDll.cpp */,
F56C77DD131EC154000AD0F6 /* DynamicDll.h */,
F56C77DE131EC154000AD0F6 /* Favourites.cpp */,
@@ -7227,6 +7232,7 @@
36A9445D15821FAC00727135 /* SortUtils.cpp in Sources */,
DF08E84515829BA600058C77 /* Exception.cpp in Sources */,
36A9465315AA269B00727135 /* DirectoryNodeTags.cpp in Sources */,
+ 7C1D698B15A8142F00658B65 /* DatabaseManager.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -30,6 +30,7 @@
7C0A7FB313A9E72E00AFC2BD /* DirtyRegionTracker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0A7FB013A9E72E00AFC2BD /* DirtyRegionTracker.cpp */; };
7C0B98F9154B7FF30065A238 /* AEDeviceInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0B98F7154B7FF30065A238 /* AEDeviceInfo.cpp */; };
7C1A89CE1526722200C63311 /* TextureCacheJob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1A89CC1526722200C63311 /* TextureCacheJob.cpp */; };
+ 7C1D697815A8141000658B65 /* DatabaseManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1D697615A8141000658B65 /* DatabaseManager.cpp */; };
7C1F6F7A13ED178F001726AB /* LibraryDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1F6F7813ED178F001726AB /* LibraryDirectory.cpp */; };
7C6EB570155E3E680080368A /* ImageFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C6EB56E155E3E680080368A /* ImageFile.cpp */; };
7C6EB71A155F3B330080368A /* HTTPImageHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C6EB718155F3B330080368A /* HTTPImageHandler.cpp */; };
@@ -1028,6 +1029,8 @@
7C1A495415A96908004AF4A4 /* SaveFileStateJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SaveFileStateJob.h; sourceTree = "<group>"; };
7C1A89CC1526722200C63311 /* TextureCacheJob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureCacheJob.cpp; sourceTree = "<group>"; };
7C1A89CD1526722200C63311 /* TextureCacheJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextureCacheJob.h; sourceTree = "<group>"; };
+ 7C1D697615A8141000658B65 /* DatabaseManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DatabaseManager.cpp; sourceTree = "<group>"; };
+ 7C1D697715A8141000658B65 /* DatabaseManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DatabaseManager.h; sourceTree = "<group>"; };
7C1F6F7813ED178F001726AB /* LibraryDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LibraryDirectory.cpp; sourceTree = "<group>"; };
7C1F6F7913ED178F001726AB /* LibraryDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LibraryDirectory.h; sourceTree = "<group>"; };
7C6EB56E155E3E680080368A /* ImageFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageFile.cpp; sourceTree = "<group>"; };
@@ -3929,6 +3932,8 @@
F56C87B8131F42ED000AD0F6 /* config.h */,
F56C87C5131F42ED000AD0F6 /* CueDocument.cpp */,
F56C87C6131F42ED000AD0F6 /* CueDocument.h */,
+ 7C1D697615A8141000658B65 /* DatabaseManager.cpp */,
+ 7C1D697715A8141000658B65 /* DatabaseManager.h */,
F56C87C9131F42ED000AD0F6 /* DynamicDll.cpp */,
F56C87CA131F42ED000AD0F6 /* DynamicDll.h */,
F56C87CB131F42ED000AD0F6 /* Favourites.cpp */,
@@ -7238,6 +7243,7 @@
36A9445215821F5300727135 /* SortUtils.cpp in Sources */,
DFC3867E158296EC008AE277 /* Exception.cpp in Sources */,
36A9465B15AA26BC00727135 /* DirectoryNodeTags.cpp in Sources */,
+ 7C1D697815A8141000658B65 /* DatabaseManager.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -259,6 +259,7 @@
7C0A7EC013A5DBCE00AFC2BD /* AppParamParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0A7EBE13A5DBCE00AFC2BD /* AppParamParser.cpp */; };
7C0B98A4154B79C30065A238 /* AEDeviceInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0B98A1154B79C30065A238 /* AEDeviceInfo.cpp */; };
7C1A85661520522500C63311 /* TextureCacheJob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1A85631520522500C63311 /* TextureCacheJob.cpp */; };
+ 7C1D682915A7D2FD00658B65 /* DatabaseManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1D682715A7D2FD00658B65 /* DatabaseManager.cpp */; };
7C1F6EBB13ECCFA7001726AB /* LibraryDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1F6EB913ECCFA7001726AB /* LibraryDirectory.cpp */; };
7C2D6AE40F35453E00DD2E85 /* SpecialProtocol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C2D6AE20F35453E00DD2E85 /* SpecialProtocol.cpp */; };
7C45DBE910F325C400D4BBF3 /* DAVDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C45DBE710F325C400D4BBF3 /* DAVDirectory.cpp */; };
@@ -1534,6 +1535,8 @@
7C1A495B15A96918004AF4A4 /* SaveFileStateJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SaveFileStateJob.h; sourceTree = "<group>"; };
7C1A85631520522500C63311 /* TextureCacheJob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureCacheJob.cpp; sourceTree = "<group>"; };
7C1A85641520522500C63311 /* TextureCacheJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextureCacheJob.h; sourceTree = "<group>"; };
+ 7C1D682715A7D2FD00658B65 /* DatabaseManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DatabaseManager.cpp; sourceTree = "<group>"; };
+ 7C1D682815A7D2FD00658B65 /* DatabaseManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DatabaseManager.h; sourceTree = "<group>"; };
7C1F6EB913ECCFA7001726AB /* LibraryDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LibraryDirectory.cpp; sourceTree = "<group>"; };
7C1F6EBA13ECCFA7001726AB /* LibraryDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LibraryDirectory.h; sourceTree = "<group>"; };
7C2D6AE20F35453E00DD2E85 /* SpecialProtocol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpecialProtocol.cpp; sourceTree = "<group>"; };
@@ -4336,6 +4339,8 @@
F5B413131065900C0035D105 /* config.h */,
E38E167E0D25F9FA00618676 /* CueDocument.cpp */,
E38E167F0D25F9FA00618676 /* CueDocument.h */,
+ 7C1D682715A7D2FD00658B65 /* DatabaseManager.cpp */,
+ 7C1D682815A7D2FD00658B65 /* DatabaseManager.h */,
E38E168C0D25F9FA00618676 /* DynamicDll.cpp */,
E38E168D0D25F9FA00618676 /* DynamicDll.h */,
E38E16900D25F9FA00618676 /* Favourites.cpp */,
@@ -7319,6 +7324,7 @@
36A9444115821E7C00727135 /* SortUtils.cpp in Sources */,
1DE0443515828F4B005DDB4D /* Exception.cpp in Sources */,
36A9464C15AA25FD00727135 /* DirectoryNodeTags.cpp in Sources */,
+ 7C1D682915A7D2FD00658B65 /* DatabaseManager.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -893,6 +893,7 @@
<ClCompile Include="..\..\xbmc\TextureCache.cpp" />
<ClCompile Include="..\..\xbmc\TextureCacheJob.cpp" />
<ClCompile Include="..\..\xbmc\TextureDatabase.cpp" />
+ <ClCompile Include="..\..\xbmc\DatabaseManager.cpp" />
<ClInclude Include="..\..\xbmc\cores\AudioEngine\AEAudioFormat.h" />
<ClInclude Include="..\..\xbmc\cores\AudioEngine\AEFactory.h" />
<ClInclude Include="..\..\xbmc\cores\AudioEngine\AESinkFactory.h" />
@@ -1847,6 +1848,7 @@
<ClInclude Include="..\..\xbmc\TextureCache.h" />
<ClInclude Include="..\..\xbmc\TextureCacheJob.h" />
<ClInclude Include="..\..\xbmc\TextureDatabase.h" />
+ <ClInclude Include="..\..\xbmc\DatabaseManager.h" />
<ClInclude Include="..\..\xbmc\ThumbLoader.h" />
<ClInclude Include="..\..\xbmc\ThumbnailCache.h" />
<ClInclude Include="..\..\xbmc\URL.h" />
@@ -2284,4 +2286,4 @@
</VisualStudio>
</ProjectExtensions>
<Import Project="$(SolutionDir)\$(ProjectFileName).targets.user" Condition="Exists('$(SolutionDir)\$(ProjectFileName).targets.user')" />
-</Project>
+</Project>
@@ -2533,6 +2533,7 @@
<ClCompile Include="..\..\xbmc\TextureCache.cpp" />
<ClCompile Include="..\..\xbmc\TextureCacheJob.cpp" />
<ClCompile Include="..\..\xbmc\TextureDatabase.cpp" />
+ <ClCompile Include="..\..\xbmc\DatabaseManager.cpp" />
<ClCompile Include="..\..\xbmc\ThumbnailCache.cpp" />
<ClCompile Include="..\..\xbmc\URL.cpp" />
<ClCompile Include="..\..\xbmc\Util.cpp" />
@@ -5161,6 +5162,7 @@
<ClInclude Include="..\..\xbmc\TextureCache.h" />
<ClInclude Include="..\..\xbmc\TextureCacheJob.h" />
<ClInclude Include="..\..\xbmc\TextureDatabase.h" />
+ <ClInclude Include="..\..\xbmc\DatabaseManager.h" />
<ClInclude Include="..\..\xbmc\ThumbnailCache.h" />
<ClInclude Include="..\..\xbmc\URL.h" />
<ClInclude Include="..\..\xbmc\Util.h" />
@@ -5236,4 +5238,4 @@
<Filter>win32</Filter>
</CustomBuild>
</ItemGroup>
-</Project>
+</Project>
View
@@ -306,6 +306,7 @@
#include "utils/SaveFileStateJob.h"
#include "utils/AlarmClock.h"
#include "utils/StringUtils.h"
+#include "DatabaseManager.h"
#ifdef _LINUX
#include "XHandle.h"
@@ -709,6 +710,9 @@ bool CApplication::Create()
CAEFactory::SetMute (g_settings.m_bMute);
CAEFactory::SetSoundMode(g_guiSettings.GetInt("audiooutput.guisoundmode"));
+ // initialize the addon database (must be before the addon manager is init'd)
+ CDatabaseManager::Get().Initialize(true);
+
// start-up Addons Framework
// currently bails out if either cpluff Dll is unavailable or system dir can not be scanned
if (!CAddonMgr::Get().Init())
@@ -1153,6 +1157,9 @@ bool CApplication::Initialize()
g_curlInterface.Load();
g_curlInterface.Unload();
+ // initialize (and update as needed) our databases
+ CDatabaseManager::Get().Initialize();
+
#ifdef HAS_WEB_SERVER
CWebServer::RegisterRequestHandler(&m_httpImageHandler);
CWebServer::RegisterRequestHandler(&m_httpVfsHandler);
View
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2012 Team XBMC
+ * http://www.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, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "DatabaseManager.h"
+#include "utils/log.h"
+#include "addons/AddonDatabase.h"
+#include "ViewDatabase.h"
+#include "TextureDatabase.h"
+#include "programs/ProgramDatabase.h"
+#include "music/MusicDatabase.h"
+#include "video/VideoDatabase.h"
+#include "settings/AdvancedSettings.h"
+
+using namespace std;
+
+CDatabaseManager &CDatabaseManager::Get()
+{
+ static CDatabaseManager s_manager;
+ return s_manager;
+}
+
+CDatabaseManager::CDatabaseManager()
+{
+}
+
+CDatabaseManager::~CDatabaseManager()
+{
+}
+
+void CDatabaseManager::Initialize(bool addonsOnly)
+{
+ Deinitialize();
+ { CAddonDatabase db; UpdateDatabase(db); }
+ if (addonsOnly)
+ return;
+ CLog::Log(LOGDEBUG, "%s, updating databases...", __FUNCTION__);
+ { CViewDatabase db; UpdateDatabase(db); }
+ { CTextureDatabase db; UpdateDatabase(db); }
+ { CProgramDatabase db; UpdateDatabase(db); }
+ { CMusicDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseMusic); }
+ { CVideoDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseVideo); }
+ CLog::Log(LOGDEBUG, "%s, updating databases... DONE", __FUNCTION__);
+}
+
+void CDatabaseManager::Deinitialize()
+{
+ CSingleLock lock(m_section);
+ m_dbStatus.clear();
+}
+
+bool CDatabaseManager::CanOpen(const std::string &name)
+{
+ CSingleLock lock(m_section);
+ map<string, DB_STATUS>::const_iterator i = m_dbStatus.find(name);
+ if (i != m_dbStatus.end())
+ return i->second == DB_READY;
+ return false; // db isn't even attempted to update yet
+}
+
+void CDatabaseManager::UpdateDatabase(CDatabase &db, DatabaseSettings *settings)
+{
+ std::string name = db.GetBaseDBName();
+ UpdateStatus(name, DB_UPDATING);
+ if (db.Update(settings ? *settings : DatabaseSettings()))
+ UpdateStatus(name, DB_READY);
+ else
+ UpdateStatus(name, DB_FAILED);
+}
+
+void CDatabaseManager::UpdateStatus(const std::string &name, DB_STATUS status)
+{
+ CSingleLock lock(m_section);
+ m_dbStatus[name] = status;
+}
View
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 Team XBMC
+ * http://www.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, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include <map>
+#include <string>
+#include "threads/CriticalSection.h"
+#include "threads/Event.h"
+
+class CDatabase;
+class DatabaseSettings;
+
+/*!
+ \ingroup database
+ \brief Database manager class for handling database updating
+
+ Ensures that databases used in XBMC are up to date, and if a database can't be
+ opened, ensures we don't continuously try it.
+
+ */
+class CDatabaseManager
+{
+public:
+ /*!
+ \brief The only way through which the global instance of the CDatabaseManager should be accessed.
+ \return the global instance.
+ */
+ static CDatabaseManager &Get();
+
+ /*! \brief Initalize the database manager
+ Checks that all databases are up to date, otherwise updates them.
+ */
+ void Initialize(bool addonsOnly = false);
+
+ /*! \brief Deinitialize the database manager
+ */
+ void Deinitialize();
+
+ /*! \brief Check whether we can open a database.
+
+ Checks whether the database has been updated correctly, if so returns true.
+ If the database update failed, returns false immediately.
+ If the database update is in progress, returns false.
+
+ \param name the name of the database to check.
+ \return true if the database can be opened, false otherwise.
+ */
+ bool CanOpen(const std::string &name);
+
+private:
+ // private construction, and no assignements; use the provided singleton methods
+ CDatabaseManager();
+ CDatabaseManager(const CDatabaseManager&);
+ CDatabaseManager const& operator=(CDatabaseManager const&);
+ virtual ~CDatabaseManager();
+
+ enum DB_STATUS { DB_CLOSED, DB_UPDATING, DB_READY, DB_FAILED };
+ void UpdateStatus(const std::string &name, DB_STATUS status);
+ void UpdateDatabase(CDatabase &db, DatabaseSettings *settings = NULL);
+
+ CCriticalSection m_section; ///< Critical section protecting m_dbStatus.
+ std::map<std::string, DB_STATUS> m_dbStatus; ///< Our database status map.
+};
View
@@ -4,6 +4,7 @@ SRCS=Application.cpp \
AutoSwitch.cpp \
BackgroundInfoLoader.cpp \
CueDocument.cpp \
+ DatabaseManager.cpp \
DynamicDll.cpp \
Favourites.cpp \
FileItem.cpp \
Oops, something went wrong.

0 comments on commit d9ca8ee

Please sign in to comment.