Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #1582 from alcoheca/upnp-cache-notify

Upnp cache notify
  • Loading branch information...
commit 59ca8732710dff84330672a0b3bcb826477b7c50 2 parents 70205b0 + 7e139d5
@alcoheca alcoheca authored
View
4 language/English/strings.po
@@ -8257,7 +8257,9 @@ msgctxt "#20187"
msgid "UPnP"
msgstr ""
-#empty string with id 20188
+msgctxt "#20188"
+msgid "Announce library updates via UPnP"
+msgstr ""
msgctxt "#20189"
msgid "Enable auto scrolling for plot & review"
View
11 lib/libUPnP/Platinum/Source/Core/PltService.cpp
@@ -460,14 +460,14 @@ PLT_Service::IsSubscribable()
| PLT_Service::SetStateVariable
+---------------------------------------------------------------------*/
NPT_Result
-PLT_Service::SetStateVariable(const char* name, const char* value)
+PLT_Service::SetStateVariable(const char* name, const char* value, const bool clearonsend /*=false*/)
{
PLT_StateVariable* stateVariable = NULL;
NPT_ContainerFind(m_StateVars, PLT_StateVariableNameFinder(name), stateVariable);
if (stateVariable == NULL)
return NPT_FAILURE;
- return stateVariable->SetValue(value);
+ return stateVariable->SetValue(value, clearonsend);
}
/*----------------------------------------------------------------------
@@ -838,6 +838,13 @@ PLT_Service::NotifyChanged()
delete sub;
}
+ // some state variables must be cleared immediatly after sending
+ iter = vars_ready.GetFirstItem();
+ while (iter) {
+ PLT_StateVariable* var = *iter;
+ var->OnSendCompleted();
+ ++iter;
+ }
return NPT_SUCCESS;
}
View
3  lib/libUPnP/Platinum/Source/Core/PltService.h
@@ -216,8 +216,9 @@ class PLT_Service
when necessary.
@param name state variable name
@param value new State Variable value.
+ @param clearonsend whether the State Variable should clear immediatly in ::OnSendingCompleted
*/
- NPT_Result SetStateVariable(const char* name, const char* value);
+ NPT_Result SetStateVariable(const char* name, const char* value, const bool clearonsend = false);
/**
Certain state variables notifications must not be sent faster than a certain
View
16 lib/libUPnP/Platinum/Source/Core/PltStateVariable.cpp
@@ -48,7 +48,8 @@ NPT_SET_LOCAL_LOGGER("platinum.core.statevariable")
PLT_StateVariable::PLT_StateVariable(PLT_Service* service) :
m_Service(service),
m_AllowedValueRange(NULL),
- m_IsSendingEventsIndirectly(true)
+ m_IsSendingEventsIndirectly(true),
+ m_ShouldClearOnSend(false)
{
}
@@ -145,7 +146,7 @@ PLT_StateVariable::SetRate(NPT_TimeInterval rate)
| PLT_StateVariable::SetValue
+---------------------------------------------------------------------*/
NPT_Result
-PLT_StateVariable::SetValue(const char* value)
+PLT_StateVariable::SetValue(const char* value, const bool clearonsend /*=false*/)
{
if (value == NULL) {
return NPT_FAILURE;
@@ -159,6 +160,7 @@ PLT_StateVariable::SetValue(const char* value)
}
m_Value = value;
+ m_ShouldClearOnSend = clearonsend;
m_Service->AddChanged(this);
}
@@ -183,6 +185,16 @@ PLT_StateVariable::IsReadyToPublish()
}
/*----------------------------------------------------------------------
+| PLT_StateVariable::OnSendCompleted
++---------------------------------------------------------------------*/
+void
+PLT_StateVariable::OnSendCompleted()
+{
+ if(m_ShouldClearOnSend)
+ m_Value = m_DefaultValue;
+}
+
+/*----------------------------------------------------------------------
| PLT_StateVariable::ValidateValue
+---------------------------------------------------------------------*/
NPT_Result
View
10 lib/libUPnP/Platinum/Source/Core/PltStateVariable.h
@@ -115,8 +115,9 @@ class PLT_StateVariable
it is an allowed value. Once the value is validated, it is marked for eventing by
calling the PLT_Service AddChanged function.
@param value new state variable value. Can be a comma separated list of values.
+ @param clearonsend whether the statevariable should be cleared immediatly after sending
*/
- NPT_Result SetValue(const char* value);
+ NPT_Result SetValue(const char* value, const bool clearonsend = false);
/**
Validate the new value of the state variable.
@@ -173,6 +174,12 @@ class PLT_StateVariable
bool IsReadyToPublish();
/**
+ * If this statevariable should clear after sending to all subscribers, clears the value without
+ * eventing the change
+ */
+ void OnSendCompleted();
+
+ /**
Serialize the state variable into xml.
*/
NPT_Result Serialize(NPT_XmlElementNode& node);
@@ -189,6 +196,7 @@ class PLT_StateVariable
NPT_String m_DefaultValue;
bool m_IsSendingEvents;
bool m_IsSendingEventsIndirectly;
+ bool m_ShouldClearOnSend;
NPT_TimeInterval m_Rate;
NPT_TimeStamp m_LastEvent;
NPT_Array<NPT_String*> m_AllowedValues;
View
6 xbmc/music/MusicDatabase.cpp
@@ -4183,6 +4183,10 @@ bool CMusicDatabase::RemoveSongsFromPath(const CStdString &path1, CSongMap &song
m_pDS->close();
+ //TODO: move this below the m_pDS->exec block, once UPnP doesn't rely on this anymore
+ for (unsigned int i = 0; i < ids.size(); i++)
+ AnnounceRemove("song", ids[i]);
+
// and delete all songs, and anything linked to them
sql = "delete from song where idSong in " + songIds;
m_pDS->exec(sql.c_str());
@@ -4193,8 +4197,6 @@ bool CMusicDatabase::RemoveSongsFromPath(const CStdString &path1, CSongMap &song
sql = "delete from karaokedata where idSong in " + songIds;
m_pDS->exec(sql.c_str());
- for (unsigned int i = 0; i < ids.size(); i++)
- AnnounceRemove("song", ids[i]);
}
// and remove the path as well (it'll be re-added later on with the new hash if it's non-empty)
sql = "delete from path" + where;
View
1  xbmc/network/upnp/UPnP.cpp
@@ -183,6 +183,7 @@ class CMediaBrowser : public PLT_SyncMediaBrowser,
path += "/";
}
+ CLog::Log(LOGDEBUG, "UPNP: notfified container update %s", (const char*)path);
CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
message.SetStringParam(path.GetChars());
g_windowManager.SendThreadMessage(message);
View
199 xbmc/network/upnp/UPnPServer.cpp
@@ -1,13 +1,16 @@
#include "UPnPServer.h"
#include "UPnPInternal.h"
+#include "Application.h"
#include "GUIViewState.h"
#include "Platinum.h"
#include "ThumbLoader.h"
+#include "interfaces/AnnouncementManager.h"
#include "filesystem/Directory.h"
#include "filesystem/MusicDatabaseDirectory.h"
#include "filesystem/VideoDatabaseDirectory.h"
#include "guilib/Key.h"
#include "music/tags/MusicInfoTag.h"
+#include "settings/GUISettings.h"
#include "utils/log.h"
#include "utils/md5.h"
#include "utils/StringUtils.h"
@@ -16,6 +19,7 @@
#include "video/VideoDatabase.h"
using namespace std;
+using namespace ANNOUNCEMENT;
using namespace XFILE;
namespace UPNP
@@ -23,6 +27,28 @@ namespace UPNP
NPT_UInt32 CUPnPServer::m_MaxReturnedItems = 0;
+const char* audio_containers[] = { "musicdb://1/", "musicdb://2/", "musicdb://3/",
+ "musicdb://4/", "musicdb://6/", "musicdb://9/",
+ "musicdb://10/" };
+
+const char* video_containers[] = { "videodb://1/2/", "videodb://2/2/", "videodb://4/",
+ "videodb://5/" };
+
+/*----------------------------------------------------------------------
+| CUPnPServer::CUPnPServer
++---------------------------------------------------------------------*/
+CUPnPServer::CUPnPServer(const char* friendly_name, const char* uuid /*= NULL*/, int port /*= 0*/) :
+ PLT_MediaConnect(friendly_name, false, uuid, port),
+ PLT_FileMediaConnectDelegate("/", "/"),
+ m_scanning(g_application.IsMusicScanning() || g_application.IsVideoScanning())
+{
+}
+
+CUPnPServer::~CUPnPServer()
+{
+ ANNOUNCEMENT::CAnnouncementManager::RemoveAnnouncer(this);
+}
+
/*----------------------------------------------------------------------
| CUPnPServer::ProcessGetSCPD
+---------------------------------------------------------------------*/
@@ -42,12 +68,100 @@ CUPnPServer::ProcessGetSCPD(PLT_Service* service,
NPT_Result
CUPnPServer::SetupServices()
{
- PLT_MediaConnect::SetupServices();
- PLT_Service* service = NULL;
- NPT_Result result = FindServiceById("urn:upnp-org:serviceId:ContentDirectory", service);
- if (service)
- service->SetStateVariable("SortCapabilities", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating");
- return result;
+ PLT_MediaConnect::SetupServices();
+ PLT_Service* service = NULL;
+ NPT_Result result = FindServiceById("urn:upnp-org:serviceId:ContentDirectory", service);
+ if (service)
+ service->SetStateVariable("SortCapabilities", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating");
+
+ m_scanning = true;
+ OnScanCompleted(AudioLibrary);
+ m_scanning = true;
+ OnScanCompleted(VideoLibrary);
+
+ // now safe to start passing on new notifications
+ ANNOUNCEMENT::CAnnouncementManager::AddAnnouncer(this);
+
+ return result;
+}
+
+/*----------------------------------------------------------------------
+| CUPnPServer::OnScanCompleted
++---------------------------------------------------------------------*/
+void
+CUPnPServer::OnScanCompleted(int type)
+{
+ if (type == AudioLibrary) {
+ for (size_t i = 0; i < sizeof(audio_containers)/sizeof(audio_containers[0]); i++)
+ UpdateContainer(audio_containers[i]);
+ }
+ else if (type == VideoLibrary) {
+ for (size_t i = 0; i < sizeof(video_containers)/sizeof(video_containers[0]); i++)
+ UpdateContainer(video_containers[i]);
+ }
+ else
+ return;
+ m_scanning = false;
+ PropagateUpdates();
+}
+
+/*----------------------------------------------------------------------
+| CUPnPServer::UpdateContainer
++---------------------------------------------------------------------*/
+void
+CUPnPServer::UpdateContainer(const string& id)
+{
+ map<string,pair<bool, unsigned long> >::iterator itr = m_UpdateIDs.find(id);
+ unsigned long count = 0;
+ if (itr != m_UpdateIDs.end())
+ count = ++itr->second.second;
+ m_UpdateIDs[id] = make_pair(true, count);
+ PropagateUpdates();
+}
+
+/*----------------------------------------------------------------------
+| CUPnPServer::PropagateUpdates
++---------------------------------------------------------------------*/
+void
+CUPnPServer::PropagateUpdates()
+{
+ PLT_Service* service = NULL;
+ NPT_String current_ids;
+ string buffer;
+ map<string,pair<bool, unsigned long> >::iterator itr;
+
+ if (m_scanning || !g_guiSettings.GetBool("services.upnpannounce"))
+ return;
+
+ NPT_CHECK_LABEL(FindServiceById("urn:upnp-org:serviceId:ContentDirectory", service), failed);
+
+ // we pause, and we must retain any changes which have not been
+ // broadcast yet
+ NPT_CHECK_LABEL(service->PauseEventing(), failed);
+ NPT_CHECK_LABEL(service->GetStateVariableValue("ContainerUpdateIDs", current_ids), failed);
+ buffer = (const char*)current_ids;
+ if (!buffer.empty())
+ buffer.append(",");
+
+ // only broadcast ids with modified bit set
+ for (itr = m_UpdateIDs.begin(); itr != m_UpdateIDs.end(); ++itr) {
+ if (itr->second.first) {
+ buffer.append(StringUtils::Format("%s,%ld,", itr->first.c_str(), itr->second.second).c_str());
+ itr->second.first = false;
+ }
+ }
+
+ // set the value, Platinum will clear ContainerUpdateIDs after sending
+ NPT_CHECK_LABEL(service->SetStateVariable("ContainerUpdateIDs", buffer.substr(0,buffer.size()-1).c_str(), true), failed);
+ NPT_CHECK_LABEL(service->IncStateVariable("SystemUpdateID"), failed);
+
+ service->PauseEventing(false);
+ return;
+
+failed:
+ // should attempt to start eventing on a failure
+ if (service) service->PauseEventing(false);
+ CLog::Log(LOGERROR, "UPNP: Unable to propagate updates");
}
/*----------------------------------------------------------------------
@@ -202,6 +316,79 @@ CUPnPServer::Build(CFileItemPtr item,
}
/*----------------------------------------------------------------------
+| CUPnPServer::Announce
++---------------------------------------------------------------------*/
+void
+CUPnPServer::Announce(AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
+{
+ NPT_String path;
+ int item_id;
+ string item_type;
+
+ if (strcmp(sender, "xbmc"))
+ return;
+
+ if (strcmp(message, "OnUpdate") && strcmp(message, "OnRemove")
+ && strcmp(message, "OnScanStarted") && strcmp(message, "OnScanFinished"))
+ return;
+
+ if (data.isNull()) {
+ if (!strcmp(message, "OnScanStarted")) {
+ m_scanning = true;
+ }
+ else if (!strcmp(message, "OnScanFinished")) {
+ OnScanCompleted(flag);
+ }
+ }
+ else {
+ // handle both updates & removals
+ if (!data["item"].isNull()) {
+ item_id = data["item"]["id"].asInteger();
+ item_type = data["item"]["type"].asString();
+ }
+ else {
+ item_id = data["id"].asInteger();
+ item_type = data["type"].asString();
+ }
+
+ // we always update 'recently added' nodes along with the specific container,
+ // as we don't differentiate 'updates' from 'adds' in RPC interface
+ if (flag == VideoLibrary) {
+ if(item_type == "episode") {
+ CVideoDatabase db;
+ if (!db.Open()) return;
+ int show_id = db.GetTvShowForEpisode(item_id);
+ UpdateContainer(StringUtils::Format("videodb://2/2/%d/", show_id));
+ UpdateContainer("videodb://5/");
+ }
+ else if(item_type == "tvshow") {
+ UpdateContainer("videodb://2/2/");
+ UpdateContainer("videodb://5/");
+ }
+ else if(item_type == "movie") {
+ UpdateContainer("videodb://1/2/");
+ UpdateContainer("videodb://4/");
+ }
+ else if(item_type == "musicvideo") {
+ UpdateContainer("videodb://4/");
+ }
+ }
+ else if (flag == AudioLibrary && item_type == "song") {
+ // we also update the 'songs' container is maybe a performance drop too
+ // high? would need to check if slow clients even cache at all anyway
+ CMusicDatabase db;
+ CAlbum album;
+ if (!db.Open()) return;
+ if (db.GetAlbumFromSong(item_id, album)) {
+ UpdateContainer(StringUtils::Format("musicdb://3/%ld", album.idAlbum));
+ UpdateContainer("musicdb://4/");
+ UpdateContainer("musicdb://6/");
+ }
+ }
+ }
+}
+
+/*----------------------------------------------------------------------
| TranslateWMPObjectId
+---------------------------------------------------------------------*/
static NPT_String TranslateWMPObjectId(NPT_String id)
View
17 xbmc/network/upnp/UPnPServer.h
@@ -19,6 +19,7 @@
*
*/
#include "PltMediaConnect.h"
+#include "interfaces/IAnnouncer.h"
#include "FileItem.h"
class CThumbLoader;
@@ -29,13 +30,13 @@ namespace UPNP
{
class CUPnPServer : public PLT_MediaConnect,
- public PLT_FileMediaConnectDelegate
+ public PLT_FileMediaConnectDelegate,
+ public ANNOUNCEMENT::IAnnouncer
{
public:
- CUPnPServer(const char* friendly_name, const char* uuid = NULL, int port = 0) :
- PLT_MediaConnect(friendly_name, false, uuid, port),
- PLT_FileMediaConnectDelegate("/", "/") {
- }
+ CUPnPServer(const char* friendly_name, const char* uuid = NULL, int port = 0);
+ ~CUPnPServer();
+ virtual void Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data);
// PLT_MediaServer methods
virtual NPT_Result OnBrowseMetadata(PLT_ActionReference& action,
@@ -90,6 +91,10 @@ class CUPnPServer : public PLT_MediaConnect,
private:
+ void OnScanCompleted(int type);
+ void UpdateContainer(const std::string& id);
+ void PropagateUpdates();
+
PLT_MediaObject* Build(CFileItemPtr item,
bool with_count,
const PLT_HttpRequestContext& context,
@@ -117,6 +122,8 @@ class CUPnPServer : public PLT_MediaConnect,
NPT_Mutex m_FileMutex;
NPT_Map<NPT_String, NPT_String> m_FileMap;
+ std::map<std::string, std::pair<bool, unsigned long> > m_UpdateIDs;
+ bool m_scanning;
public:
// class members
static NPT_UInt32 m_MaxReturnedItems;
View
1  xbmc/settings/GUISettings.cpp
@@ -829,6 +829,7 @@ void CGUISettings::Initialize()
CSettingsCategory* srvUpnp = AddCategory(SETTINGS_SERVICE, "upnp", 20187);
AddBool(srvUpnp, "services.upnpserver", 21360, false);
+ AddBool(srvUpnp, "services.upnpannounce", 20188, true);
AddBool(srvUpnp, "services.upnprenderer", 21881, false);
#ifdef HAS_WEB_SERVER
View
5 xbmc/settings/GUIWindowSettingsCategory.cpp
@@ -737,6 +737,11 @@ void CGUIWindowSettingsCategory::UpdateSettings()
CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID());
if (pControl) pControl->SetEnabled(g_guiSettings.GetBool("services.esenabled"));
}
+ else if (strSetting.Equals("services.upnpannounce"))
+ {
+ CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID());
+ pControl->SetEnabled(g_guiSettings.GetBool("services.upnpserver"));
+ }
else if (strSetting.Equals("audiocds.quality"))
{ // only visible if we are doing non-WAV ripping
CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID());
View
24 xbmc/video/VideoDatabase.cpp
@@ -2731,13 +2731,15 @@ void CVideoDatabase::DeleteMovie(const CStdString& strFilenameAndPath, bool bKee
m_pDS->exec(strSQL.c_str());
}
+ //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
+ if (!bKeepId)
+ AnnounceRemove("movie", idMovie);
+
CStdString strPath, strFileName;
SplitPath(strFilenameAndPath,strPath,strFileName);
InvalidatePathHash(strPath);
CommitTransaction();
- if (!bKeepId)
- AnnounceRemove("movie", idMovie);
}
catch (...)
{
@@ -2801,12 +2803,14 @@ void CVideoDatabase::DeleteTvShow(const CStdString& strPath, bool bKeepId /* = f
m_pDS->exec(strSQL.c_str());
}
+ //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
+ if (!bKeepId)
+ AnnounceRemove("tvshow", idTvShow);
+
InvalidatePathHash(strPath);
CommitTransaction();
- if (!bKeepId)
- AnnounceRemove("tvshow", idTvShow);
}
catch (...)
{
@@ -2840,6 +2844,10 @@ void CVideoDatabase::DeleteEpisode(const CStdString& strFilenameAndPath, int idE
}
}
+ //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
+ if (!bKeepId)
+ AnnounceRemove("episode", idEpisode);
+
CStdString strSQL;
strSQL=PrepareSQL("delete from actorlinkepisode where idEpisode=%i", idEpisode);
m_pDS->exec(strSQL.c_str());
@@ -2862,8 +2870,6 @@ void CVideoDatabase::DeleteEpisode(const CStdString& strFilenameAndPath, int idE
m_pDS->exec(strSQL.c_str());
}
- if (!bKeepId)
- AnnounceRemove("episode", idEpisode);
}
catch (...)
{
@@ -2922,13 +2928,15 @@ void CVideoDatabase::DeleteMusicVideo(const CStdString& strFilenameAndPath, bool
m_pDS->exec(strSQL.c_str());
}
+ //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
+ if (!bKeepId)
+ AnnounceRemove("musicvideo", idMVideo);
+
CStdString strPath, strFileName;
SplitPath(strFilenameAndPath,strPath,strFileName);
InvalidatePathHash(strPath);
CommitTransaction();
- if (!bKeepId)
- AnnounceRemove("musicvideo", idMVideo);
}
catch (...)
{
Please sign in to comment.
Something went wrong with that request. Please try again.