summaryrefslogtreecommitdiff
path: root/media
diff options
context:
space:
mode:
Diffstat (limited to 'media')
-rw-r--r--media/mediaManager.cpp337
-rw-r--r--media/pluginManager.cpp39
2 files changed, 260 insertions, 116 deletions
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 );