summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/media/mediaManager.h12
-rw-r--r--include/plugin.h32
-rw-r--r--include/pluginManager.h5
-rw-r--r--include/server.h2
-rw-r--r--media/mediaManager.cpp337
-rw-r--r--media/pluginManager.cpp39
-rw-r--r--plugins/provider/vdrProvider/vdrProvider.cpp101
7 files changed, 380 insertions, 148 deletions
diff --git a/include/media/mediaManager.h b/include/media/mediaManager.h
index c9e042d..9c5b9a1 100644
--- a/include/media/mediaManager.h
+++ b/include/media/mediaManager.h
@@ -15,14 +15,15 @@
#include <list>
#include <string>
#include <stdint.h>
-#include <tntdb/connection.h>
#include <tntdb/connect.h>
+#include <tntdb/connection.h>
namespace upnp {
class cResourceStreamer;
class cMediaManager : public cThread {
+ friend void cUPnPResourceProvider::OnContainerUpdate(const string& uri, long updateID, const string& target = string());
private:
struct MediaRequest {
@@ -81,13 +82,18 @@ private:
int CreateResponse(MediaRequest&, const string&, const string&);
- void OnContainerUpdate(string uri, long updateID);
+ void OnContainerUpdate(const string& uri, long updateID, const string& target);
+ bool UpdateContainerUpdateId(const string& objectID, long updateID);
+
+ bool ScanURI(const string& uri, cUPnPResourceProvider* provider);
+
+ bool RefreshObject(const cMetadata& metadata);
cUPnPResourceProvider* CreateResourceProvider(const std::string& uri);
uint32_t systemUpdateID;
IdList eventedContainerUpdateIDs;
- StringList scanDirectories;
+ StringList scanTargets;
string databaseFile;
string pluginDirectory;
tntdb::Connection connection;
diff --git a/include/plugin.h b/include/plugin.h
index cd24e91..c8abd57 100644
--- a/include/plugin.h
+++ b/include/plugin.h
@@ -12,6 +12,8 @@
#include <map>
#include <list>
#include <stdint.h>
+#include <vdr/thread.h>
+#include "../include/tools.h"
using namespace std;
@@ -116,6 +118,10 @@ public:
long GetInteger() const;
bool GetBoolean() const;
+ bool IsEmpty() { return key.empty() && value.empty(); }
+
+ static Property Empty;
+
private:
string key;
string value;
@@ -186,6 +192,8 @@ public:
bool SetObjectIDByUri(const string& uri);
bool SetParentIDByUri(const string& uri);
+ string ToString();
+
private:
PropertyMap properties;
@@ -201,11 +209,10 @@ public:
#define UPNP_REGISTER_RESOURCE_PROVIDER(cls) extern "C" void *UPnPCreateResourceProvider(void) { return new cls; }
-class cUPnPResourceProvider {
+class cUPnPResourceProvider : public cThread {
+ friend class cMediaManager;
public:
- typedef list<string> EntryList;
-
virtual ~cUPnPResourceProvider(){};
/**
@@ -257,7 +264,7 @@ public:
*
* The given URI is an absolute URI.
*/
- virtual EntryList GetContainerEntries(const string& uri) = 0;
+ virtual StringList GetContainerEntries(const string& uri) = 0;
/**
* Checks if the given URI is a container.
@@ -445,8 +452,23 @@ protected:
* was moved from
* - change of the parent container, where the file or container
* was moved to
+ *
+ * The optional target must be an element inside the container
+ * specified in the URI. If the target is not empty, only this
+ * element will be checked for changes. Otherwise the full container
+ * is scanned.
+ */
+ void OnContainerUpdate(const string& uri, long containerUpdateId, const string& target = string());
+
+ /**
+ * Thread action to check for updates
+ *
+ * This should be used to determine changes on the containers. It should
+ * be overridden by the implementor. However, it is not required and
+ * therefore empty by default.
+ *
*/
- void OnContainerUpdate(const string& uri, long containerUpdateId, const string& changesUri = string());
+ virtual void Action();
};
diff --git a/include/pluginManager.h b/include/pluginManager.h
index 8520b01..563d4fb 100644
--- a/include/pluginManager.h
+++ b/include/pluginManager.h
@@ -20,6 +20,7 @@ class cPluginManager {
public:
typedef std::list< boost::shared_ptr<cMediaProfiler> > ProfilerList;
+ typedef std::list< boost::shared_ptr<cUPnPResourceProvider> > ProviderList;
cPluginManager();
virtual ~cPluginManager();
@@ -29,6 +30,7 @@ public:
int Count() const;
const ProfilerList& GetProfilers() const;
+ const ProviderList& GetProviders() const;
cUPnPResourceProvider* CreateProvider(const string& schema);
private:
@@ -59,8 +61,9 @@ private:
typedef std::map<string, ResourceProviderFuncPtr > ProviderMap;
DLLList dlls;
- ProviderMap providers;
+ ProviderMap providerFactory;
ProfilerList profilers;
+ ProviderList providers;
};
diff --git a/include/server.h b/include/server.h
index 3f24725..48e85a9 100644
--- a/include/server.h
+++ b/include/server.h
@@ -10,9 +10,9 @@
#include <string>
#include <upnp/upnp.h>
-#include "../include/webserver.h"
#include "../include/config.h"
#include "../include/service.h"
+#include "../include/webserver.h"
using namespace std;
diff --git a/media/mediaManager.cpp b/media/mediaManager.cpp
index 409b49d..55c8164 100644
--- a/media/mediaManager.cpp
+++ b/media/mediaManager.cpp
@@ -124,17 +124,63 @@ IdList cMediaManager::GetContainerUpdateIDs(bool unevented){
return list;
}
-void cMediaManager::OnContainerUpdate(string uri, long updateID){
- ++systemUpdateID;
+void cMediaManager::OnContainerUpdate(const string& uri, long updateID, const string& target){
+ systemUpdateID = time(NULL);
- eventedContainerUpdateIDs[tools::GenerateUUIDFromURL(uri)] = updateID;
+ string objectID = tools::GenerateUUIDFromURL(uri);
- scanDirectories.push_back(uri);
+ eventedContainerUpdateIDs[objectID] = updateID;
+
+ // If we cannot update the containerUpdateID, we do not know, it is very likely, that this container
+ // does not exist. Therefore, we cannot scan this directory successfully.
+ if(!UpdateContainerUpdateId(objectID, updateID)) return;
+
+ stringstream ss;
+
+ ss << uri;
+
+ if(!target.empty()){
+ ss << target;
+ }
+
+ scanTargets.push_back(ss.str());
// Start scanning for changed files.
Start();
}
+bool cMediaManager::UpdateContainerUpdateId(const string& objectID, long int updateID){
+ stringstream update;
+
+ update << "UPDATE " << db::Metadata << " SET "
+ << " `" << property::object::KEY_OBJECT_UPDATE_ID << "`=" << updateID
+ << " WHERE `" << property::object::KEY_OBJECTID << "`"
+ << " = :objectID";
+
+ try {
+ tntdb::Statement stmt = connection.prepare(update.str());
+
+ stmt.setString("objectID", objectID);
+
+ if(stmt.execute() == 0){
+ isyslog("UPnP\tContainer with ID '%s' not found. Cannot update it.", objectID.c_str());
+ return false;
+ }
+
+ dsyslog("UPnP\tUpdated container with ID '%s': %ld", objectID.c_str(), updateID);
+
+ return true;
+
+ } catch (const std::exception& e) {
+ esyslog("UPnP\tException occurred while updating container with ID '%s': %s", objectID.c_str(), e.what());
+
+ return false;
+ }
+
+ return false;
+
+}
+
StringList cMediaManager::GetSearchCapabilities() const {
StringList list;
@@ -391,15 +437,6 @@ cMediaManager::BrowseFlag cMediaManager::ToBrowseFlag(const std::string& browseF
bool cMediaManager::Initialise(){
- pluginManager = new upnp::cPluginManager();
-
- if(!pluginManager->LoadPlugins(pluginDirectory)){
- esyslog("UPnP\tError while loading upnp plugin directory '%s'", pluginDirectory.c_str());
- return false;
- } else {
- dsyslog("UPnP\tFound %d plugins", pluginManager->Count());
- }
-
try {
stringstream ss;
ss << "sqlite:" << databaseFile;
@@ -408,103 +445,101 @@ bool cMediaManager::Initialise(){
dsyslog("UPNP\tPreparing database structure...");
- if(CheckIntegrity()) return true;
-
- connection.beginTransaction();
-
- ss.str(string());
-
- ss << "CREATE TABLE " << db::Metadata
- << "("
- << "`" << property::object::KEY_OBJECTID << "` TEXT PRIMARY KEY,"
- << "`" << property::object::KEY_PARENTID << "` TEXT NOT NULL,"
- << "`" << property::object::KEY_TITLE << "` TEXT NOT NULL,"
- << "`" << property::object::KEY_CLASS << "` TEXT NOT NULL,"
- << "`" << property::object::KEY_RESTRICTED << "` INTEGER NOT NULL,"
- << "`" << property::object::KEY_CREATOR << "` TEXT,"
- << "`" << property::object::KEY_DESCRIPTION << "` TEXT,"
- << "`" << property::object::KEY_LONG_DESCRIPTION << "` TEXT,"
- << "`" << property::object::KEY_DATE << "` TEXT,"
- << "`" << property::object::KEY_LANGUAGE << "` TEXT,"
- << "`" << property::object::KEY_CHANNEL_NR << "` INTEGER,"
- << "`" << property::object::KEY_CHANNEL_NAME << "` TEXT,"
- << "`" << property::object::KEY_SCHEDULED_START << "` TEXT,"
- << "`" << property::object::KEY_SCHEDULED_END << "` TEXT"
- << "`" << property::object::KEY_OBJECT_UPDATE_ID << "` INTEGER"
- << ")";
-
- tntdb::Statement objectTable = connection.prepare(ss.str());
-
- objectTable.execute();
-
- ss.str(string());
-
- ss << "CREATE TABLE " << db::Details
- << "("
- << " `propertyID` INTEGER PRIMARY KEY,"
- << " `" << property::object::KEY_OBJECTID << "` TEXT "
- << " REFERENCES metadata (`"<< property::object::KEY_OBJECTID <<"`) ON DELETE CASCADE ON UPDATE CASCADE,"
- << " `property` TEXT,"
- << " `value` TEXT"
- << ")";
-
- tntdb::Statement detailsTable = connection.prepare(ss.str());
-
- detailsTable.execute();
-
- ss.str(string());
-
- ss << "CREATE TABLE " << db::Resources
- << "("
- << " resourceID INTEGER PRIMARY KEY,"
- << " `" << property::object::KEY_OBJECTID << "` TEXT "
- << " REFERENCES metadata (`"<< property::object::KEY_OBJECTID <<"`) ON DELETE CASCADE ON UPDATE CASCADE,"
- << "`" << property::resource::KEY_RESOURCE << "` TEXT NOT NULL,"
- << "`" << property::resource::KEY_PROTOCOL_INFO << "` TEXT NOT NULL,"
- << "`" << property::resource::KEY_SIZE << "` INTEGER,"
- << "`" << property::resource::KEY_DURATION << "` TEXT,"
- << "`" << property::resource::KEY_RESOLUTION << "` TEXT,"
- << "`" << property::resource::KEY_BITRATE << "` INTEGER,"
- << "`" << property::resource::KEY_SAMPLE_FREQUENCY << "` INTEGER,"
- << "`" << property::resource::KEY_BITS_PER_SAMPLE << "` INTEGER,"
- << "`" << property::resource::KEY_NR_AUDIO_CHANNELS << "` INTEGER,"
- << "`" << property::resource::KEY_COLOR_DEPTH << "` INTEGER"
- << ")";
-
- tntdb::Statement resourcesTable = connection.prepare(ss.str());
-
- resourcesTable.execute();
-
- ss.str(string());
-
- ss << "INSERT INTO " << db::Metadata << " ("
- << "`" << property::object::KEY_OBJECTID << "`, "
- << "`" << property::object::KEY_PARENTID << "`, "
- << "`" << property::object::KEY_TITLE << "`, "
- << "`" << property::object::KEY_CLASS << "`, "
- << "`" << property::object::KEY_RESTRICTED << "`, "
- << "`" << property::object::KEY_CREATOR << "`, "
- << "`" << property::object::KEY_DESCRIPTION << "`, "
- << "`" << property::object::KEY_LONG_DESCRIPTION << "`) "
- << " VALUES (:objectID, :parentID, :title, :class, :restricted, :creator, :description, :longDescription)";
-
- tntdb::Statement rootContainer = connection.prepare(ss.str());
-
- const cMediaServer::Description desc = cMediaServer::GetInstance()->GetServerDescription();
-
- rootContainer.setString("objectID", "0")
- .setString("parentID", "-1")
- .setString("title", desc.friendlyName)
- .setString("creator", desc.manufacturer)
- .setString("class", "object.container")
- .setBool("restricted", true)
- .setString("description", desc.modelName)
- .setString("longDescription", desc.modelDescription)
- .execute();
-
- connection.commitTransaction();
-
- return true;
+ if(!CheckIntegrity()){
+ connection.beginTransaction();
+
+ ss.str(string());
+
+ ss << "CREATE TABLE " << db::Metadata
+ << "("
+ << "`" << property::object::KEY_OBJECTID << "` TEXT PRIMARY KEY,"
+ << "`" << property::object::KEY_PARENTID << "` TEXT NOT NULL,"
+ << "`" << property::object::KEY_TITLE << "` TEXT NOT NULL,"
+ << "`" << property::object::KEY_CLASS << "` TEXT NOT NULL,"
+ << "`" << property::object::KEY_RESTRICTED << "` INTEGER NOT NULL,"
+ << "`" << property::object::KEY_CREATOR << "` TEXT,"
+ << "`" << property::object::KEY_DESCRIPTION << "` TEXT,"
+ << "`" << property::object::KEY_LONG_DESCRIPTION << "` TEXT,"
+ << "`" << property::object::KEY_DATE << "` TEXT,"
+ << "`" << property::object::KEY_LANGUAGE << "` TEXT,"
+ << "`" << property::object::KEY_CHANNEL_NR << "` INTEGER,"
+ << "`" << property::object::KEY_CHANNEL_NAME << "` TEXT,"
+ << "`" << property::object::KEY_SCHEDULED_START << "` TEXT,"
+ << "`" << property::object::KEY_SCHEDULED_END << "` TEXT"
+ << "`" << property::object::KEY_OBJECT_UPDATE_ID << "` INTEGER"
+ << ")";
+
+ tntdb::Statement objectTable = connection.prepare(ss.str());
+
+ objectTable.execute();
+
+ ss.str(string());
+
+ ss << "CREATE TABLE " << db::Details
+ << "("
+ << " `propertyID` INTEGER PRIMARY KEY,"
+ << " `" << property::object::KEY_OBJECTID << "` TEXT "
+ << " REFERENCES metadata (`"<< property::object::KEY_OBJECTID <<"`) ON DELETE CASCADE ON UPDATE CASCADE,"
+ << " `property` TEXT,"
+ << " `value` TEXT"
+ << ")";
+
+ tntdb::Statement detailsTable = connection.prepare(ss.str());
+
+ detailsTable.execute();
+
+ ss.str(string());
+
+ ss << "CREATE TABLE " << db::Resources
+ << "("
+ << " resourceID INTEGER PRIMARY KEY,"
+ << " `" << property::object::KEY_OBJECTID << "` TEXT "
+ << " REFERENCES metadata (`"<< property::object::KEY_OBJECTID <<"`) ON DELETE CASCADE ON UPDATE CASCADE,"
+ << "`" << property::resource::KEY_RESOURCE << "` TEXT NOT NULL,"
+ << "`" << property::resource::KEY_PROTOCOL_INFO << "` TEXT NOT NULL,"
+ << "`" << property::resource::KEY_SIZE << "` INTEGER,"
+ << "`" << property::resource::KEY_DURATION << "` TEXT,"
+ << "`" << property::resource::KEY_RESOLUTION << "` TEXT,"
+ << "`" << property::resource::KEY_BITRATE << "` INTEGER,"
+ << "`" << property::resource::KEY_SAMPLE_FREQUENCY << "` INTEGER,"
+ << "`" << property::resource::KEY_BITS_PER_SAMPLE << "` INTEGER,"
+ << "`" << property::resource::KEY_NR_AUDIO_CHANNELS << "` INTEGER,"
+ << "`" << property::resource::KEY_COLOR_DEPTH << "` INTEGER"
+ << ")";
+
+ tntdb::Statement resourcesTable = connection.prepare(ss.str());
+
+ resourcesTable.execute();
+
+ ss.str(string());
+
+ ss << "INSERT INTO " << db::Metadata << " ("
+ << "`" << property::object::KEY_OBJECTID << "`, "
+ << "`" << property::object::KEY_PARENTID << "`, "
+ << "`" << property::object::KEY_TITLE << "`, "
+ << "`" << property::object::KEY_CLASS << "`, "
+ << "`" << property::object::KEY_RESTRICTED << "`, "
+ << "`" << property::object::KEY_CREATOR << "`, "
+ << "`" << property::object::KEY_DESCRIPTION << "`, "
+ << "`" << property::object::KEY_LONG_DESCRIPTION << "`) "
+ << " VALUES (:objectID, :parentID, :title, :class, :restricted, :creator, :description, :longDescription)";
+
+ tntdb::Statement rootContainer = connection.prepare(ss.str());
+
+ const cMediaServer::Description desc = cMediaServer::GetInstance()->GetServerDescription();
+
+ rootContainer.setString("objectID", "0")
+ .setString("parentID", "-1")
+ .setString("title", desc.friendlyName)
+ .setString("creator", desc.manufacturer)
+ .setString("class", "object.container")
+ .setBool("restricted", true)
+ .setString("description", desc.modelName)
+ .setString("longDescription", desc.modelDescription)
+ .execute();
+
+ connection.commitTransaction();
+ }
} catch (const std::exception& e) {
esyslog("UPnP\tException occurred while initializing database '%s': %s", databaseFile.c_str(), e.what());
@@ -514,7 +549,25 @@ bool cMediaManager::Initialise(){
return false;
}
- return false;
+ dsyslog("UPNP\tLoading Plugins...");
+ pluginManager = new upnp::cPluginManager();
+
+ if(!pluginManager->LoadPlugins(pluginDirectory)){
+ esyslog("UPnP\tError while loading upnp plugin directory '%s'", pluginDirectory.c_str());
+ return false;
+ } else {
+ dsyslog("UPnP\tFound %d plugins", pluginManager->Count());
+ }
+
+ dsyslog("UPNP\tScanning directories...");
+ // Do an full initial scan on startup.
+ upnp::cPluginManager::ProviderList providers = pluginManager->GetProviders();
+ for(upnp::cPluginManager::ProviderList::iterator it = providers.begin(); it != providers.end(); ++it){
+ scanTargets.push_back((*it)->GetRootContainer());
+ }
+ Start();
+
+ return true;
}
bool cMediaManager::CheckIntegrity(){
@@ -622,7 +675,69 @@ void cMediaManager::SetPluginDirectory(const string& directory){
}
void cMediaManager::Action(){
+ string uri;
+ while(!scanTargets.empty()){
+ uri = scanTargets.front();
+ boost::shared_ptr<cUPnPResourceProvider> provider(CreateResourceProvider(uri));
+ if(!ScanURI(uri, provider.get())){
+ isyslog("UPnP\tAn error occured while scanning '%s'!", uri.c_str());
+ }
+ scanTargets.pop_front();
+ }
+}
+
+bool cMediaManager::ScanURI(const string& uri, cUPnPResourceProvider* provider){
+ if (provider == NULL) return false;
+
+ cMetadata metadata;
+
+ if(!provider->IsContainer(uri)){
+
+ if(!RefreshObject(metadata)){
+ isyslog("UPnP\tUnable to save the metadata of '%s'", uri.c_str());
+ return false;
+ }
+ } else {
+ if(!provider->GetMetadata(uri, metadata)){
+ isyslog("UPnP\tUnable to get the metadata of '%s'", uri.c_str());
+ return false;
+ }
+
+ if(!RefreshObject(metadata)){
+ isyslog("UPnP\tUnable to save the metadata of '%s'", uri.c_str());
+ return false;
+ }
+
+ StringList entries = provider->GetContainerEntries(uri);
+ stringstream ss, uristrm;
+
+ ss << "DELETE FROM " << db::Metadata << " WHERE "
+ << " `" << property::object::KEY_PARENTID << "`"
+ << " = '" << tools::GenerateUUIDFromURL(uri) << "'";
+
+ for(StringList::iterator it = entries.begin(); it != entries.end(); ++it){
+ uristrm.str(string());
+ uristrm << uri << *it;
+ ss << " AND"
+ << " `" << property::object::KEY_OBJECTID << "`"
+ << " != '" << tools::GenerateUUIDFromURL(uristrm.str()) << "'";
+ }
+
+ cout << ss.str() << endl;
+
+ for(StringList::iterator it = entries.begin(); it != entries.end(); ++it){
+ uristrm.str(string());
+ uristrm << uri << *it;
+ ScanURI(uristrm.str(), provider);
+ }
+ }
+
+ return true;
+}
+
+bool cMediaManager::RefreshObject(const cMetadata& metadata){
+ return true;
}
} // namespace upnp
diff --git a/media/pluginManager.cpp b/media/pluginManager.cpp
index a76af8f..59dccfa 100644
--- a/media/pluginManager.cpp
+++ b/media/pluginManager.cpp
@@ -5,10 +5,13 @@
* Author: savop
*/
+#include "../include/server.h"
+#include "../include/media/mediaManager.h"
#include "../include/pluginManager.h"
#include "../include/tools/string.h"
#include "../include/tools/uuid.h"
#include <string>
+#include <sstream>
#include <dlfcn.h>
#include <dirent.h>
@@ -112,9 +115,25 @@ cMetadata::PropertyRange cMetadata::GetAllProperties() {
}
cMetadata::Property& cMetadata::GetPropertyByKey(const string& property) {
- return (*properties.find(property)).second;
+ PropertyMap::iterator it = properties.find(property);
+
+ return (it != properties.end()) ? (*it).second : cMetadata::Property::Empty;
}
+string cMetadata::ToString() {
+ stringstream ss;
+
+ cMetadata::PropertyRange properties = GetAllProperties();
+
+ for(PropertyMap::iterator it = properties.first; it != properties.second; ++it){
+ ss << "'" << (*it).first << "' ('" << (*it).second.GetKey() << "') = '" << (*it).second.GetString() << "'" << endl;
+ }
+
+ return ss.str();
+}
+
+cMetadata::Property cMetadata::Property::Empty;
+
cMetadata::Property::Property(string key, bool val)
: key(key)
{
@@ -222,7 +241,7 @@ bool cUPnPResourceProvider::GetMetadata(const string& uri, cMetadata& metadata){
metadata.SetObjectIDByUri(uri);
metadata.SetParentIDByUri(uri.substr(0,uri.find_last_of("/")));
metadata.SetProperty(cMetadata::Property(property::object::KEY_TITLE, uri.substr(uri.find_last_of("/")+1)));
- metadata.SetProperty(cMetadata::Property(property::object::KEY_CLASS, "object.container"));
+ metadata.SetProperty(cMetadata::Property(property::object::KEY_CLASS, string("object.container")));
metadata.SetProperty(cMetadata::Property(property::object::KEY_RESTRICTED, true));
return true;
@@ -252,7 +271,11 @@ bool cUPnPResourceProvider::Seek(size_t offset, int origin){
void cUPnPResourceProvider::Close(){
}
+void cUPnPResourceProvider::OnContainerUpdate(const string& uri, long int cUID, const string& target){
+ cMediaServer::GetInstance()->GetManager().OnContainerUpdate(uri, cUID, target);
+}
+void cUPnPResourceProvider::Action(){}
upnp::cPluginManager::cPluginManager()
{}
@@ -263,13 +286,17 @@ const cPluginManager::ProfilerList& upnp::cPluginManager::GetProfilers() const {
return profilers;
}
+const cPluginManager::ProviderList& upnp::cPluginManager::GetProviders() const {
+ return providers;
+}
+
int upnp::cPluginManager::Count() const {
return dlls.size();
}
cUPnPResourceProvider* upnp::cPluginManager::CreateProvider(const string& schema) {
- if(providers[schema])
- return providers[schema]();
+ if(providerFactory[schema])
+ return providerFactory[schema]();
else
return NULL;
}
@@ -300,7 +327,9 @@ bool upnp::cPluginManager::LoadPlugins(const string& directory){
if(dll->IsProvider()){
boost::shared_ptr<cUPnPResourceProvider> provider((cUPnPResourceProvider*)(dll->GetFunc()()));
- providers[provider->ProvidesSchema()] = (ResourceProviderFuncPtr)dll->GetFunc();
+ providerFactory[provider->ProvidesSchema()] = (ResourceProviderFuncPtr)dll->GetFunc();
+ providers.push_back( provider );
+ provider->Start();
} else {
boost::shared_ptr<cMediaProfiler> profiler((cMediaProfiler*)(dll->GetFunc()()));
profilers.push_back( profiler );
diff --git a/plugins/provider/vdrProvider/vdrProvider.cpp b/plugins/provider/vdrProvider/vdrProvider.cpp
index bcf06a4..fa7a32b 100644
--- a/plugins/provider/vdrProvider/vdrProvider.cpp
+++ b/plugins/provider/vdrProvider/vdrProvider.cpp
@@ -6,6 +6,7 @@
*/
#include <plugin.h>
+#include <vdr/epg.h>
#include <vdr/channels.h>
#include <vdr/tools.h>
#include <vdr/config.h>
@@ -13,34 +14,45 @@
#include <sstream>
#include <tools.h>
#include <vdr/thread.h>
+#include <iostream>
using namespace std;
namespace upnp {
-class VdrProvider : public cUPnPResourceProvider, cThread {
+class VdrProvider : public cUPnPResourceProvider {
private:
- int lastUpdateID;
+ time_t lastModified;
+
+ bool IsRootContainer(const string& uri){
+ if(uri.find(GetRootContainer(), 0) != 0){
+ isyslog("VdrProvider\tUri does not contain the root.");
+ return false;
+ } else {
+ return true;
+ }
+ }
public:
VdrProvider()
- : lastUpdateID(0)
+ : lastModified(0)
{}
+ virtual ~VdrProvider(){
+ Cancel(2);
+ }
+
virtual string ProvidesSchema(){ return "vdr"; }
virtual string GetRootContainer(){
return ProvidesSchema() + "://";
}
- virtual cUPnPResourceProvider::EntryList GetContainerEntries(const string& uri){
- if(uri.find(GetRootContainer(), 0) != 0){
- isyslog("VdrProvider\tUri does not contain the root.");
- return cUPnPResourceProvider::EntryList();
- }
+ virtual StringList GetContainerEntries(const string& uri){
+ if(!IsRootContainer(uri)) return StringList();
- EntryList list;
+ StringList list;
// Check if this is the root:
if(uri.compare(GetRootContainer()) == 0){
@@ -64,37 +76,82 @@ public:
return false;
}
- virtual long GetContainerUpdateId(const string&){
- // TODO: provide a container update id
- return lastUpdateID;
+ virtual long GetContainerUpdateId(const string& uri){
+ if(IsRootContainer(uri)) return 0;
+
+ // We have no containers. So just return the last modification date.
+ // Containers like groups are about to come soon.
+ return (long)lastModified;
}
virtual bool GetMetadata(const string& uri, cMetadata& metadata){
- if(uri.find(GetRootContainer(), 0) != 0){
- isyslog("VdrProvider\tUri does not contain the root.");
- return false;
- }
+ if(!IsRootContainer(uri)) return false;
- return false;
+ if(!cUPnPResourceProvider::GetMetadata(uri, metadata)) return false;
+
+ metadata.SetProperty(cMetadata::Property(property::object::KEY_PARENTID, string("0")));
+ metadata.SetProperty(cMetadata::Property(property::object::KEY_TITLE, string("VDR Live-TV")));
+ metadata.SetProperty(cMetadata::Property("dlna:containerType", string("Tuner_1_0")));
+
+ return true;
}
virtual string GetHTTPUri(const string& uri, const string& currentIP){
- if(uri.find(GetRootContainer(), 0) != 0){
- isyslog("VdrProvider\tUri does not contain the root.");
- return string();
- }
+ if(!IsRootContainer(uri)) return string();
int port = 3000;
stringstream ss;
- ss << "http://" << currentIP << ":" << port << "/TS/" << uri.substr(6);
+ ss << "http://" << currentIP << ":" << port
+ << "/"
+ << "EXT;"
+ << "PROG=cat;"
+ << "DLNA_contentFeatures=DLNA.ORG_PN=MPEG_TS_SD_EU_ISO+DLNA.ORG_OP=00+DLNA.ORG_CI=0+DLNA.ORG_FLAGS=ED100000000000000000000000000000"
+ << "/"
+ << uri.substr(6);
return ss.str();
}
virtual void Action(){
+ dsyslog("VdrProvider\tStarting vdrProvider thread");
+
+ const cSchedules* Schedules;
+ long now = 0;
+ bool modified = false;
+ while(Running()){
+ now = time(NULL);
+
+ if(!Channels.BeingEdited() && Channels.Modified() > 0){
+ OnContainerUpdate(GetRootContainer(), now);
+ modified = true;
+ }
+
+ { // Reduce Scope of Schedules lock.
+ cSchedulesLock lock;
+ Schedules = cSchedules::Schedules(lock);
+ // iterate over all the schedules, find those, which were modified and tell
+ // it to the media manager
+ for(cSchedule* Schedule = Schedules->First(); Schedule; Schedule = Schedules->Next(Schedule))
+ {
+ if(Schedule->Modified() > lastModified && Schedule->PresentSeenWithin(30)){
+ OnContainerUpdate(GetRootContainer(), now, *Schedule->ChannelID().ToString());
+ modified = true;
+ }
+ }
+ }
+
+ if(modified){
+ modified = false;
+ lastModified = now;
+ }
+
+ sleep(2);
+ }
+
+ dsyslog("VdrProvider\tStopping vdrProvider thread");
}
};