Permalink
Browse files

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

Upnp cache notify
  • Loading branch information...
2 parents 70205b0 + 7e139d5 commit 59ca8732710dff84330672a0b3bcb826477b7c50 @alcoheca alcoheca committed Oct 11, 2012
@@ -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"
@@ -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;
}
@@ -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
@@ -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
@@ -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;
@@ -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;
@@ -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);
@@ -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,13 +19,36 @@
#include "video/VideoDatabase.h"
using namespace std;
+using namespace ANNOUNCEMENT;
using namespace XFILE;
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)
Oops, something went wrong.

0 comments on commit 59ca873

Please sign in to comment.