summaryrefslogtreecommitdiff
path: root/database
diff options
context:
space:
mode:
authorDenis Loh <denis.loh@gmail.com>2009-10-24 14:24:17 +0200
committerDenis Loh <denis.loh@gmail.com>2009-10-24 14:24:17 +0200
commit1cf955a715830130b7add8c1183d65b0f442fd23 (patch)
treec9d03961e9f83b1100ef6010a4a53063f127aa5d /database
downloadvdr-plugin-upnp-1cf955a715830130b7add8c1183d65b0f442fd23.tar.gz
vdr-plugin-upnp-1cf955a715830130b7add8c1183d65b0f442fd23.tar.bz2
Initial commit
Diffstat (limited to 'database')
-rw-r--r--database/database.cpp274
-rw-r--r--database/database.h834
-rw-r--r--database/metadata.cpp389
-rw-r--r--database/metadata.h60
-rw-r--r--database/object.cpp1702
-rw-r--r--database/object.h397
-rw-r--r--database/resources.cpp282
-rw-r--r--database/resources.h51
8 files changed, 3989 insertions, 0 deletions
diff --git a/database/database.cpp b/database/database.cpp
new file mode 100644
index 0000000..22c41cd
--- /dev/null
+++ b/database/database.cpp
@@ -0,0 +1,274 @@
+/*
+ * File: database.h
+ * Author: savop
+ *
+ * Created on 3. September 2009, 22:20
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <sqlite3.h>
+#include "database.h"
+#include "../common.h"
+#include "object.h"
+#include "../upnp.h"
+
+cSQLiteDatabase* cSQLiteDatabase::mInstance = NULL;
+
+cSQLiteDatabase::cSQLiteDatabase(){
+ this->mActiveTransaction = false;
+ this->mDatabase = NULL;
+ this->mLastRow = NULL;
+ this->mRows = NULL;
+}
+
+cSQLiteDatabase::~cSQLiteDatabase(){
+ sqlite3_close(this->mDatabase);
+}
+
+cSQLiteDatabase* cSQLiteDatabase::getInstance(){
+ if(cSQLiteDatabase::mInstance == NULL){
+ cSQLiteDatabase::mInstance = new cSQLiteDatabase;
+ DatabaseLocker.Wait();
+ cSQLiteDatabase::mInstance->initialize();
+ }
+
+ if(cSQLiteDatabase::mInstance != NULL)
+ return cSQLiteDatabase::mInstance;
+ else
+ return NULL;
+}
+
+int cSQLiteDatabase::execStatement(const char* Statement){
+ char* Error;
+ if(!this->mDatabase){
+ ERROR("Database not open. Cannot continue");
+ return -1;
+ }
+ this->mRows = new cRows;
+#ifdef SQLITE_PRINT_STATEMENTS
+ MESSAGE("SQLite: %s", Statement);
+#endif
+ if(sqlite3_exec(this->mDatabase, Statement, cSQLiteDatabase::getResultRow, (cSQLiteDatabase*)this, &Error)!=SQLITE_OK){
+ ERROR("Database error: %s", Error);
+ ERROR("Statement was: %s", Statement);
+ sqlite3_free(Error);
+ return -1;
+ }
+
+ sqlite3_free(Error);
+ return 0;
+}
+
+int cSQLiteDatabase::getResultRow(void* DB, int NumCols, char** Values, char** ColNames){
+ cRow* Row = new cRow;
+ Row->ColCount = NumCols;
+ Row->Columns = new char*[NumCols];
+ Row->Values = new char*[NumCols];
+ for(int i=0; i < NumCols; i++){
+ Row->Columns[i] = strdup0(ColNames[i]);
+ Row->Values[i] = strdup0(Values[i]);
+ }
+ cSQLiteDatabase* Database = (cSQLiteDatabase*)DB;
+ Database->mRows->Add(Row);
+ return 0;
+}
+
+cRows::cRows(){
+ this->mLastRow = NULL;
+}
+
+cRows::~cRows(){
+ this->mLastRow = NULL;
+}
+
+bool cRows::fetchRow(cRow** Row){
+ if(this->mLastRow==NULL){
+ this->mLastRow = this->First();
+ }
+ else {
+ this->mLastRow = this->Next(this->mLastRow);
+ }
+ if(this->mLastRow != NULL){
+ *Row = this->mLastRow;
+ return true;
+ }
+ else {
+ *Row = NULL;
+ return false;
+ }
+ return false;
+}
+
+cRow::cRow(){
+ this->currentCol = 0;
+ this->ColCount = 0;
+ this->Columns = NULL;
+ this->Values = NULL;
+}
+
+cRow::~cRow(){
+ for(int i=0;i<this->ColCount;i++){
+ delete Columns[i];
+ delete Values[i];
+ }
+ this->Columns = NULL;
+ this->Values = NULL;
+}
+
+bool cRow::fetchColumn(cString* Column, cString* Value){
+ char *Col, *Val;
+ bool ret = this->fetchColumn(&Col, &Val);
+ if(ret){
+ *Column = cString(Col,true);
+ *Value = cString(Val,true);
+ }
+ return ret;
+}
+
+bool cRow::fetchColumn(char** Column, char** Value){
+ if(currentCol>=this->ColCount){
+ return false;
+ }
+ #ifdef SQLITE_PRINT_FETCHES
+ MESSAGE("Fetching column %s='%s' (%d/%d)", this->Columns[currentCol], this->Values[currentCol], currentCol+1, this->ColCount);
+ #endif
+ *Column = strdup0(this->Columns[currentCol]);
+ *Value = strcasecmp(this->Values[currentCol],"NULL")?strdup0(this->Values[currentCol]):NULL;
+ currentCol++;
+ return true;
+}
+
+int cSQLiteDatabase::initialize(){
+ int ret;
+ cString File = cString::sprintf("%s/%s", cPluginUpnp::getConfigDirectory(), SQLITE_DB_FILE);
+ if((ret = sqlite3_open(File, &this->mDatabase))){
+ ERROR("Unable to open database file %s (Error code: %d)!", *File, ret);
+ sqlite3_close(this->mDatabase);
+ return -1;
+ }
+ MESSAGE("Database file %s opened.", *File);
+ if(this->initializeTables()){
+ ERROR("Error while creating tables");
+ return -1;
+ }
+ else if(this->initializeTriggers()){
+ ERROR("Error while setting triggers");
+ return -1;
+ }
+ return 0;
+}
+
+void cSQLiteDatabase::startTransaction(){
+ if(this->mActiveTransaction){
+ if(this->mAutoCommit){
+ this->commitTransaction();
+ }
+ else {
+ this->rollbackTransaction();
+ }
+ }
+ this->execStatement("BEGIN TRANSACTION");
+ this->mActiveTransaction = true;
+}
+
+void cSQLiteDatabase::commitTransaction(){
+ this->execStatement("COMMIT TRANSACTION");
+ this->mActiveTransaction = false;
+}
+
+void cSQLiteDatabase::rollbackTransaction(){
+ this->execStatement("ROLLBACK TRANSACTION");
+ this->mActiveTransaction = false;
+}
+
+int cSQLiteDatabase::initializeTables(){
+ int ret = 0;
+ this->startTransaction();
+ if(this->execStatement(SQLITE_CREATE_TABLE_SYSTEM)==-1) ret = -1;
+ if(this->execStatement(SQLITE_CREATE_TABLE_PRIMARY_KEYS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_CREATE_TABLE_ALBUMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_CREATE_TABLE_AUDIOBROADCASTS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_CREATE_TABLE_AUDIOITEMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_CREATE_TABLE_CONTAINER)==-1) ret = -1;
+ if(this->execStatement(SQLITE_CREATE_TABLE_IMAGEITEMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_CREATE_TABLE_ITEMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_CREATE_TABLE_MOVIES)==-1) ret = -1;
+ if(this->execStatement(SQLITE_CREATE_TABLE_OBJECTS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_CREATE_TABLE_PHOTOS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_CREATE_TABLE_PLAYLISTS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_CREATE_TABLE_RESOURCES)==-1) ret = -1;
+ if(this->execStatement(SQLITE_CREATE_TABLE_SEARCHCLASS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_CREATE_TABLE_VIDEOBROADCASTS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_CREATE_TABLE_VIDEOITEMS)==-1) ret = -1;
+ if(ret){
+ this->rollbackTransaction();
+ }
+ else {
+ this->commitTransaction();
+ }
+ return ret;
+}
+
+int cSQLiteDatabase::initializeTriggers(){
+ int ret = 0;
+ this->startTransaction();
+ if(this->execStatement(SQLITE_TRIGGER_UPDATE_SYSTEM)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_UPDATE_OBJECTID)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_D_AUDIOITEMS_AUDIOBROADCASTS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_D_CONTAINERS_ALBUMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_D_CONTAINERS_PLAYLISTS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_D_CONTAINERS_SEARCHCLASSES)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_D_IMAGEITEMS_PHOTOS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_D_ITEMS_AUDIOITEMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_D_ITEMS_IMAGEITEMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_D_ITEMS_ITEMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_D_ITEMS_VIDEOITEMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_D_OBJECTS_OBJECTS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_D_OBJECT_CONTAINERS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_D_OBJECT_ITEMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_D_OBJECT_RESOURCES)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_D_VIDEOITEMS_MOVIES)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_D_VIDEOITEMS_VIDEOBROADCASTS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_I_AUDIOITEMS_AUDIOBROADCASTS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_I_CONTAINERS_ALBUMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_I_CONTAINERS_PLAYLISTS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_I_CONTAINERS_SEARCHCLASSES)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_I_IMAGEITEMS_PHOTOS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_I_ITEMS_AUDIOITEMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_I_ITEMS_IMAGEITEMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_I_ITEMS_ITEMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_I_ITEMS_VIDEOITEMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_I_OBJECTS_OBJECTS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_I_OBJECT_CONTAINERS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_I_OBJECT_ITEMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_I_OBJECT_RESOURCES)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_I_VIDEOITEMS_MOVIES)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_I_VIDEOITEMS_VIDEOBROADCASTS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_U_AUDIOITEMS_AUDIOBROADCASTS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_U_CONTAINERS_ALBUMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_U_CONTAINERS_PLAYLISTS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_U_CONTAINERS_SEARCHCLASSES)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_U_IMAGEITEMS_PHOTOS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_U_ITEMS_AUDIOITEMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_U_ITEMS_IMAGEITEMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_U_ITEMS_ITEMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_U_ITEMS_VIDEOITEMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_U_OBJECTS_OBJECTS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_U_OBJECT_CONTAINERS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_U_OBJECT_ITEMS)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_U_OBJECT_RESOURCES)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_U_VIDEOITEMS_MOVIES)==-1) ret = -1;
+ if(this->execStatement(SQLITE_TRIGGER_U_VIDEOITEMS_VIDEOBROADCASTS)==-1) ret = -1;
+ if(ret){
+ this->rollbackTransaction();
+ }
+ else {
+ this->commitTransaction();
+ }
+ return ret;
+}
+
+long cSQLiteDatabase::getLastInsertRowID() const {
+ return (long)sqlite3_last_insert_rowid(this->mDatabase);
+} \ No newline at end of file
diff --git a/database/database.h b/database/database.h
new file mode 100644
index 0000000..fe91f2b
--- /dev/null
+++ b/database/database.h
@@ -0,0 +1,834 @@
+/*
+ * File: database.h
+ * Author: savop
+ *
+ * Created on 3. September 2009, 22:20
+ */
+
+#ifndef _DATABASE_H
+#define _DATABASE_H
+
+#include <sqlite3.h>
+#include <vdr/tools.h>
+#include "../common.h"
+
+//#define SQLITE_PRINT_STATEMENTS
+//#define SQLITE_PRINT_FETCHES
+#define SQLITE_CASCADE_DELETES
+
+#define PK_OBJECTS TOSTRING(1)
+#define PK_RESOURCES TOSTRING(2)
+#define PK_SEARCHCLASSES TOSTRING(3)
+
+#define SQLITE_FIRST_CUSTOMID TOSTRING(0)
+
+#define SQLITE_COLUMN_NAME_LENGTH 64
+
+#define SQLITE_TABLE_RESOURCES "Resources"
+#define SQLITE_TABLE_OBJECTS "Objects"
+#define SQLITE_TABLE_ITEMS "Items"
+#define SQLITE_TABLE_CONTAINERS "Containers"
+#define SQLITE_TABLE_VIDEOITEMS "VideoItems"
+#define SQLITE_TABLE_AUDIOITEMS "AudioItems"
+#define SQLITE_TABLE_IMAGEITEMS "ImageItems"
+#define SQLITE_TABLE_VIDEOBROADCASTS "VideoBroadcasts"
+#define SQLITE_TABLE_AUDIOBROADCASTS "AudioBroadcasts"
+#define SQLITE_TABLE_MOVIES "Movies"
+#define SQLITE_TABLE_PHOTOS "Photos"
+#define SQLITE_TABLE_ALBUMS "Albums"
+#define SQLITE_TABLE_PLAYLISTS "Playlists"
+#define SQLITE_TABLE_SEARCHCLASS "SearchClass"
+#define SQLITE_TABLE_PRIMARY_KEYS "PrimaryKeys"
+#define SQLITE_TABLE_SYSTEM "System"
+
+#define SQLITE_TYPE_TEXT "TEXT"
+#define SQLITE_TYPE_INTEGER "INTEGER"
+#define SQLITE_TYPE_BOOL SQLITE_TYPE_INTEGER
+#define SQLITE_TYPE_DATE SQLITE_TYPE_TEXT
+#define SQLITE_TYPE_ULONG SQLITE_TYPE_INTEGER
+#define SQLITE_TYPE_LONG SQLITE_TYPE_INTEGER
+#define SQLITE_TYPE_UINTEGER SQLITE_TYPE_INTEGER
+
+#define SQLITE_TRANSACTION_BEGIN "BEGIN IMMEDIATE TRANSACTION "
+#define SQLITE_TRANSACTION_END "COMMIT TRANSACTION"
+#define SQLITE_TRANSACTION_TYPE "ROLLBACK"
+
+#define SQLITE_CONFLICT_CLAUSE "ON CONFLICT " SQLITE_TRANSACTION_TYPE
+#define SQLITE_PRIMARY_KEY SQLITE_TYPE_INTEGER " PRIMARY KEY"
+#define SQLITE_NOT_NULL "NOT NULL"
+#define SQLITE_UNIQUE "UNIQUE"
+
+#define SQLITE_COL_OBJECTID "ObjectID"
+#define SQLITE_COL_PARENTID "ParentID"
+#define SQLITE_COL_TITLE "Title"
+#define SQLITE_COL_CREATOR "Creator"
+#define SQLITE_COL_CLASS "Class"
+#define SQLITE_COL_RESTRICTED "Restricted"
+#define SQLITE_COL_WRITESTATUS "WriteStatus"
+#define SQLITE_COL_REFERENCEID "RefID"
+#define SQLITE_COL_CLASSDERIVED "IncludeDerived"
+#define SQLITE_COL_SEARCHABLE "Searchable"
+#define SQLITE_COL_CONTAINER_UID "UpdateID"
+#define SQLITE_COL_RESOURCEID "ResourceID"
+#define SQLITE_COL_PROTOCOLINFO "ProtocolInfo"
+#define SQLITE_COL_CONTENTTYPE "ContentType"
+#define SQLITE_COL_RESOURCETYPE "ResourceType"
+#define SQLITE_COL_RESOURCE "Resource"
+#define SQLITE_COL_SIZE "Size"
+#define SQLITE_COL_DURATION "Duration"
+#define SQLITE_COL_BITRATE "Bitrate"
+#define SQLITE_COL_SAMPLEFREQUENCE "SampleFreq"
+#define SQLITE_COL_BITSPERSAMPLE "BitsPerSample"
+#define SQLITE_COL_NOAUDIOCHANNELS "NoAudioChannels"
+#define SQLITE_COL_COLORDEPTH "ColorDepth"
+#define SQLITE_COL_RESOLUTION "Resolution"
+#define SQLITE_COL_GENRE "Genre"
+#define SQLITE_COL_LONGDESCRIPTION "LongDescription"
+#define SQLITE_COL_PRODUCER "Producer"
+#define SQLITE_COL_RATING "Rating"
+#define SQLITE_COL_ACTOR "Actor"
+#define SQLITE_COL_DIRECTOR "Director"
+#define SQLITE_COL_DESCRIPTION "Description"
+#define SQLITE_COL_PUBLISHER "Publisher"
+#define SQLITE_COL_LANGUAGE "Language"
+#define SQLITE_COL_RELATION "Relation"
+#define SQLITE_COL_STORAGEMEDIUM "StorageMedium"
+#define SQLITE_COL_DVDREGIONCODE "DVDRegionCode"
+#define SQLITE_COL_CHANNELNAME "Channelname"
+#define SQLITE_COL_SCHEDULEDSTARTTIME "ScheduledStartTime"
+#define SQLITE_COL_SCHEDULEDENDTIME "ScheduledEndTime"
+#define SQLITE_COL_ICON "Icon"
+#define SQLITE_COL_REGION "Region"
+#define SQLITE_COL_CHANNELNR "ChannelNr"
+#define SQLITE_COL_RIGHTS "Rights"
+#define SQLITE_COL_RADIOCALLSIGN "CallSign"
+#define SQLITE_COL_RADIOSTATIONID "StationID"
+#define SQLITE_COL_RADIOBAND "Band"
+#define SQLITE_COL_CONTRIBUTOR "Contributor"
+#define SQLITE_COL_DATE "Date"
+#define SQLITE_COL_ALBUM "Album"
+#define SQLITE_COL_ARTIST "Artist"
+#define SQLITE_COL_DLNA_CONTAINERTYPE "DLNAContainer"
+#define SQLITE_COL_CHILDCOUNT "ChildCount"
+
+#define SQLITE_UPNP_OBJECTID SQLITE_COL_OBJECTID " " SQLITE_TYPE_INTEGER " " SQLITE_NOT_NULL " " SQLITE_CONFLICT_CLAUSE " "\
+ SQLITE_UNIQUE " " SQLITE_CONFLICT_CLAUSE
+
+#define SQLITE_INSERT_TRIGGER(TableA,TableB,Class) "CREATE TRIGGER IF NOT EXISTS "\
+ TableA "_I_" TableB " "\
+ "BEFORE INSERT ON "\
+ TableB " "\
+ "FOR EACH ROW BEGIN "\
+ "SELECT CASE "\
+ "WHEN ("\
+ "((SELECT " SQLITE_COL_OBJECTID " FROM " TableA " "\
+ "WHERE " SQLITE_COL_OBJECTID "=NEW." SQLITE_COL_OBJECTID " "\
+ ") IS NULL) "\
+ "OR "\
+ "((SELECT " SQLITE_COL_OBJECTID " FROM " SQLITE_TABLE_OBJECTS " "\
+ "WHERE " SQLITE_COL_OBJECTID "=NEW." SQLITE_COL_OBJECTID " "\
+ "AND " SQLITE_COL_CLASS " LIKE '" Class "%') IS NULL) "\
+ ") THEN "\
+ "RAISE(" SQLITE_TRANSACTION_TYPE ", "\
+ "'INSERT on table " TableB " failed due constraint violation "\
+ "on foreign key " SQLITE_COL_OBJECTID "'"\
+ ") "\
+ "END; END;"
+
+#define SQLITE_UPDATE_TRIGGER(TableA,TableB,Class) "CREATE TRIGGER IF NOT EXISTS "\
+ TableA "_U_" TableB " "\
+ "BEFORE UPDATE ON "\
+ TableB " "\
+ "FOR EACH ROW BEGIN "\
+ "SELECT CASE "\
+ "WHEN ("\
+ "((SELECT " SQLITE_COL_OBJECTID " FROM " TableA " "\
+ "WHERE " SQLITE_COL_OBJECTID "=NEW." SQLITE_COL_OBJECTID " "\
+ "AND " SQLITE_COL_CLASS " LIKE '" Class "%') IS NULL)"\
+ ") THEN "\
+ "RAISE(" SQLITE_TRANSACTION_TYPE ", "\
+ "'UPDATE on table " TableB " failed due constraint violation "\
+ "on foreign key " SQLITE_COL_OBJECTID "'"\
+ ") "\
+ "END; END;"
+
+#define SQLITE_INSERT_REFERENCE_TRIGGER(Table,Column) "CREATE TRIGGER IF NOT EXISTS "\
+ Table "_I_" Table " "\
+ "BEFORE INSERT ON " \
+ Table " " \
+ "FOR EACH ROW BEGIN "\
+ "SELECT CASE "\
+ "WHEN ( "\
+ "((SELECT " SQLITE_COL_OBJECTID " FROM " Table " "\
+ "WHERE " SQLITE_COL_OBJECTID " = NEW." Column ") IS NULL) "\
+ "AND "\
+ "(NEW." Column "!=-1)"\
+ ")THEN "\
+ "RAISE(" SQLITE_TRANSACTION_TYPE ", 'INSERT on table " Table " "\
+ "violates foreign key \"" Column "\"') "\
+ "END; END;"
+
+#define SQLITE_UPDATE_REFERENCE_TRIGGER(Table,Column) "CREATE TRIGGER IF NOT EXISTS "\
+ Table "_U_" Table " "\
+ "BEFORE INSERT ON " \
+ Table " " \
+ "FOR EACH ROW BEGIN "\
+ "SELECT CASE "\
+ "WHEN ( "\
+ "((SELECT " SQLITE_COL_OBJECTID " FROM " Table " "\
+ "WHERE " SQLITE_COL_OBJECTID " = NEW." Column ") IS NULL) "\
+ "AND "\
+ "(NEW." Column "!=-1)"\
+ ")THEN "\
+ "RAISE(" SQLITE_TRANSACTION_TYPE ", 'UPDATE on table " Table " "\
+ "violates foreign key \"" Column "\"') "\
+ "END; END;"
+
+#define SQLITE_DELETE_REFERENCE_TRIGGER(Table,Column) "CREATE TRIGGER IF NOT EXISTS "\
+ Table "_D_" Table " " \
+ "BEFORE DELETE ON " \
+ Table " " \
+ "FOR EACH ROW BEGIN "\
+ "SELECT CASE "\
+ "WHEN ("\
+ "(SELECT " Column " FROM " Table " "\
+ "WHERE " Column " = OLD." SQLITE_COL_OBJECTID ") IS NOT NULL"\
+ ")THEN "\
+ "RAISE(" SQLITE_TRANSACTION_TYPE ", 'DELETE on table " Table " "\
+ "violates foreign key \"" Column "\"') "\
+ "END; END;"
+
+#ifdef SQLITE_CASCADE_DELETES
+#define SQLITE_DELETE_TRIGGER(TableA,TableB) "CREATE TRIGGER IF NOT EXISTS "\
+ TableA "_D_" TableB " "\
+ "BEFORE DELETE ON "\
+ TableA " "\
+ "FOR EACH ROW BEGIN "\
+ "DELETE FROM " TableB " "\
+ "WHERE " SQLITE_COL_OBJECTID "=OLD." SQLITE_COL_OBJECTID "; "\
+ "END;"
+
+#define SQLITE_DELETE_PARENT_TRIGGER "CREATE TRIGGER IF NOT EXISTS "\
+ SQLITE_TABLE_OBJECTS "_D_" SQLITE_TABLE_OBJECTS " " \
+ "BEFORE DELETE ON " \
+ SQLITE_TABLE_OBJECTS " " \
+ "FOR EACH ROW BEGIN "\
+ "DELETE FROM " SQLITE_TABLE_OBJECTS " "\
+ "WHERE " SQLITE_COL_PARENTID "=OLD." SQLITE_COL_OBJECTID "; "\
+ "END;"
+#else
+#define SQLITE_DELETE_TRIGGER(TableA,TableB) "CREATE TRIGGER IF NOT EXISTS "\
+ TableA "_D_" TableB " "\
+ "BEFORE DELETE ON "\
+ TableA " "\
+ "FOR EACH ROW BEGIN "\
+ "SELECT CASE "\
+ "WHEN ("\
+ "(SELECT " SQLITE_COL_OBJECTID " FROM " TableB " "\
+ "WHERE " SQLITE_COL_OBJECTID "=OLD." SQLITE_COL_OBJECTID ") IS NOT NULL"\
+ ") THEN "\
+ "RAISE(" SQLITE_TRANSACTION_TYPE ", "\
+ "'DELETE on table " TableA " failed due constraint violation "\
+ "on foreign key " SQLITE_COL_OBJECTID "'"\
+ ") "\
+ "END; END;"
+
+#define SQLITE_DELETE_PARENT_TRIGGER SQLITE_DELETE_REFERENCE_TRIGGER(SQLITE_TABLE_OBJECTS, SQLITE_COL_PARENTID)
+#endif
+
+ /**********************************************\
+ * *
+ * Primary keys *
+ * *
+ \**********************************************/
+
+#define SQLITE_CREATE_TABLE_PRIMARY_KEYS "CREATE TABLE IF NOT EXISTS "\
+ SQLITE_TABLE_PRIMARY_KEYS \
+ "("\
+ "KeyID " SQLITE_PRIMARY_KEY " " SQLITE_NOT_NULL ","\
+ "Key " SQLITE_TYPE_INTEGER " " SQLITE_NOT_NULL\
+ ");"\
+ "INSERT OR IGNORE INTO "\
+ SQLITE_TABLE_PRIMARY_KEYS \
+ "(KeyID, Key) VALUES ("\
+ PK_OBJECTS "," SQLITE_FIRST_CUSTOMID\
+ ");"\
+ "INSERT OR IGNORE INTO "\
+ SQLITE_TABLE_PRIMARY_KEYS \
+ "(KeyID, Key) VALUES ("\
+ PK_RESOURCES ",0"\
+ ");"\
+ "INSERT OR IGNORE INTO "\
+ SQLITE_TABLE_PRIMARY_KEYS \
+ "(KeyID, Key) VALUES ("\
+ PK_SEARCHCLASSES ",0"\
+ ");"
+
+#define SQLITE_TRIGGER_UPDATE_OBJECTID "CREATE TRIGGER IF NOT EXISTS "\
+ SQLITE_TABLE_OBJECTS "_PK_UPDATE "\
+ "AFTER INSERT ON "\
+ SQLITE_TABLE_OBJECTS " "\
+ "BEGIN "\
+ "UPDATE " SQLITE_TABLE_PRIMARY_KEYS " SET Key=Key+1 WHERE KeyID=" PK_OBJECTS "; "\
+ "END;"
+
+ /**********************************************\
+ * *
+ * System settings *
+ * *
+ \**********************************************/
+
+#define SQLITE_CREATE_TABLE_SYSTEM "CREATE TABLE IF NOT EXISTS "\
+ SQLITE_TABLE_SYSTEM " "\
+ "("\
+ "Key " SQLITE_TYPE_TEXT " " SQLITE_NOT_NULL " " SQLITE_UNIQUE ","\
+ "Value " SQLITE_TYPE_TEXT " "\
+ ");"
+
+#define SQLITE_TRIGGER_UPDATE_SYSTEM "CREATE TRIGGER IF NOT EXISTS "\
+ SQLITE_TABLE_SYSTEM "_VALUE_UPDATE "\
+ "BEFORE UPDATE "\
+ "ON " SQLITE_TABLE_SYSTEM " "\
+ "WHEN ((SELECT Key FROM " SQLITE_TABLE_SYSTEM " WHERE Key=NEW.Key) IS NULL) "\
+ "BEGIN INSERT INTO " SQLITE_TABLE_SYSTEM " (Key) VALUES (NEW.Key); END;"
+
+ /**********************************************\
+ * *
+ * Objects *
+ * *
+ \**********************************************/
+
+#define SQLITE_CREATE_TABLE_OBJECTS "CREATE TABLE IF NOT EXISTS "\
+ SQLITE_TABLE_OBJECTS \
+ "(" \
+ SQLITE_COL_OBJECTID " " SQLITE_PRIMARY_KEY " " SQLITE_NOT_NULL " " SQLITE_CONFLICT_CLAUSE "," \
+ SQLITE_COL_PARENTID " " SQLITE_TYPE_INTEGER " " SQLITE_NOT_NULL " " SQLITE_CONFLICT_CLAUSE "," \
+ SQLITE_COL_TITLE " " SQLITE_TYPE_TEXT " " SQLITE_NOT_NULL "," \
+ SQLITE_COL_CREATOR " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_CLASS " " SQLITE_TYPE_TEXT " " SQLITE_NOT_NULL "," \
+ SQLITE_COL_RESTRICTED " " SQLITE_TYPE_BOOL " " SQLITE_NOT_NULL "," \
+ SQLITE_COL_WRITESTATUS " " SQLITE_TYPE_INTEGER \
+ ");"
+
+// Trigger for foreign key ParentID
+
+#define SQLITE_TRIGGER_D_OBJECTS_OBJECTS SQLITE_DELETE_PARENT_TRIGGER
+
+#define SQLITE_TRIGGER_I_OBJECTS_OBJECTS SQLITE_INSERT_REFERENCE_TRIGGER(SQLITE_TABLE_OBJECTS, SQLITE_COL_PARENTID)\
+ "CREATE TRIGGER IF NOT EXISTS "\
+ SQLITE_TABLE_OBJECTS "_PI_" SQLITE_TABLE_OBJECTS " "\
+ "BEFORE INSERT ON "\
+ SQLITE_TABLE_OBJECTS " " \
+ "FOR EACH ROW BEGIN "\
+ "SELECT CASE "\
+ "WHEN ("\
+ "((SELECT " SQLITE_COL_PARENTID " FROM " SQLITE_TABLE_OBJECTS " "\
+ "WHERE " SQLITE_COL_PARENTID "=-1) IS NOT NULL) "\
+ "AND "\
+ "(NEW." SQLITE_COL_PARENTID "=-1) "\
+ ") THEN "\
+ "RAISE(" SQLITE_TRANSACTION_TYPE ","\
+ "'INSERT on table " SQLITE_TABLE_OBJECTS " violates constraint. "\
+ SQLITE_COL_PARENTID " must uniquely be -1') "\
+ "END; END;"
+
+#define SQLITE_TRIGGER_U_OBJECTS_OBJECTS SQLITE_UPDATE_REFERENCE_TRIGGER(SQLITE_TABLE_OBJECTS, SQLITE_COL_PARENTID)\
+ "CREATE TRIGGER IF NOT EXISTS "\
+ SQLITE_TABLE_OBJECTS "_PU_" SQLITE_TABLE_OBJECTS " "\
+ "BEFORE UPDATE ON "\
+ SQLITE_TABLE_OBJECTS " " \
+ "FOR EACH ROW BEGIN "\
+ "SELECT CASE "\
+ "WHEN ("\
+ "((SELECT " SQLITE_COL_PARENTID " FROM " SQLITE_TABLE_OBJECTS " "\
+ "WHERE " SQLITE_COL_PARENTID "=-1 "\
+ "AND " SQLITE_COL_OBJECTID "!=NEW." SQLITE_COL_OBJECTID " ) IS NOT NULL) "\
+ "AND "\
+ "(NEW." SQLITE_COL_PARENTID "=-1) "\
+ ") THEN "\
+ "RAISE(" SQLITE_TRANSACTION_TYPE ","\
+ "'UPDATE on table " SQLITE_TABLE_OBJECTS " violates constraint. "\
+ SQLITE_COL_PARENTID " must uniquely be -1') "\
+ "END; END;"
+
+ /**********************************************\
+ * *
+ * Items *
+ * *
+ \**********************************************/
+
+#define SQLITE_CREATE_TABLE_ITEMS "CREATE TABLE IF NOT EXISTS "\
+ SQLITE_TABLE_ITEMS \
+ "(" \
+ SQLITE_UPNP_OBJECTID "," \
+ SQLITE_COL_REFERENCEID " " SQLITE_TYPE_INTEGER " DEFAULT -1" \
+ ");"
+
+// Trigger for foreign key ObjectID
+
+#define SQLITE_TRIGGER_D_OBJECT_ITEMS SQLITE_DELETE_TRIGGER(SQLITE_TABLE_OBJECTS,\
+ SQLITE_TABLE_ITEMS)
+
+#define SQLITE_TRIGGER_I_OBJECT_ITEMS SQLITE_INSERT_TRIGGER(SQLITE_TABLE_OBJECTS,\
+ SQLITE_TABLE_ITEMS,\
+ UPNP_CLASS_ITEM)
+
+#define SQLITE_TRIGGER_U_OBJECT_ITEMS SQLITE_UPDATE_TRIGGER(SQLITE_TABLE_OBJECTS,\
+ SQLITE_TABLE_ITEMS,\
+ UPNP_CLASS_ITEM)
+
+// Trigger for Reference items
+
+#define SQLITE_TRIGGER_I_ITEMS_ITEMS SQLITE_INSERT_REFERENCE_TRIGGER(SQLITE_TABLE_ITEMS, SQLITE_COL_REFERENCEID)
+
+#define SQLITE_TRIGGER_U_ITEMS_ITEMS SQLITE_UPDATE_REFERENCE_TRIGGER(SQLITE_TABLE_ITEMS, SQLITE_COL_REFERENCEID)
+
+#define SQLITE_TRIGGER_D_ITEMS_ITEMS SQLITE_DELETE_REFERENCE_TRIGGER(SQLITE_TABLE_ITEMS, SQLITE_COL_REFERENCEID)
+
+ /**********************************************\
+ * *
+ * Containers *
+ * *
+ \**********************************************/
+
+#define SQLITE_CREATE_TABLE_CONTAINER "CREATE TABLE IF NOT EXISTS "\
+ SQLITE_TABLE_CONTAINERS \
+ "(" \
+ SQLITE_UPNP_OBJECTID "," \
+ SQLITE_COL_SEARCHABLE " " SQLITE_TYPE_INTEGER ","\
+ SQLITE_COL_CONTAINER_UID " " SQLITE_TYPE_INTEGER " " SQLITE_NOT_NULL ","\
+ SQLITE_COL_DLNA_CONTAINERTYPE " " SQLITE_TYPE_TEXT \
+ ");"
+
+#define SQLITE_TRIGGER_D_OBJECT_CONTAINERS SQLITE_DELETE_TRIGGER(SQLITE_TABLE_OBJECTS,\
+ SQLITE_TABLE_CONTAINERS)
+
+#define SQLITE_TRIGGER_I_OBJECT_CONTAINERS SQLITE_INSERT_TRIGGER(SQLITE_TABLE_OBJECTS,\
+ SQLITE_TABLE_CONTAINERS,\
+ UPNP_CLASS_CONTAINER)
+
+#define SQLITE_TRIGGER_U_OBJECT_CONTAINERS SQLITE_UPDATE_TRIGGER(SQLITE_TABLE_OBJECTS,\
+ SQLITE_TABLE_CONTAINERS,\
+ UPNP_CLASS_CONTAINER)
+
+ /**********************************************\
+ * *
+ * Video items *
+ * *
+ \**********************************************/
+
+#define SQLITE_CREATE_TABLE_VIDEOITEMS "CREATE TABLE IF NOT EXISTS "\
+ SQLITE_TABLE_VIDEOITEMS \
+ "(" \
+ SQLITE_UPNP_OBJECTID "," \
+ SQLITE_COL_GENRE " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_LONGDESCRIPTION " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_PRODUCER " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_RATING " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_ACTOR " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_DIRECTOR " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_DESCRIPTION " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_PUBLISHER " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_LANGUAGE " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_RELATION " " SQLITE_TYPE_TEXT \
+ ");"
+
+#define SQLITE_TRIGGER_D_ITEMS_VIDEOITEMS SQLITE_DELETE_TRIGGER(SQLITE_TABLE_ITEMS, SQLITE_TABLE_VIDEOITEMS)
+
+#define SQLITE_TRIGGER_U_ITEMS_VIDEOITEMS SQLITE_UPDATE_TRIGGER(SQLITE_TABLE_ITEMS, \
+ SQLITE_TABLE_VIDEOITEMS, \
+ UPNP_CLASS_VIDEO)
+
+#define SQLITE_TRIGGER_I_ITEMS_VIDEOITEMS SQLITE_INSERT_TRIGGER(SQLITE_TABLE_ITEMS, \
+ SQLITE_TABLE_VIDEOITEMS, \
+ UPNP_CLASS_VIDEO)
+
+ /**********************************************\
+ * *
+ * Audio items *
+ * *
+ \**********************************************/
+
+#define SQLITE_CREATE_TABLE_AUDIOITEMS "CREATE TABLE IF NOT EXISTS "\
+ SQLITE_TABLE_AUDIOITEMS \
+ "(" \
+ SQLITE_UPNP_OBJECTID "," \
+ SQLITE_COL_GENRE " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_LONGDESCRIPTION " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_DESCRIPTION " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_PUBLISHER " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_RELATION " " SQLITE_TYPE_TEXT \
+ ");"
+
+#define SQLITE_TRIGGER_D_ITEMS_AUDIOITEMS SQLITE_DELETE_TRIGGER(SQLITE_TABLE_ITEMS, SQLITE_TABLE_AUDIOITEMS)
+
+#define SQLITE_TRIGGER_U_ITEMS_AUDIOITEMS SQLITE_UPDATE_TRIGGER(SQLITE_TABLE_ITEMS, \
+ SQLITE_TABLE_AUDIOITEMS, \
+ UPNP_CLASS_AUDIO)
+
+#define SQLITE_TRIGGER_I_ITEMS_AUDIOITEMS SQLITE_INSERT_TRIGGER(SQLITE_TABLE_ITEMS, \
+ SQLITE_TABLE_AUDIOITEMS, \
+ UPNP_CLASS_AUDIO)
+
+ /**********************************************\
+ * *
+ * Image items *
+ * *
+ \**********************************************/
+
+#define SQLITE_CREATE_TABLE_IMAGEITEMS "CREATE TABLE IF NOT EXISTS "\
+ SQLITE_TABLE_IMAGEITEMS \
+ "("\
+ SQLITE_UPNP_OBJECTID "," \
+ SQLITE_COL_LONGDESCRIPTION " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_DESCRIPTION " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_PUBLISHER " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_STORAGEMEDIUM " " SQLITE_TYPE_TEXT ","\
+ SQLITE_COL_RATING " " SQLITE_TYPE_TEXT ","\
+ SQLITE_COL_DATE " " SQLITE_TYPE_TEXT ","\
+ SQLITE_COL_RIGHTS " " SQLITE_TYPE_TEXT\
+ ");"
+
+#define SQLITE_TRIGGER_D_ITEMS_IMAGEITEMS SQLITE_DELETE_TRIGGER(SQLITE_TABLE_ITEMS, SQLITE_TABLE_IMAGEITEMS)
+
+#define SQLITE_TRIGGER_U_ITEMS_IMAGEITEMS SQLITE_UPDATE_TRIGGER(SQLITE_TABLE_ITEMS, \
+ SQLITE_TABLE_IMAGEITEMS, \
+ UPNP_CLASS_IMAGE)
+
+#define SQLITE_TRIGGER_I_ITEMS_IMAGEITEMS SQLITE_INSERT_TRIGGER(SQLITE_TABLE_ITEMS, \
+ SQLITE_TABLE_IMAGEITEMS, \
+ UPNP_CLASS_IMAGE)
+
+ /**********************************************\
+ * *
+ * Video broadcasts *
+ * *
+ \**********************************************/
+
+#define SQLITE_CREATE_TABLE_VIDEOBROADCASTS "CREATE TABLE IF NOT EXISTS "\
+ SQLITE_TABLE_VIDEOBROADCASTS \
+ "("\
+ SQLITE_UPNP_OBJECTID "," \
+ SQLITE_COL_ICON " " SQLITE_TYPE_TEXT ","\
+ SQLITE_COL_REGION " " SQLITE_TYPE_TEXT ","\
+ SQLITE_COL_CHANNELNR " " SQLITE_TYPE_TEXT ","\
+ SQLITE_COL_CHANNELNAME " " SQLITE_TYPE_TEXT " " SQLITE_UNIQUE \
+ ");"
+
+#define SQLITE_TRIGGER_D_VIDEOITEMS_VIDEOBROADCASTS SQLITE_DELETE_TRIGGER(SQLITE_TABLE_VIDEOITEMS, SQLITE_TABLE_VIDEOBROADCASTS)
+
+#define SQLITE_TRIGGER_U_VIDEOITEMS_VIDEOBROADCASTS SQLITE_UPDATE_TRIGGER(SQLITE_TABLE_VIDEOITEMS,\
+ SQLITE_TABLE_VIDEOBROADCASTS,\
+ UPNP_CLASS_VIDEOBC)
+
+#define SQLITE_TRIGGER_I_VIDEOITEMS_VIDEOBROADCASTS SQLITE_INSERT_TRIGGER(SQLITE_TABLE_VIDEOITEMS,\
+ SQLITE_TABLE_VIDEOBROADCASTS,\
+ UPNP_CLASS_VIDEOBC)
+
+ /**********************************************\
+ * *
+ * Audio broadcasts *
+ * *
+ \**********************************************/
+
+#define SQLITE_CREATE_TABLE_AUDIOBROADCASTS "CREATE TABLE IF NOT EXISTS "\
+ SQLITE_TABLE_AUDIOBROADCASTS \
+ "("\
+ SQLITE_UPNP_OBJECTID "," \
+ SQLITE_COL_REGION " " SQLITE_TYPE_TEXT ","\
+ SQLITE_COL_RADIOCALLSIGN " " SQLITE_TYPE_TEXT ","\
+ SQLITE_COL_RADIOSTATIONID " " SQLITE_TYPE_TEXT ","\
+ SQLITE_COL_RADIOBAND " " SQLITE_TYPE_TEXT ","\
+ SQLITE_COL_CHANNELNR " " SQLITE_TYPE_INTEGER \
+ ");"
+
+#define SQLITE_TRIGGER_D_AUDIOITEMS_AUDIOBROADCASTS SQLITE_DELETE_TRIGGER(SQLITE_TABLE_AUDIOITEMS, SQLITE_TABLE_AUDIOBROADCASTS)
+
+#define SQLITE_TRIGGER_I_AUDIOITEMS_AUDIOBROADCASTS SQLITE_INSERT_TRIGGER(SQLITE_TABLE_AUDIOITEMS,\
+ SQLITE_TABLE_AUDIOBROADCASTS,\
+ UPNP_CLASS_AUDIOBC)
+
+#define SQLITE_TRIGGER_U_AUDIOITEMS_AUDIOBROADCASTS SQLITE_UPDATE_TRIGGER(SQLITE_TABLE_AUDIOITEMS,\
+ SQLITE_TABLE_AUDIOBROADCASTS,\
+ UPNP_CLASS_AUDIOBC)
+
+ /**********************************************\
+ * *
+ * Movies *
+ * *
+ \**********************************************/
+
+#define SQLITE_CREATE_TABLE_MOVIES "CREATE TABLE IF NOT EXISTS "\
+ SQLITE_TABLE_MOVIES \
+ "("\
+ SQLITE_UPNP_OBJECTID "," \
+ SQLITE_COL_STORAGEMEDIUM " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_DVDREGIONCODE " " SQLITE_TYPE_INTEGER "," \
+ SQLITE_COL_CHANNELNAME " " SQLITE_TYPE_TEXT ","\
+ SQLITE_COL_SCHEDULEDSTARTTIME " " SQLITE_TYPE_TEXT ","\
+ SQLITE_COL_SCHEDULEDENDTIME " " SQLITE_TYPE_TEXT\
+ ");"
+
+#define SQLITE_TRIGGER_D_VIDEOITEMS_MOVIES SQLITE_DELETE_TRIGGER(SQLITE_TABLE_VIDEOITEMS, SQLITE_TABLE_MOVIES)
+
+
+#define SQLITE_TRIGGER_I_VIDEOITEMS_MOVIES SQLITE_INSERT_TRIGGER(SQLITE_TABLE_VIDEOITEMS,\
+ SQLITE_TABLE_MOVIES,\
+ UPNP_CLASS_MOVIE)
+
+#define SQLITE_TRIGGER_U_VIDEOITEMS_MOVIES SQLITE_UPDATE_TRIGGER(SQLITE_TABLE_VIDEOITEMS,\
+ SQLITE_TABLE_MOVIES,\
+ UPNP_CLASS_MOVIE)
+
+ /**********************************************\
+ * *
+ * Photos *
+ * *
+ \**********************************************/
+
+#define SQLITE_CREATE_TABLE_PHOTOS "CREATE TABLE IF NOT EXISTS "\
+ SQLITE_TABLE_PHOTOS \
+ "("\
+ SQLITE_UPNP_OBJECTID "," \
+ SQLITE_COL_ALBUM " " SQLITE_TYPE_TEXT\
+ ");"
+
+#define SQLITE_TRIGGER_D_IMAGEITEMS_PHOTOS SQLITE_DELETE_TRIGGER(SQLITE_TABLE_IMAGEITEMS, SQLITE_TABLE_PHOTOS)
+
+#define SQLITE_TRIGGER_I_IMAGEITEMS_PHOTOS SQLITE_INSERT_TRIGGER(SQLITE_TABLE_IMAGEITEMS,\
+ SQLITE_TABLE_PHOTOS,\
+ UPNP_CLASS_PHOTO)
+
+#define SQLITE_TRIGGER_U_IMAGEITEMS_PHOTOS SQLITE_UPDATE_TRIGGER(SQLITE_TABLE_IMAGEITEMS,\
+ SQLITE_TABLE_PHOTOS,\
+ UPNP_CLASS_PHOTO)
+
+ /**********************************************\
+ * *
+ * Albums *
+ * *
+ \**********************************************/
+
+#define SQLITE_CREATE_TABLE_ALBUMS "CREATE TABLE IF NOT EXISTS "\
+ SQLITE_TABLE_ALBUMS \
+ "("\
+ SQLITE_UPNP_OBJECTID "," \
+ SQLITE_COL_STORAGEMEDIUM " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_LONGDESCRIPTION " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_DESCRIPTION " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_PUBLISHER " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_CONTRIBUTOR " " SQLITE_TYPE_TEXT ","\
+ SQLITE_COL_DATE " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_RELATION " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_RIGHTS " " SQLITE_TYPE_TEXT \
+ ");"
+
+#define SQLITE_TRIGGER_D_CONTAINERS_ALBUMS SQLITE_DELETE_TRIGGER(SQLITE_TABLE_CONTAINERS, SQLITE_TABLE_ALBUMS)
+
+#define SQLITE_TRIGGER_U_CONTAINERS_ALBUMS SQLITE_UPDATE_TRIGGER(SQLITE_TABLE_CONTAINERS,\
+ SQLITE_TABLE_ALBUMS,\
+ UPNP_CLASS_ALBUM)
+
+#define SQLITE_TRIGGER_I_CONTAINERS_ALBUMS SQLITE_INSERT_TRIGGER(SQLITE_TABLE_CONTAINERS,\
+ SQLITE_TABLE_ALBUMS,\
+ UPNP_CLASS_ALBUM)
+
+ /**********************************************\
+ * *
+ * Playlists *
+ * *
+ \**********************************************/
+
+#define SQLITE_CREATE_TABLE_PLAYLISTS "CREATE TABLE IF NOT EXISTS "\
+ SQLITE_TABLE_PLAYLISTS \
+ "(" \
+ SQLITE_UPNP_OBJECTID "," \
+ SQLITE_COL_ARTIST " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_GENRE " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_LONGDESCRIPTION " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_DESCRIPTION " " SQLITE_TYPE_TEXT ","\
+ SQLITE_COL_PRODUCER " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_STORAGEMEDIUM " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_CONTRIBUTOR " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_DATE " " SQLITE_TYPE_TEXT ","\
+ SQLITE_COL_LANGUAGE " " SQLITE_TYPE_TEXT ","\
+ SQLITE_COL_RIGHTS " " SQLITE_TYPE_TEXT\
+ ");"
+
+#define SQLITE_TRIGGER_D_CONTAINERS_PLAYLISTS SQLITE_DELETE_TRIGGER(SQLITE_TABLE_CONTAINERS, SQLITE_TABLE_PLAYLISTS)
+
+#define SQLITE_TRIGGER_I_CONTAINERS_PLAYLISTS SQLITE_INSERT_TRIGGER(SQLITE_TABLE_CONTAINERS,\
+ SQLITE_TABLE_PLAYLISTS,\
+ UPNP_CLASS_PLAYLISTCONT)
+
+#define SQLITE_TRIGGER_U_CONTAINERS_PLAYLISTS SQLITE_UPDATE_TRIGGER(SQLITE_TABLE_CONTAINERS,\
+ SQLITE_TABLE_PLAYLISTS,\
+ UPNP_CLASS_PLAYLISTCONT)
+
+ /**********************************************\
+ * *
+ * Search classes *
+ * *
+ \**********************************************/
+
+#define SQLITE_CREATE_TABLE_SEARCHCLASS "CREATE TABLE IF NOT EXISTS "\
+ SQLITE_TABLE_SEARCHCLASS \
+ "(" \
+ SQLITE_COL_OBJECTID " " SQLITE_TYPE_INTEGER " " SQLITE_NOT_NULL "," \
+ SQLITE_COL_CLASS " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_CLASSDERIVED " " SQLITE_TYPE_BOOL \
+ ");"
+
+#define SQLITE_TRIGGER_D_CONTAINERS_SEARCHCLASSES "CREATE TRIGGER IF NOT EXISTS " \
+ SQLITE_TABLE_CONTAINERS "_D_" SQLITE_TABLE_SEARCHCLASS " " \
+ "BEFORE DELETE ON " \
+ SQLITE_TABLE_CONTAINERS " " \
+ "FOR EACH ROW BEGIN "\
+ "DELETE FROM " SQLITE_TABLE_SEARCHCLASS " "\
+ "WHERE " SQLITE_COL_OBJECTID "= OLD." SQLITE_COL_OBJECTID "; " \
+ "END;"
+
+#define SQLITE_TRIGGER_U_CONTAINERS_SEARCHCLASSES "CREATE TRIGGER IF NOT EXISTS " \
+ SQLITE_TABLE_CONTAINERS "_U_" SQLITE_TABLE_SEARCHCLASS " " \
+ "BEFORE UPDATE ON " \
+ SQLITE_TABLE_SEARCHCLASS " " \
+ "FOR EACH ROW BEGIN "\
+ "SELECT CASE "\
+ "WHEN ("\
+ "(SELECT " SQLITE_COL_OBJECTID " FROM " SQLITE_TABLE_CONTAINERS " "\
+ "WHERE " SQLITE_COL_OBJECTID "=NEW." SQLITE_COL_OBJECTID ") IS NULL "\
+ ") THEN "\
+ "RAISE (" SQLITE_TRANSACTION_TYPE ", 'UPDATE on table " SQLITE_TABLE_SEARCHCLASS " "\
+ "violates foreign key constraint \"" SQLITE_COL_OBJECTID "\"') " \
+ "END; END;"
+
+#define SQLITE_TRIGGER_I_CONTAINERS_SEARCHCLASSES "CREATE TRIGGER IF NOT EXISTS " \
+ SQLITE_TABLE_CONTAINERS "_I_" SQLITE_TABLE_SEARCHCLASS " " \
+ "BEFORE INSERT ON " \
+ SQLITE_TABLE_SEARCHCLASS " " \
+ "FOR EACH ROW BEGIN "\
+ "SELECT CASE "\
+ "WHEN ("\
+ "(SELECT " SQLITE_COL_OBJECTID " FROM " SQLITE_TABLE_CONTAINERS " "\
+ "WHERE " SQLITE_COL_OBJECTID "=NEW." SQLITE_COL_OBJECTID ") IS NULL "\
+ ") THEN "\
+ "RAISE (" SQLITE_TRANSACTION_TYPE ", 'INSERT on table " SQLITE_TABLE_SEARCHCLASS " "\
+ "violates foreign key constraint \"" SQLITE_COL_OBJECTID "\"') " \
+ "END; END;"
+
+ /**********************************************\
+ * *
+ * Resources *
+ * *
+ \**********************************************/
+
+#define SQLITE_CREATE_TABLE_RESOURCES "CREATE TABLE IF NOT EXISTS "\
+ SQLITE_TABLE_RESOURCES \
+ "(" \
+ SQLITE_COL_RESOURCEID " " SQLITE_PRIMARY_KEY " " SQLITE_NOT_NULL "," \
+ SQLITE_COL_OBJECTID " " SQLITE_TYPE_INTEGER " " SQLITE_NOT_NULL "," \
+ SQLITE_COL_PROTOCOLINFO " " SQLITE_TYPE_TEXT " " SQLITE_NOT_NULL "," \
+ SQLITE_COL_CONTENTTYPE " " SQLITE_TYPE_TEXT " " SQLITE_NOT_NULL "," \
+ SQLITE_COL_RESOURCETYPE " " SQLITE_TYPE_INTEGER " " SQLITE_NOT_NULL "," \
+ SQLITE_COL_RESOURCE " " SQLITE_TYPE_TEXT " " SQLITE_NOT_NULL "," \
+ SQLITE_COL_SIZE " " SQLITE_TYPE_ULONG "," \
+ SQLITE_COL_DURATION " " SQLITE_TYPE_TEXT "," \
+ SQLITE_COL_BITRATE " " SQLITE_TYPE_UINTEGER "," \
+ SQLITE_COL_SAMPLEFREQUENCE " " SQLITE_TYPE_UINTEGER "," \
+ SQLITE_COL_BITSPERSAMPLE " " SQLITE_TYPE_UINTEGER "," \
+ SQLITE_COL_NOAUDIOCHANNELS " " SQLITE_TYPE_UINTEGER "," \
+ SQLITE_COL_COLORDEPTH " " SQLITE_TYPE_UINTEGER "," \
+ SQLITE_COL_RESOLUTION " " SQLITE_TYPE_TEXT \
+ ");"
+
+#define SQLITE_TRIGGER_D_OBJECT_RESOURCES "CREATE TRIGGER IF NOT EXISTS " \
+ SQLITE_TABLE_OBJECTS "_D_" SQLITE_TABLE_RESOURCES " " \
+ "BEFORE DELETE ON " \
+ SQLITE_TABLE_OBJECTS " " \
+ "FOR EACH ROW BEGIN "\
+ "DELETE FROM " SQLITE_TABLE_RESOURCES " "\
+ "WHERE " SQLITE_COL_OBJECTID "= OLD." SQLITE_COL_OBJECTID "; " \
+ "END;"
+
+#define SQLITE_TRIGGER_I_OBJECT_RESOURCES "CREATE TRIGGER IF NOT EXISTS " \
+ SQLITE_TABLE_OBJECTS "_I_" SQLITE_TABLE_RESOURCES " " \
+ "BEFORE INSERT ON " \
+ SQLITE_TABLE_RESOURCES " " \
+ "FOR EACH ROW BEGIN "\
+ "SELECT CASE "\
+ "WHEN ("\
+ "(SELECT " SQLITE_COL_OBJECTID " FROM " SQLITE_TABLE_OBJECTS " "\
+ "WHERE " SQLITE_COL_OBJECTID "=NEW." SQLITE_COL_OBJECTID ") IS NULL"\
+ ") THEN "\
+ "RAISE (" SQLITE_TRANSACTION_TYPE ", 'INSERT on table " SQLITE_TABLE_RESOURCES " "\
+ "violates foreign key constraint \"" SQLITE_COL_OBJECTID "\"') " \
+ "END; END;"
+
+#define SQLITE_TRIGGER_U_OBJECT_RESOURCES "CREATE TRIGGER IF NOT EXISTS " \
+ SQLITE_TABLE_OBJECTS "_U_" SQLITE_TABLE_RESOURCES " " \
+ "BEFORE UPDATE ON " \
+ SQLITE_TABLE_RESOURCES " " \
+ "FOR EACH ROW BEGIN "\
+ "SELECT CASE "\
+ "WHEN ("\
+ "(SELECT " SQLITE_COL_OBJECTID " FROM " SQLITE_TABLE_OBJECTS " "\
+ "WHERE " SQLITE_COL_OBJECTID "=NEW." SQLITE_COL_OBJECTID ") IS NULL"\
+ ") THEN "\
+ "RAISE (" SQLITE_TRANSACTION_TYPE ", 'INSERT on table " SQLITE_TABLE_RESOURCES " "\
+ "violates foreign key constraint \"" SQLITE_COL_OBJECTID "\"') " \
+ "END; END;"
+
+class cSQLiteDatabase;
+
+class cRow : public cListObject {
+ friend class cSQLiteDatabase;
+private:
+ int currentCol;
+ int ColCount;
+ char** Columns;
+ char** Values;
+ cRow();
+public:
+ virtual ~cRow();
+ int Count(){ return this->ColCount; }
+ bool fetchColumn(cString* Column, cString* Value);
+ bool fetchColumn(char** Column, char** Value);
+};
+
+class cRows : public cList<cRow> {
+ friend class cSQLiteDatabase;
+private:
+ cRow* mLastRow;
+ cRows();
+public:
+ virtual ~cRows();
+ bool fetchRow(cRow** Row);
+};
+
+class cSQLiteDatabase {
+private:
+ bool mAutoCommit;
+ bool mActiveTransaction;
+ cRow* mLastRow;
+ cRows* mRows;
+ sqlite3* mDatabase;
+ static cSQLiteDatabase* mInstance;
+ cSQLiteDatabase();
+ int initialize();
+ int initializeTables();
+ int initializeTriggers();
+ static int getResultRow(void* DB, int NumCols, char** Values, char** ColNames);
+public:
+ virtual ~cSQLiteDatabase();
+ static cSQLiteDatabase* getInstance();
+ int getResultCount() const { return this->mRows->Count(); }
+ long getLastInsertRowID() const;
+ cRows* getResultRows() const { return this->mRows; }
+ int execStatement(const char* Statement);
+ void startTransaction();
+ void commitTransaction();
+ void rollbackTransaction();
+ void setAutoCommit(bool Commit=true){ this->mAutoCommit = Commit; }
+};
+
+#endif /* _DATABASE_H */ \ No newline at end of file
diff --git a/database/metadata.cpp b/database/metadata.cpp
new file mode 100644
index 0000000..ac19118
--- /dev/null
+++ b/database/metadata.cpp
@@ -0,0 +1,389 @@
+/*
+ * File: metadata.cpp
+ * Author: savop
+ *
+ * Created on 28. Mai 2009, 16:50
+ */
+
+#include <upnp/ixml.h>
+#include <time.h>
+#include <vdr/tools.h>
+#include "object.h"
+#include "resources.h"
+#include "metadata.h"
+#include "../common.h"
+#include "../misc/search.h"
+#include <vdr/channels.h>
+#include <vdr/epg.h>
+#include <upnp/upnp.h>
+
+#define KEY_SYSTEM_UPDATE_ID "SystemUpdateID"
+
+ /**********************************************\
+ * *
+ * Media database *
+ * *
+ \**********************************************/
+
+cMediaDatabase::cMediaDatabase(){
+ this->mSystemUpdateID = 0;
+ this->mLastInsertObjectID = 0;
+ this->mDatabase = cSQLiteDatabase::getInstance();
+ this->mObjects = new cHash<cUPnPClassObject>;
+ this->mFactory = cUPnPObjectFactory::getInstance();
+ this->mFactory->registerMediator(UPNP_CLASS_ITEM, new cUPnPItemMediator(this));
+ this->mFactory->registerMediator(UPNP_CLASS_CONTAINER, new cUPnPContainerMediator(this));
+ this->mFactory->registerMediator(UPNP_CLASS_VIDEO, new cUPnPVideoItemMediator(this));
+ this->mFactory->registerMediator(UPNP_CLASS_VIDEOBC, new cUPnPVideoBroadcastMediator(this));
+}
+
+cMediaDatabase::~cMediaDatabase(){
+ delete this->mDatabase;
+}
+
+bool cMediaDatabase::init(){
+ MESSAGE("Initializing...");
+ if(this->prepareDatabase()){
+ ERROR("Initializing of database failed.");
+ return false;
+ }
+ if(this->loadChannels()){
+ ERROR("Loading channels failed");
+ return false;
+ }
+ return true;
+}
+
+void cMediaDatabase::updateSystemID(){
+ cString Statement = cString::sprintf("INSERT OR REPLACE INTO %s (Key,Value) VALUES ('%s','%d')",
+ SQLITE_TABLE_SYSTEM,
+ KEY_SYSTEM_UPDATE_ID,
+ this->getSystemUpdateID()+1
+ );
+ this->mDatabase->execStatement(Statement);
+}
+
+const char* cMediaDatabase::getContainerUpdateIDs(){
+ return "";
+}
+
+unsigned int cMediaDatabase::getSystemUpdateID(){
+ cString Statement = cString::sprintf("SELECT Value FROM %s WHERE Key='%s'",
+ SQLITE_TABLE_SYSTEM,
+ KEY_SYSTEM_UPDATE_ID
+ );
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return 0;
+ }
+ cRows* Rows = this->mDatabase->getResultRows();
+ cRow* Row;
+ cString Column, Value;
+ if(!Rows->fetchRow(&Row)){
+ ERROR("No rows found");
+ return 0;
+ }
+ while(Row->fetchColumn(&Column, &Value)){
+ if(!strcasecmp(Column, "Value")){
+ this->mSystemUpdateID = (unsigned int)atoi(Value);
+ }
+ }
+ return this->mSystemUpdateID;
+}
+
+cUPnPObjectID cMediaDatabase::getNextObjectID(){
+ cString Statement, Column, Value;
+ const char* Format = "SELECT Key FROM %s WHERE KeyID=%s";
+ Statement = cString::sprintf(Format, SQLITE_TABLE_PRIMARY_KEYS, PK_OBJECTS);
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return 0;
+ }
+ cRows* Rows = this->mDatabase->getResultRows();
+ cRow* Row;
+ if(!Rows->fetchRow(&Row)){
+ ERROR("No rows found");
+ return 0;
+ }
+ while(Row->fetchColumn(&Column, &Value)){
+ if(!strcasecmp(Column, "Key")){
+ this->mLastInsertObjectID = atoi(Value);
+ return this->mLastInsertObjectID;
+ }
+ }
+ delete Rows;
+ return 0;
+}
+
+cUPnPClassObject* cMediaDatabase::getObjectByID(cUPnPObjectID ID){
+ MESSAGE("Try to find Object with ID '%s'", *ID);
+ cUPnPClassObject* Object;
+ if((Object = this->mObjects->Get((unsigned int)ID))){
+ MESSAGE("Found cached object with ID '%s'", *ID);
+ }
+ else if((Object = this->mFactory->getObject(ID))){
+ //this->cacheObject(Object);
+ MESSAGE("Found object with ID '%s' in database", *ID);
+ }
+ else {
+ ERROR("No object with such ID '%s'", *ID);
+ return NULL;
+ }
+ return Object;
+}
+
+void cMediaDatabase::cacheObject(cUPnPClassObject* Object){
+ if(this->mObjects->Get((unsigned int)Object->getID())==NULL){
+ MESSAGE("Added %s to cache.", *Object->getID());
+ this->mObjects->Add(Object, (unsigned int)Object->getID());
+ }
+}
+
+int cMediaDatabase::prepareDatabase(){
+ if(this->getObjectByID(0)==NULL){
+ MESSAGE("Creating database structure");
+ cUPnPClassContainer* Root = (cUPnPClassContainer*)this->mFactory->createObject(UPNP_CLASS_CONTAINER, _(PLUGIN_SHORT_NAME));
+ Root->setID(0);
+ if(this->mFactory->saveObject(Root)) return -1;
+
+ cClass VideoClass = { UPNP_CLASS_VIDEO, true };
+ cClass AudioClass = { UPNP_CLASS_AUDIO, true };
+ cClass VideoBCClass = { UPNP_CLASS_VIDEOBC, true };
+ cClass AudioBCClass = { UPNP_CLASS_AUDIOBC, true };
+
+ cUPnPClassContainer* Video = (cUPnPClassContainer*)this->mFactory->createObject(UPNP_CLASS_CONTAINER, _("Video"));
+ Video->setID(1);
+ Root->addObject(Video);
+ Video->addSearchClass(VideoClass);
+ Video->setSearchable(true);
+ if(this->mFactory->saveObject(Video)) return -1;
+
+ cUPnPClassContainer* Audio = (cUPnPClassContainer*)this->mFactory->createObject(UPNP_CLASS_CONTAINER, _("Audio"));
+ Audio->setID(2);
+ Root->addObject(Audio);
+ Audio->addSearchClass(AudioClass);
+ Audio->setSearchable(true);
+ if(this->mFactory->saveObject(Audio)) return -1;
+
+ cUPnPClassContainer* TV = (cUPnPClassContainer*)this->mFactory->createObject(UPNP_CLASS_CONTAINER, _("TV"));
+ TV->setID(3);
+ TV->setContainerType(DLNA_CONTAINER_TUNER);
+ TV->setSearchable(true);
+ TV->addSearchClass(VideoBCClass);
+ Video->addObject(TV);
+ if(this->mFactory->saveObject(TV)) return -1;
+
+ cUPnPClassContainer* Records = (cUPnPClassContainer*)this->mFactory->createObject(UPNP_CLASS_CONTAINER, _("Records"));
+ Records->setID(4);
+ Video->addObject(Records);
+ Records->addSearchClass(VideoClass);
+ Records->setSearchable(true);
+ if(this->mFactory->saveObject(Records)) return -1;
+
+ cUPnPClassContainer* Radio = (cUPnPClassContainer*)this->mFactory->createObject(UPNP_CLASS_CONTAINER, _("Radio"));
+ Radio->setID(5);
+ Audio->addObject(Radio);
+ Radio->addSearchClass(AudioBCClass);
+ Radio->setSearchable(true);
+ if(this->mFactory->saveObject(Radio)) return -1;
+ }
+ return 0;
+}
+
+int cMediaDatabase::loadChannels(){
+ MESSAGE("Loading channels from Database");
+ cUPnPClassContainer* TV = (cUPnPClassContainer*)this->getObjectByID(3);
+ if(TV){
+ // Iterating channels
+ cList<cUPnPClassObject>* List = TV->getObjectList();
+ for(cChannel* Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)){
+ bool inList = false;
+ for(cUPnPClassVideoBroadcast* Child = (cUPnPClassVideoBroadcast*)List->First();
+ Child;
+ Child = (cUPnPClassVideoBroadcast*)List->Next(Child)){
+ if(!strcasecmp(Child->getChannelName(),Channel->Name())){ inList = true; break; }
+ }
+ if(!inList){
+ if(!Channel->GroupSep()){
+ tChannelID ChannelID = Channel->GetChannelID();
+ MESSAGE("Adding channel '%s' ID:%s", Channel->Name(), *ChannelID.ToString());
+ cUPnPClassVideoBroadcast* ChannelItem;
+ ChannelItem = (cUPnPClassVideoBroadcast*)this->mFactory->createObject(UPNP_CLASS_VIDEOBC, Channel->Name());
+ ChannelItem->setChannelName(Channel->Name());
+ ChannelItem->setChannelNr(Channel->Number());
+ // Set primary language of the stream
+ if(Channel->Alang(0)){
+ ChannelItem->setLanguage(Channel->Alang(0));
+ }
+ cUPnPResources::getInstance()->createFromChannel(ChannelItem, Channel);
+ TV->addObject(ChannelItem);
+ if(this->mFactory->saveObject(ChannelItem)) return -1;
+ }
+ else {
+ MESSAGE("Skipping group '%s'", Channel->Name());
+ // Skip channel groups
+ // Channel groups may be supported theoretically. However, DLNA states that a tuner needs
+ // a consecutive list of channels. A simple work-around may be a virtual tuner for each group.
+ }
+ }
+ else {
+ MESSAGE("Skipping %s, already in database", Channel->Name());
+ }
+ }
+ }
+ return 0;
+}
+
+void cMediaDatabase::Action(){
+ time_t LastEPGUpdate = 0;
+ while(this->Running()){
+
+ if(cSchedules::Modified() >= LastEPGUpdate){
+ MESSAGE("Schedule changed. Updating...");
+ updateChannelEPG();
+ LastEPGUpdate = cSchedules::Modified();
+ }
+
+ cCondWait::SleepMs(60 * 1000); // sleep a minute
+ }
+}
+
+void cMediaDatabase::updateChannelEPG(){
+ cUPnPClassContainer* TV = (cUPnPClassContainer*)this->getObjectByID(3);
+ if(TV){
+ // Iterating channels
+ MESSAGE("Getting schedule...");
+ cSchedulesLock SchedulesLock;
+ const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock);
+
+ cList<cUPnPClassObject>* List = TV->getObjectList();
+ MESSAGE("TV folder has %d items", List->Count());
+ for(cUPnPClassVideoBroadcast* ChannelItem = (cUPnPClassVideoBroadcast*)List->First();
+ ChannelItem;
+ ChannelItem = (cUPnPClassVideoBroadcast*)List->Next(ChannelItem)
+ ){
+ MESSAGE("Find channel by number %d", ChannelItem->getChannelNr());
+ cChannel* Channel = Channels.GetByNumber(ChannelItem->getChannelNr());
+ MESSAGE("Found channel with ID %s", *Channel->GetChannelID().ToString());
+
+ const cSchedule* Schedule = Schedules->GetSchedule(Channel);
+ const cEvent* Event = Schedule?Schedule->GetPresentEvent():NULL;
+ if(Event){
+
+ time_t LastEPGChange = Event->StartTime();
+ time_t LastObjectChange = ChannelItem->modified();
+
+ MESSAGE("Last event start: %s", ctime(&LastEPGChange));
+ MESSAGE("Last object modification: %s", ctime(&LastObjectChange));
+ if(LastEPGChange >= LastObjectChange){
+ MESSAGE("Updating details");
+
+ if(Event){
+ ChannelItem->setTitle(Event->Title()?Event->Title():Channel->Name());
+ ChannelItem->setLongDescription(Event->Description());
+ ChannelItem->setDescription(Event->ShortText());
+ }
+ else {
+ ChannelItem->setTitle(Channel->Name());
+ ChannelItem->setLongDescription(NULL);
+ ChannelItem->setDescription(NULL);
+ }
+
+ this->mFactory->saveObject(ChannelItem);
+ }
+ else {
+ MESSAGE("Channel did not change");
+ }
+ }
+ else {
+ MESSAGE("No EPG data");
+ ChannelItem->setTitle(Channel->Name());
+ ChannelItem->setLongDescription(NULL);
+ ChannelItem->setDescription(NULL);
+ }
+ }
+ }
+}
+
+int cMediaDatabase::browse(cUPnPResultSet** Results, const char* ID, bool BrowseMetadata, const char* Filter, unsigned int Offset, unsigned int Count, const char* SortCriteria){
+ *Results = new cUPnPResultSet;
+ (*Results)->mNumberReturned = 0;
+ (*Results)->mTotalMatches = 0;
+ (*Results)->mResult = NULL;
+
+ MESSAGE("===== Browsing =====");
+ MESSAGE("ID: %s", ID);
+ MESSAGE("Browse %s", BrowseMetadata?"metadata":"children");
+ MESSAGE("Filter: %s", Filter);
+ MESSAGE("Offset: %d", Offset);
+ MESSAGE("Count: %d", Count);
+ MESSAGE("Sort: %s", SortCriteria);
+
+ cUPnPObjectID ObjectID = atoi(ID);
+
+ cStringList* FilterList = cFilterCriteria::parse(Filter);
+ cList<cSortCrit>* SortCriterias = cSortCriteria::parse(SortCriteria);
+
+ if(!SortCriterias){
+ return UPNP_CDS_E_INVALID_SORT_CRITERIA;
+ }
+
+ cUPnPClassObject* Object = this->getObjectByID(ObjectID);
+ if(Object){
+ IXML_Document* DIDLDoc = NULL;
+ if(ixmlParseBufferEx(UPNP_DIDL_SKELETON, &DIDLDoc)==IXML_SUCCESS){
+
+ IXML_Node* Root = ixmlNode_getFirstChild((IXML_Node*) DIDLDoc);
+ switch(BrowseMetadata){
+ case true:
+ ixmlNode_appendChild(Root, Object->createDIDLFragment(DIDLDoc, FilterList));
+ delete FilterList;
+ (*Results)->mNumberReturned = 1;
+ (*Results)->mTotalMatches = 1;
+ (*Results)->mResult = ixmlDocumenttoString(DIDLDoc);
+ ixmlDocument_free(DIDLDoc);
+ return UPNP_E_SUCCESS;
+ case false:
+ if(Object->isContainer()){
+ cUPnPClassContainer* Container = Object->getContainer();
+ (*Results)->mTotalMatches = Container->getChildCount();
+ cUPnPObjects* Children = Container->getObjectList();
+
+ if(SortCriterias){
+ for(cSortCrit* SortBy = SortCriterias->First(); SortBy ; SortBy = SortCriterias->Next(SortBy)){
+ MESSAGE("Sorting by %s %s", SortBy->Property, SortBy->SortDescending?"ascending":"descending");
+ Children->SortBy(SortBy->Property, SortBy->SortDescending);
+ }
+ }
+
+ cUPnPClassObject* Child = Children->First();
+ if(Count==0) Count = Container->getChildCount();
+ while(Offset-- && (Child = Children->Next(Child))){}
+ for(; Count && Child ; Child = Children->Next(Child), Count--){
+ MESSAGE("Appending %s to didl", Child->getTitle());
+ ixmlNode_appendChild(Root, Child->createDIDLFragment(DIDLDoc, FilterList));
+ (*Results)->mNumberReturned++;
+ }
+ delete FilterList;
+ delete SortCriterias;
+ }
+ else {
+ (*Results)->mNumberReturned = 0;
+ (*Results)->mTotalMatches = 0;
+ }
+ (*Results)->mResult = ixmlDocumenttoString(DIDLDoc);
+ ixmlDocument_free(DIDLDoc);
+ return UPNP_E_SUCCESS;
+ }
+ }
+ else {
+ ERROR("Unable to parse DIDL skeleton");
+ return UPNP_CDS_E_CANT_PROCESS_REQUEST;
+ }
+ }
+ else {
+ ERROR("No such object: %s", ID);
+ return UPNP_CDS_E_NO_SUCH_OBJECT; // No such object;
+ }
+ return UPNP_SOAP_E_ACTION_FAILED;
+} \ No newline at end of file
diff --git a/database/metadata.h b/database/metadata.h
new file mode 100644
index 0000000..32d663e
--- /dev/null
+++ b/database/metadata.h
@@ -0,0 +1,60 @@
+/*
+ * File: metadata.h
+ * Author: savop
+ *
+ * Created on 28. Mai 2009, 21:14
+ */
+
+#ifndef _METADATA_H
+#define _METADATA_H
+
+#include <vdr/tools.h>
+#include <vdr/channels.h>
+#include <vdr/recording.h>
+#include "../common.h"
+#include "database.h"
+#include "object.h"
+#include "resources.h"
+
+struct cUPnPResultSet {
+ int mNumberReturned;
+ int mTotalMatches;
+ const char* mResult;
+};
+
+struct cSearchCriteria {
+ const char* Property;
+ bool Descending;
+};
+
+class cMediaDatabase : public cThread {
+ friend class cUPnPServer;
+ friend class cUPnPObjectMediator;
+private:
+ unsigned int mSystemUpdateID;
+ cUPnPObjectFactory* mFactory;
+ cHash<cUPnPClassObject>* mObjects;
+ cSQLiteDatabase* mDatabase;
+ cUPnPObjectID mLastInsertObjectID;
+ cUPnPObjectID getNextObjectID();
+ void cacheObject(cUPnPClassObject* Object);
+ int prepareDatabase();
+ int loadChannels();
+ int loadRecordings();
+ void updateChannelEPG();
+ void updateRecordings();
+ bool init();
+ void updateSystemID();
+ virtual void Action();
+public:
+ unsigned int getSystemUpdateID();
+ const char* getContainerUpdateIDs();
+ cMediaDatabase();
+ virtual ~cMediaDatabase();
+ cUPnPClassObject* getObjectByID(cUPnPObjectID ID);
+ int browse(OUT cUPnPResultSet** Results, IN const char* ID, IN bool BrowseMetadata, IN const char* Filter = "*", IN unsigned int Offset = 0, IN unsigned int Count = 0, IN const char* SortCriteria = "");
+ int search(OUT cUPnPResultSet** Results, IN const char* ID, IN const char* Search, IN const char* Filter = "*", IN unsigned int Offset = 0, IN unsigned int Count = 0, IN const char* SortCriteria = "");
+};
+
+#endif /* _METADATA_H */
+
diff --git a/database/object.cpp b/database/object.cpp
new file mode 100644
index 0000000..748078d
--- /dev/null
+++ b/database/object.cpp
@@ -0,0 +1,1702 @@
+/*
+ * File: object.cpp
+ * Author: savop
+ *
+ * Created on 11. September 2009, 20:39
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <upnp/upnptools.h>
+#include <vdr/recording.h>
+#include <vector>
+#include "database.h"
+#include <vdr/tools.h>
+#include <upnp/ixml.h>
+#include "metadata.h"
+#include "object.h"
+#include "../common.h"
+#include "resources.h"
+
+cUPnPResource::cUPnPResource(){
+ this->mBitrate = 0;
+ this->mBitsPerSample = 0;
+ this->mColorDepth = 0;
+ this->mDuration = NULL;
+ this->mImportURI = NULL;
+ this->mNrAudioChannels = 0;
+ this->mObjectID = 0;
+ this->mProtocolInfo = NULL;
+ this->mResolution = NULL;
+ this->mResource = NULL;
+ this->mResourceID = 0;
+ this->mSampleFrequency = 0;
+ this->mSize = 0;
+ this->mContentType = NULL;
+}
+
+off64_t cUPnPResource::getFileSize() const {
+ return (this->mSize) ? this->mSize : -1;
+}
+
+time_t cUPnPResource::getLastModification() const {
+ time_t Time;
+ const cRecording* Recording;
+ const cEvent* Event;
+ switch(this->mResourceType){
+ case UPNP_RESOURCE_CHANNEL:
+ case UPNP_RESOURCE_URL:
+ Time = time(NULL);
+ break;
+ case UPNP_RESOURCE_RECORDING:
+ Recording = Recordings.GetByName(this->mResource);
+ Event = (Recording)?Recording->Info()->GetEvent():NULL;
+ Time = (Event)?Event->EndTime():time(NULL);
+ break;
+ case UPNP_RESOURCE_FILE:
+ //break;
+ default:
+ ERROR("Invalid resource type. This resource might be broken");
+ Time = -1;
+ }
+ return Time;
+}
+
+static int CompareUPnPObjects(const void *a, const void *b){
+ const cUPnPClassObject *la = *(const cUPnPClassObject **)a;
+ const cUPnPClassObject *lb = *(const cUPnPClassObject **)b;
+ return la->Compare(*lb);
+}
+
+cUPnPObjects::cUPnPObjects(){}
+
+cUPnPObjects::~cUPnPObjects(){}
+
+void cUPnPObjects::SortBy(const char* Property, bool Descending){
+ int n = Count();
+ cUPnPClassObject *a[n];
+ cUPnPClassObject *object = (cUPnPClassObject *)objects;
+ int i = 0;
+ while (object && i < n) {
+ object->setSortCriteria(Property, Descending);
+ a[i++] = object;
+ object = (cUPnPClassObject *)object->Next();
+ }
+ qsort(a, n, sizeof(cUPnPClassObject *), CompareUPnPObjects);
+ objects = lastObject = NULL;
+ for (i = 0; i < n; i++) {
+ a[i]->Unlink();
+ count--;
+ Add(a[i]);
+ }
+}
+
+ /**********************************************\
+ * *
+ * UPnP Objects *
+ * *
+ \**********************************************/
+
+ /**********************************************\
+ * *
+ * Object *
+ * *
+ \**********************************************/
+
+cUPnPClassObject::cUPnPClassObject(){
+ this->mID = -1;
+ this->mResources = new cList<cUPnPResource>;
+ this->mResourcesID = new cHash<cUPnPResource>;
+ this->mParent = NULL;
+ this->mClass = NULL;
+ this->mCreator = NULL;
+ this->mTitle = NULL;
+ this->mWriteStatus = WS_UNKNOWN;
+ this->mRestricted = true;
+ this->mDIDLFragment = NULL;
+ this->mSortCriteria = NULL;
+ this->mLastModified = NULL;
+}
+
+cUPnPClassObject::~cUPnPClassObject(){
+ if(this->mParent) this->mParent->getContainer()->removeObject(this);
+ this->mResources->Clear();
+ this->mResourcesID->Clear();
+ delete this->mResources;
+ delete this->mResourcesID;
+ free(this->mDIDLFragment);
+}
+
+int cUPnPClassObject::Compare(const cListObject& ListObject) const {
+ char* Value1 = NULL; char* Value2 = NULL; int ret = 0;
+ cUPnPClassObject* Object = (cUPnPClassObject*)&ListObject;
+ if(Object->getProperty(this->mSortCriteria, &Value1) &&
+ this->getProperty(this->mSortCriteria, &Value2)){
+ ret = strcmp(Value1, Value2);
+ if(this->mSortDescending) ret *= -1;
+ }
+ return ret;
+}
+
+void cUPnPClassObject::setSortCriteria(const char* Property, bool Descending){
+ this->mSortCriteria = Property;
+ this->mSortDescending = Descending;
+}
+
+void cUPnPClassObject::clearSortCriteria(){
+ this->mSortCriteria = NULL;
+ this->mSortDescending = false;
+}
+
+int cUPnPClassObject::setID(cUPnPObjectID ID){
+ MESSAGE("Set ID from %s to %s", *this->getID(),*ID);
+ if((int)ID < 0){
+ ERROR("Invalid object ID '%s'",*ID);
+ return -1;
+ }
+ this->mID = ID;
+ return 0;
+}
+
+int cUPnPClassObject::setParent(cUPnPClassContainer* Parent){
+ if(Parent==NULL){
+ MESSAGE("Object '%s' elected as root object", *this->getID());
+ }
+ // unregister from old parent
+ if(this->mParent && Parent != this->mParent){
+ this->mParent->getContainer()->removeObject(this);
+ }
+ this->mParent = Parent;
+ return 0;
+}
+
+int cUPnPClassObject::setClass(const char* Class){
+ if( !strcasecmp(Class, UPNP_CLASS_ALBUM) ||
+ !strcasecmp(Class, UPNP_CLASS_AUDIO) ||
+ !strcasecmp(Class, UPNP_CLASS_AUDIOBC) ||
+ !strcasecmp(Class, UPNP_CLASS_AUDIOBOOK) ||
+ !strcasecmp(Class, UPNP_CLASS_CONTAINER) ||
+ !strcasecmp(Class, UPNP_CLASS_GENRE) ||
+ !strcasecmp(Class, UPNP_CLASS_IMAGE) ||
+ !strcasecmp(Class, UPNP_CLASS_ITEM) ||
+ !strcasecmp(Class, UPNP_CLASS_MOVIE) ||
+ !strcasecmp(Class, UPNP_CLASS_MOVIEGENRE) ||
+ !strcasecmp(Class, UPNP_CLASS_MUSICALBUM) ||
+ !strcasecmp(Class, UPNP_CLASS_MUSICARTIST) ||
+ !strcasecmp(Class, UPNP_CLASS_MUSICGENRE) ||
+ !strcasecmp(Class, UPNP_CLASS_MUSICTRACK) ||
+ !strcasecmp(Class, UPNP_CLASS_MUSICVIDCLIP) ||
+ !strcasecmp(Class, UPNP_CLASS_OBJECT) ||
+ !strcasecmp(Class, UPNP_CLASS_PERSON) ||
+ !strcasecmp(Class, UPNP_CLASS_PHOTO) ||
+ !strcasecmp(Class, UPNP_CLASS_PHOTOALBUM) ||
+ !strcasecmp(Class, UPNP_CLASS_PLAYLIST) ||
+ !strcasecmp(Class, UPNP_CLASS_PLAYLISTCONT) ||
+ !strcasecmp(Class, UPNP_CLASS_STORAGEFOLD) ||
+ !strcasecmp(Class, UPNP_CLASS_STORAGESYS) ||
+ !strcasecmp(Class, UPNP_CLASS_STORAGEVOL) ||
+ !strcasecmp(Class, UPNP_CLASS_TEXT) ||
+ !strcasecmp(Class, UPNP_CLASS_VIDEO) ||
+ !strcasecmp(Class, UPNP_CLASS_VIDEOBC)
+ ){
+ this->mClass = strdup0(Class);
+ return 0;
+ }
+ else {
+ ERROR("Invalid or unsupported class '%s'", Class);
+ return -1;
+ }
+}
+
+int cUPnPClassObject::setTitle(const char* Title){
+ if(Title==NULL){
+ ERROR("Title is empty but required");
+ return -1;
+ }
+ this->mTitle = strdup0(Title);
+ return 0;
+}
+
+int cUPnPClassObject::setCreator(const char* Creator){
+ this->mCreator = strdup0(Creator);
+ return 0;
+}
+
+int cUPnPClassObject::setRestricted(bool Restricted){
+ this->mRestricted = Restricted;
+ return 0;
+}
+
+int cUPnPClassObject::setWriteStatus(int WriteStatus){
+ if( WriteStatus == WS_MIXED ||
+ WriteStatus == WS_NOT_WRITABLE ||
+ WriteStatus == WS_PROTECTED ||
+ WriteStatus == WS_UNKNOWN ||
+ WriteStatus == WS_WRITABLE){
+ this->mWriteStatus = WriteStatus;
+ return 0;
+ }
+ else {
+ ERROR("Invalid write status '%d'", WriteStatus);
+ return -1;
+ }
+}
+
+bool cUPnPClassObject::getProperty(const char* Property, char** Value) const {
+ cString Val;
+ if(!strcasecmp(Property, SQLITE_COL_OBJECTID) || !strcasecmp(Property, UPNP_PROP_OBJECTID)){
+ Val = *this->getID();
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_PARENTID) || !strcasecmp(Property, UPNP_PROP_PARENTID)){
+ Val = *this->getParentID();
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_CLASS) || !strcasecmp(Property, UPNP_PROP_CLASS)){
+ Val = this->getClass();
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_TITLE) || !strcasecmp(Property, UPNP_PROP_TITLE)){
+ Val = this->getTitle();
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_CREATOR) || !strcasecmp(Property, UPNP_PROP_CREATOR)){
+ Val = this->getCreator();
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_RESTRICTED) || !strcasecmp(Property, UPNP_PROP_RESTRICTED)){
+ Val = this->isRestricted()?"1":"0";
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_WRITESTATUS) || !strcasecmp(Property, UPNP_PROP_WRITESTATUS)){
+ Val = cString::sprintf("%d",this->getWriteStatus());
+ }
+ else {
+ ERROR("Invalid property '%s'", Property);
+ return false;
+ }
+ *Value = strdup0(*Val);
+ return true;
+}
+
+cStringList* cUPnPClassObject::getPropertyList(){
+ cStringList* Properties = new cStringList;
+ Properties->Append(strdup(UPNP_PROP_CREATOR));
+ Properties->Append(strdup(UPNP_PROP_WRITESTATUS));
+ return Properties;
+}
+
+bool cUPnPClassObject::setProperty(const char* Property, const char* Value){
+ int ret;
+ if(!strcasecmp(Property, SQLITE_COL_OBJECTID) || !strcasecmp(Property, UPNP_PROP_OBJECTID)){
+ ERROR("Not allowed to set object ID by hand");
+ return false;
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_PARENTID) || !strcasecmp(Property, UPNP_PROP_PARENTID)){
+ ERROR("Not allowed to set parent ID by hand");
+ return false;
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_CLASS) || !strcasecmp(Property, UPNP_PROP_CLASS)){
+ ERROR("Not allowed to set class by hand");
+ return false;
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_TITLE) || !strcasecmp(Property, UPNP_PROP_TITLE)){
+ ret = this->setTitle(Value);
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_CREATOR) || !strcasecmp(Property, UPNP_PROP_CREATOR)){
+ ret = this->setCreator(Value);
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_RESTRICTED) || !strcasecmp(Property, UPNP_PROP_RESTRICTED)){
+ ret = this->setRestricted(atoi(Value)==1?true:false);
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_WRITESTATUS) || !strcasecmp(Property, UPNP_PROP_WRITESTATUS)){
+ ret= this->setWriteStatus(atoi(Value));
+ }
+ else {
+ ERROR("Invalid property '%s'", Property);
+ return false;
+ }
+ return ret<0?false:true;
+}
+
+int cUPnPClassObject::addResource(cUPnPResource* Resource){
+ MESSAGE("Adding resource #%d", Resource->getID());
+ if(!Resource){
+ ERROR("No resource");
+ return -1;
+ }
+ this->mResources->Add(Resource);
+ this->mResourcesID->Add(Resource, Resource->getID());
+ return 0;
+}
+
+int cUPnPClassObject::removeResource(cUPnPResource* Resource){
+ if(!Resource){
+ ERROR("No resource");
+ return -1;
+ }
+ this->mResourcesID->Del(Resource, Resource->getID());
+ this->mResources->Del(Resource);
+ return 0;
+}
+
+ /**********************************************\
+ * *
+ * Item *
+ * *
+ \**********************************************/
+
+cUPnPClassItem::cUPnPClassItem(){
+ this->setClass(UPNP_CLASS_ITEM);
+ this->mReference = NULL;
+}
+
+int cUPnPClassItem::setReference(cUPnPClassItem* Reference){
+ this->mReference = Reference;
+ return 0;
+}
+
+cStringList* cUPnPClassItem::getPropertyList(){
+ cStringList* Properties = cUPnPClassObject::getPropertyList();
+ Properties->Append(strdup(UPNP_PROP_REFERENCEID));
+ return Properties;
+}
+
+bool cUPnPClassItem::getProperty(const char* Property, char** Value) const {
+
+ if(!strcasecmp(Property, SQLITE_COL_REFERENCEID) || !strcasecmp(Property, UPNP_PROP_REFERENCEID)){
+ *Value = strdup0(*this->getReferenceID());
+ }
+ else return cUPnPClassObject::getProperty(Property, Value);
+ return true;
+}
+
+bool cUPnPClassItem::setProperty(const char* Property, const char* Value){
+ return cUPnPClassObject::setProperty(Property, Value);
+}
+
+IXML_Node* cUPnPClassItem::createDIDLFragment(IXML_Document* Document, cStringList* Filter){
+ this->mDIDLFragment = Document;
+
+ MESSAGE("==(%s)= %s =====", *this->getID(), this->getTitle());
+ MESSAGE("ParentID: %s", *this->getParentID());
+ MESSAGE("Restricted: %s", this->isRestricted()?"1":"0");
+ MESSAGE("Class: %s", this->getClass());
+
+ IXML_Node* Didl = ixmlNode_getFirstChild((IXML_Node*) this->mDIDLFragment);
+
+ IXML_Element* eItem = ixmlDocument_createElement(this->mDIDLFragment, "item");
+ ixmlElement_setAttribute(eItem, att(UPNP_PROP_OBJECTID), *this->getID());
+ ixmlElement_setAttribute(eItem, att(UPNP_PROP_PARENTID), *this->getParentID());
+ ixmlElement_setAttribute(eItem, att(UPNP_PROP_RESTRICTED), this->isRestricted()?"1":"0");
+
+ ixmlNode_appendChild(Didl, (IXML_Node*) eItem);
+
+ IXML_Element* eTitle = ixmlDocument_createElement(this->mDIDLFragment, UPNP_PROP_TITLE);
+ IXML_Node* Title = ixmlDocument_createTextNode(this->mDIDLFragment, this->getTitle());
+
+ IXML_Element* eClass = ixmlDocument_createElement(this->mDIDLFragment, UPNP_PROP_CLASS);
+ IXML_Node* Class = ixmlDocument_createTextNode(this->mDIDLFragment, this->getClass());
+
+ ixmlNode_appendChild((IXML_Node*) eTitle, Title);
+ ixmlNode_appendChild((IXML_Node*) eClass, Class);
+ ixmlNode_appendChild((IXML_Node*) eItem, (IXML_Node*) eTitle);
+ ixmlNode_appendChild((IXML_Node*) eItem, (IXML_Node*) eClass);
+
+// if(Filter==NULL || Filter->Find(UPNP_PROP_CREATOR)) ixmlAddProperty(this->mDIDLFragment, eItem, UPNP_PROP_CREATOR, this->getCreator());
+// if(Filter==NULL || Filter->Find(UPNP_PROP_WRITESTATUS)) ixmlAddProperty(this->mDIDLFragment, eItem, UPNP_PROP_WRITESTATUS, itoa(this->getWriteStatus()));
+// if(Filter==NULL || Filter->Find(UPNP_PROP_REFERENCEID)) ixmlAddProperty(this->mDIDLFragment, eItem, UPNP_PROP_REFERENCEID, *this->getReferenceID());
+
+ for(cUPnPResource* Resource = this->getResources()->First(); Resource; Resource = this->getResources()->Next(Resource)){
+ MESSAGE("Resource: %s", Resource->getResource());
+ MESSAGE("Protocolinfo: %s", Resource->getProtocolInfo());
+
+ cString URLBase = cString::sprintf("http://%s:%d", UpnpGetServerIpAddress(), UpnpGetServerPort());
+ cString ResourceURL = cString::sprintf("%s%s/get?resId=%d", *URLBase, UPNP_DIR_SHARES, Resource->getID());
+
+ MESSAGE("Resource-URI: %s", *ResourceURL);
+
+ IXML_Element* eRes = ixmlDocument_createElement(this->mDIDLFragment, UPNP_PROP_RESOURCE);
+ IXML_Node* Res = ixmlDocument_createTextNode(this->mDIDLFragment, *ResourceURL);
+ ixmlNode_appendChild((IXML_Node*) eRes, Res);
+
+ if(Resource->getBitrate()) ixmlElement_setAttribute(eRes, att(UPNP_PROP_BITRATE), itoa(Resource->getBitrate()));
+ if(Resource->getBitsPerSample()) ixmlElement_setAttribute(eRes, att(UPNP_PROP_BITSPERSAMPLE), itoa(Resource->getBitsPerSample()));
+ if(Resource->getColorDepth()) ixmlElement_setAttribute(eRes, att(UPNP_PROP_COLORDEPTH), itoa(Resource->getColorDepth()));
+ if(Resource->getDuration()) ixmlElement_setAttribute(eRes, att(UPNP_PROP_DURATION), Resource->getDuration());
+ if(Resource->getProtocolInfo()) ixmlElement_setAttribute(eRes, att(UPNP_PROP_PROTOCOLINFO), Resource->getProtocolInfo());
+
+ ixmlNode_appendChild((IXML_Node*) eItem, (IXML_Node*) eRes);
+ }
+
+ return (IXML_Node*)eItem;
+}
+
+ /**********************************************\
+ * *
+ * Container *
+ * *
+ \**********************************************/
+
+cUPnPClassContainer::cUPnPClassContainer(){
+ this->setClass(UPNP_CLASS_CONTAINER);
+ this->mChildren = new cUPnPObjects;
+ this->mChildrenID = new cHash<cUPnPClassObject>;
+ this->mContainerType = NULL;
+ this->mUpdateID = 0;
+ this->mSearchable = false;
+}
+
+cUPnPClassContainer::~cUPnPClassContainer(){
+ delete this->mChildren;
+ delete this->mChildrenID;
+}
+
+IXML_Node* cUPnPClassContainer::createDIDLFragment(IXML_Document* Document, cStringList* Filter){
+ this->mDIDLFragment = Document;
+
+ MESSAGE("===(%s)= %s =====", *this->getID(), this->getTitle());
+ MESSAGE("ParentID: %s", *this->getParentID());
+ MESSAGE("Restricted: %s", this->isRestricted()?"1":"0");
+ MESSAGE("Class: %s", this->getClass());
+
+ IXML_Node* Didl = ixmlNode_getFirstChild((IXML_Node*) this->mDIDLFragment);
+ IXML_Element* eItem = ixmlDocument_createElement(this->mDIDLFragment, "container");
+ ixmlElement_setAttribute(eItem, att(UPNP_PROP_OBJECTID), *this->getID());
+ ixmlElement_setAttribute(eItem, att(UPNP_PROP_PARENTID), *this->getParentID());
+ ixmlElement_setAttribute(eItem, att(UPNP_PROP_RESTRICTED), this->isRestricted()?"1":"0");
+ ixmlNode_appendChild(Didl, (IXML_Node*) eItem);
+
+ IXML_Element* eTitle = ixmlDocument_createElement(this->mDIDLFragment, UPNP_PROP_TITLE);
+ IXML_Node* Title = ixmlDocument_createTextNode(this->mDIDLFragment, this->getTitle());
+
+ IXML_Element* eClass = ixmlDocument_createElement(this->mDIDLFragment, UPNP_PROP_CLASS);
+ IXML_Node* Class = ixmlDocument_createTextNode(this->mDIDLFragment, this->getClass());
+
+ ixmlNode_appendChild((IXML_Node*) eTitle, Title);
+ ixmlNode_appendChild((IXML_Node*) eClass, Class);
+ ixmlNode_appendChild((IXML_Node*) eItem, (IXML_Node*) eTitle);
+ ixmlNode_appendChild((IXML_Node*) eItem, (IXML_Node*) eClass);
+
+ return (IXML_Node*)eItem;
+}
+
+int cUPnPClassContainer::setUpdateID(unsigned int UID){
+ this->mUpdateID = UID;
+ return 0;
+}
+
+cStringList* cUPnPClassContainer::getPropertyList(){
+ cStringList* Properties = cUPnPClassObject::getPropertyList();
+ Properties->Append(strdup(UPNP_PROP_DLNA_CONTAINERTYPE));
+ Properties->Append(strdup(UPNP_PROP_SEARCHABLE));
+ return Properties;
+}
+
+bool cUPnPClassContainer::setProperty(const char* Property, const char* Value){
+ int ret;
+ if(!strcasecmp(Property, SQLITE_COL_DLNA_CONTAINERTYPE) || !strcasecmp(Property, UPNP_PROP_DLNA_CONTAINERTYPE)){
+ ret = this->setContainerType(Value);
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_SEARCHABLE) || !strcasecmp(Property, UPNP_PROP_SEARCHABLE)){
+ ret = this->setSearchable(Value);
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_CONTAINER_UID)){
+ ret = this->setUpdateID((unsigned int)atoi(Value));
+ }
+ else return cUPnPClassObject::setProperty(Property, Value);
+ return ret<0?false:true;
+}
+
+bool cUPnPClassContainer::getProperty(const char* Property, char** Value) const {
+ cString Val;
+ if(!strcasecmp(Property, SQLITE_COL_DLNA_CONTAINERTYPE) || !strcasecmp(Property, UPNP_PROP_DLNA_CONTAINERTYPE)){
+ Val = this->getContainerType();
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_SEARCHABLE) || !strcasecmp(Property, UPNP_PROP_SEARCHABLE)){
+ Val = this->isSearchable()?"1":"0";
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_CONTAINER_UID)){
+ Val = cString::sprintf("%d", this->getUpdateID());
+ }
+ else return cUPnPClassObject::getProperty(Property, Value);
+ *Value = strdup0(*Val);
+ return true;
+}
+
+void cUPnPClassContainer::addObject(cUPnPClassObject* Object){
+ MESSAGE("Adding object (ID:%s) to container (ID:%s)", *Object->getID(), *this->getID());
+ this->mChildren->Add(Object);
+ this->mChildrenID->Add(Object, (unsigned int)Object->getID());
+ Object->setParent(this);
+}
+
+void cUPnPClassContainer::removeObject(cUPnPClassObject* Object){
+ MESSAGE("Removing object (ID:%s) from container (ID:%s)", *Object->getID(), *this->getID());
+ this->mChildrenID->Del(Object, (unsigned int)Object->getID());
+ this->mChildren->Del(Object, false);
+ Object->mParent = NULL;
+}
+
+cUPnPClassObject* cUPnPClassContainer::getObject(cUPnPObjectID ID) const {
+ MESSAGE("Getting object (ID:%s)", *ID);
+ if((int)ID < 0){
+ ERROR("Invalid object ID");
+ return NULL;
+ }
+ return this->mChildrenID->Get((unsigned int)ID);
+}
+
+int cUPnPClassContainer::setContainerType(const char* Type){
+ if(Type==NULL){
+ this->mContainerType = Type;
+ }
+ else if(!strcasecmp(Type, DLNA_CONTAINER_TUNER)){
+ this->mContainerType = Type;
+ }
+ else {
+ ERROR("Invalid container type '%s'",Type);
+ return -1;
+ }
+ return 0;
+}
+
+int cUPnPClassContainer::addSearchClass(cClass SearchClass){
+ this->mSearchClasses.push_back(SearchClass);
+ return 0;
+}
+
+int cUPnPClassContainer::delSearchClass(cClass SearchClass){
+ tClassVector::iterator it = this->mSearchClasses.begin();
+ cClass Class;
+ for(unsigned int i=0; i<this->mSearchClasses.size(); i++){
+ Class = this->mSearchClasses[i];
+ if(Class == SearchClass){
+ this->mSearchClasses.erase(it+i);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int cUPnPClassContainer::addCreateClass(cClass CreateClass){
+ this->mCreateClasses.push_back(CreateClass);
+ return 0;
+}
+
+int cUPnPClassContainer::delCreateClass(cClass CreateClass){
+ tClassVector::iterator it = this->mCreateClasses.begin();
+ cClass Class;
+ for(unsigned int i=0; i<this->mCreateClasses.size(); i++){
+ Class = this->mCreateClasses[i];
+ if(Class == CreateClass){
+ this->mCreateClasses.erase(it+i);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int cUPnPClassContainer::setSearchClasses(std::vector<cClass> SearchClasses){
+ this->mSearchClasses = SearchClasses;
+ return 0;
+}
+
+int cUPnPClassContainer::setCreateClasses(std::vector<cClass> CreateClasses){
+ this->mCreateClasses = CreateClasses;
+ return 0;
+}
+
+int cUPnPClassContainer::setSearchable(bool Searchable){
+ this->mSearchable = Searchable;
+ return 0;
+}
+
+bool cUPnPClassContainer::isUpdated(){
+ static unsigned int lastUpdateID = this->getUpdateID();
+ if(lastUpdateID != this->getUpdateID()){
+ lastUpdateID = this->getUpdateID();
+ return true;
+ }
+ else return false;
+}
+
+ /**********************************************\
+ * *
+ * Video item *
+ * *
+ \**********************************************/
+
+cUPnPClassVideoItem::cUPnPClassVideoItem(){
+ this->setClass(UPNP_CLASS_VIDEO);
+ this->mGenre = NULL;
+ this->mLongDescription = NULL;
+ this->mProducers = NULL;
+ this->mRating = NULL;
+ this->mActors = NULL;
+ this->mDirectors = NULL;
+ this->mDescription = NULL;
+ this->mPublishers = NULL;
+ this->mLanguage = NULL;
+ this->mRelations = NULL;
+}
+
+cUPnPClassVideoItem::~cUPnPClassVideoItem(){
+}
+
+//cString cUPnPClassVideoItem::createDIDLFragment(cStringList* Filter){
+// return NULL;
+//}
+
+cStringList* cUPnPClassVideoItem::getPropertyList(){
+ cStringList* Properties = cUPnPClassItem::getPropertyList();
+ Properties->Append(strdup(UPNP_PROP_LONGDESCRIPTION));
+ Properties->Append(strdup(UPNP_PROP_PRODUCER));
+ Properties->Append(strdup(UPNP_PROP_GENRE));
+ Properties->Append(strdup(UPNP_PROP_RATING));
+ Properties->Append(strdup(UPNP_PROP_ACTOR));
+ Properties->Append(strdup(UPNP_PROP_DIRECTOR));
+ Properties->Append(strdup(UPNP_PROP_DESCRIPTION));
+ Properties->Append(strdup(UPNP_PROP_PUBLISHER));
+ Properties->Append(strdup(UPNP_PROP_LANGUAGE));
+ Properties->Append(strdup(UPNP_PROP_RELATION));
+ return Properties;
+}
+
+bool cUPnPClassVideoItem::getProperty(const char* Property, char** Value) const {
+ cString Val;
+ if(!strcasecmp(Property,SQLITE_COL_GENRE) || !strcasecmp(Property,UPNP_PROP_GENRE)){
+ Val = this->getGenre();
+ }
+ else if(!strcasecmp(Property,SQLITE_COL_LONGDESCRIPTION) || !strcasecmp(Property,UPNP_PROP_LONGDESCRIPTION)){
+ Val = this->getLongDescription();
+ }
+ else if(!strcasecmp(Property,SQLITE_COL_PRODUCER) || !strcasecmp(Property,UPNP_PROP_PRODUCER)){
+ Val = this->getProducers();
+ }
+ else if(!strcasecmp(Property,SQLITE_COL_RATING) || !strcasecmp(Property,UPNP_PROP_RATING)){
+ Val = this->getRating();
+ }
+ else if(!strcasecmp(Property,SQLITE_COL_ACTOR) || !strcasecmp(Property,UPNP_PROP_ACTOR)){
+ Val = this->getActors();
+ }
+ else if(!strcasecmp(Property,SQLITE_COL_DIRECTOR) || !strcasecmp(Property,UPNP_PROP_DIRECTOR)){
+ Val = this->getDirectors();
+ }
+ else if(!strcasecmp(Property,SQLITE_COL_DESCRIPTION) || !strcasecmp(Property,UPNP_PROP_DESCRIPTION)){
+ Val = this->getDescription();
+ }
+ else if(!strcasecmp(Property,SQLITE_COL_PUBLISHER) || !strcasecmp(Property,UPNP_PROP_PUBLISHER)){
+ Val = this->getPublishers();
+ }
+ else if(!strcasecmp(Property,SQLITE_COL_LANGUAGE) || !strcasecmp(Property,UPNP_PROP_LANGUAGE)){
+ Val = this->getLanguage();
+ }
+ else if(!strcasecmp(Property,SQLITE_COL_RELATION) || !strcasecmp(Property,UPNP_PROP_RELATION)){
+ Val = this->getRelations();
+ }
+ else return cUPnPClassItem::getProperty(Property, Value);
+ *Value = strdup0(*Val);
+ return true;
+}
+
+bool cUPnPClassVideoItem::setProperty(const char* Property, const char* Value){
+ bool ret;
+ if(!strcasecmp(Property,SQLITE_COL_GENRE) || !strcasecmp(Property,UPNP_PROP_GENRE)){
+ ret = this->setGenre(Value);
+ }
+ else if(!strcasecmp(Property,SQLITE_COL_LONGDESCRIPTION) || !strcasecmp(Property,UPNP_PROP_LONGDESCRIPTION)){
+ ret = this->setLongDescription(Value);
+ }
+ else if(!strcasecmp(Property,SQLITE_COL_PRODUCER) || !strcasecmp(Property,UPNP_PROP_PRODUCER)){
+ ret = this->setProducers(Value);
+ }
+ else if(!strcasecmp(Property,SQLITE_COL_RATING) || !strcasecmp(Property,UPNP_PROP_RATING)){
+ ret = this->setRating(Value);
+ }
+ else if(!strcasecmp(Property,SQLITE_COL_ACTOR) || !strcasecmp(Property,UPNP_PROP_ACTOR)){
+ ret = this->setActors(Value);
+ }
+ else if(!strcasecmp(Property,SQLITE_COL_DIRECTOR) || !strcasecmp(Property,UPNP_PROP_DIRECTOR)){
+ ret = this->setDirectors(Value);
+ }
+ else if(!strcasecmp(Property,SQLITE_COL_DESCRIPTION) || !strcasecmp(Property,UPNP_PROP_DESCRIPTION)){
+ ret = this->setDescription(Value);
+ }
+ else if(!strcasecmp(Property,SQLITE_COL_PUBLISHER) || !strcasecmp(Property,UPNP_PROP_PUBLISHER)){
+ ret = this->setPublishers(Value);
+ }
+ else if(!strcasecmp(Property,SQLITE_COL_LANGUAGE) || !strcasecmp(Property,UPNP_PROP_LANGUAGE)){
+ ret = this->setLanguage(Value);
+ }
+ else if(!strcasecmp(Property,SQLITE_COL_RELATION) || !strcasecmp(Property,UPNP_PROP_RELATION)){
+ ret = this->setRelations(Value);
+ }
+ else return cUPnPClassItem::setProperty(Property, Value);
+ return ret<0?false:true;
+}
+
+int cUPnPClassVideoItem::setActors(const char* Actors){
+ this->mActors = Actors;
+ return 0;
+}
+
+int cUPnPClassVideoItem::setGenre(const char* Genre){
+ this->mGenre = Genre;
+ return 0;
+}
+
+int cUPnPClassVideoItem::setDescription(const char* Description){
+ this->mDescription = Description;
+ return 0;
+}
+
+int cUPnPClassVideoItem::setLongDescription(const char* LongDescription){
+ this->mLongDescription = LongDescription;
+ return 0;
+}
+
+int cUPnPClassVideoItem::setProducers(const char* Producers){
+ this->mProducers = Producers;
+ return 0;
+}
+
+int cUPnPClassVideoItem::setRating(const char* Rating){
+ this->mRating = Rating;
+ return 0;
+}
+
+int cUPnPClassVideoItem::setDirectors(const char* Directors){
+ this->mDirectors = Directors;
+ return 0;
+}
+
+int cUPnPClassVideoItem::setPublishers(const char* Publishers){
+ this->mPublishers = Publishers;
+ return 0;
+}
+
+int cUPnPClassVideoItem::setLanguage(const char* Language){
+ this->mLanguage = Language;
+ return 0;
+}
+
+int cUPnPClassVideoItem::setRelations(const char* Relations){
+ this->mRelations = Relations;
+ return 0;
+}
+
+ /**********************************************\
+ * *
+ * Video Broadcast item *
+ * *
+ \**********************************************/
+
+cUPnPClassVideoBroadcast::cUPnPClassVideoBroadcast(){
+ this->setClass(UPNP_CLASS_VIDEOBC);
+ this->mIcon = NULL;
+ this->mRegion = NULL;
+ this->mChannelNr = 0;
+}
+
+cUPnPClassVideoBroadcast::~cUPnPClassVideoBroadcast(){
+}
+
+//cString cUPnPClassVideoBroadcast::createDIDLFragment(cStringList* Filter){
+// return NULL;
+//}
+
+cStringList* cUPnPClassVideoBroadcast::getPropertyList(){
+ cStringList* Properties = cUPnPClassVideoItem::getPropertyList();
+ Properties->Append(strdup(UPNP_PROP_CHANNELNAME));
+ Properties->Append(strdup(UPNP_PROP_CHANNELNR));
+ Properties->Append(strdup(UPNP_PROP_ICON));
+ Properties->Append(strdup(UPNP_PROP_REGION));
+ return Properties;
+}
+
+bool cUPnPClassVideoBroadcast::setProperty(const char* Property, const char* Value){
+ bool ret;
+ if(!strcasecmp(Property, SQLITE_COL_CHANNELNAME) || !strcasecmp(Property, UPNP_PROP_CHANNELNAME)){
+ ret = this->setChannelName(Value);
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_CHANNELNR) || !strcasecmp(Property, UPNP_PROP_CHANNELNR)){
+ ret = this->setChannelNr(atoi(Value));
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_ICON) || !strcasecmp(Property, UPNP_PROP_ICON)){
+ ret = this->setIcon(Value);
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_REGION) || !strcasecmp(Property, UPNP_PROP_REGION)){
+ ret = this->setRegion(Value);
+ }
+ else return cUPnPClassVideoItem::setProperty(Property, Value);
+ return ret<0?false:true;
+}
+
+bool cUPnPClassVideoBroadcast::getProperty(const char* Property, char** Value) const {
+ cString Val;
+ if(!strcasecmp(Property, SQLITE_COL_CHANNELNAME) || !strcasecmp(Property, UPNP_PROP_CHANNELNAME)){
+ Val = this->getChannelName();
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_CHANNELNR) || !strcasecmp(Property, UPNP_PROP_CHANNELNR)){
+ Val = itoa(this->getChannelNr());
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_ICON) || !strcasecmp(Property, UPNP_PROP_ICON)){
+ Val = this->getIcon();
+ }
+ else if(!strcasecmp(Property, SQLITE_COL_REGION) || !strcasecmp(Property, UPNP_PROP_REGION)){
+ Val = this->getRegion();
+ }
+ else return cUPnPClassVideoItem::getProperty(Property, Value);
+ *Value = strdup0(*Val);
+ return true;
+}
+
+int cUPnPClassVideoBroadcast::setChannelName(const char* ChannelName){
+ this->mChannelName = ChannelName;
+ return 0;
+}
+
+int cUPnPClassVideoBroadcast::setChannelNr(int ChannelNr){
+ this->mChannelNr = ChannelNr;
+ return 0;
+}
+
+int cUPnPClassVideoBroadcast::setIcon(const char* IconURI){
+ this->mIcon = IconURI;
+ return 0;
+}
+
+int cUPnPClassVideoBroadcast::setRegion(const char* Region){
+ this->mRegion = Region;
+ return 0;
+}
+
+ /**********************************************\
+ * *
+ * Mediator factory *
+ * *
+ \**********************************************/
+
+cUPnPObjectFactory* cUPnPObjectFactory::mInstance = NULL;
+
+cUPnPObjectFactory* cUPnPObjectFactory::getInstance(){
+ if(!cUPnPObjectFactory::mInstance)
+ cUPnPObjectFactory::mInstance = new cUPnPObjectFactory();
+
+ if(cUPnPObjectFactory::mInstance) return cUPnPObjectFactory::mInstance;
+ else return NULL;
+}
+
+cUPnPObjectFactory::cUPnPObjectFactory(){
+ this->mDatabase = cSQLiteDatabase::getInstance();
+}
+
+void cUPnPObjectFactory::registerMediator(const char* UPnPClass, cMediatorInterface* Mediator){
+ if(UPnPClass == NULL){
+ ERROR("Class is undefined");
+ return;
+ }
+ if(Mediator == NULL){
+ ERROR("Mediator is undefined");
+ return;
+ }
+ MESSAGE("Registering mediator for class '%s'", UPnPClass);
+ this->mMediators[UPnPClass] = Mediator;
+ MESSAGE("Now %d mediators registered", this->mMediators.size());
+ return;
+}
+
+void cUPnPObjectFactory::unregisterMediator(const char* UPnPClass, bool freeMediator){
+ if(UPnPClass == NULL){
+ ERROR("Class is undefined");
+ return;
+ }
+ tMediatorMap::iterator MediatorIterator = this->mMediators.find(UPnPClass);
+ if(MediatorIterator==this->mMediators.end()){
+ ERROR("No such mediator found for class '%s'", UPnPClass);
+ return;
+ }
+ MESSAGE("Unregistering mediator for class '%s'", UPnPClass);
+ this->mMediators.erase(MediatorIterator);
+ if(freeMediator) delete MediatorIterator->second;
+ MESSAGE("Now %d mediators registered", this->mMediators.size());
+ return;
+}
+
+cMediatorInterface* cUPnPObjectFactory::findMediatorByID(cUPnPObjectID ID){
+ cString Format = "SELECT %s FROM %s WHERE %s=%s";
+ cString Statement = NULL, Column = NULL, Value = NULL, Class = NULL;
+ cRows* Rows; cRow* Row;
+ Statement = cString::sprintf(Format, SQLITE_COL_CLASS, SQLITE_TABLE_OBJECTS, SQLITE_COL_OBJECTID, *ID);
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return NULL;
+ }
+ Rows = this->mDatabase->getResultRows();
+ if(!Rows->fetchRow(&Row)){
+ ERROR("No such object with ID '%s'",*ID);
+ return NULL;
+ }
+ while(Row->fetchColumn(&Column, &Value)){
+ if(!strcasecmp(Column, SQLITE_COL_CLASS)){
+ Class = strdup0(*Value);
+ }
+ }
+ return this->findMediatorByClass(Class);
+}
+
+cMediatorInterface* cUPnPObjectFactory::findMediatorByClass(const char* Class){
+ if(!Class){ ERROR("No class specified"); return NULL; }
+ MESSAGE("Searching for mediator '%s' in %d mediators", Class, this->mMediators.size());
+ tMediatorMap::iterator MediatorIterator = this->mMediators.find(Class);
+ if(MediatorIterator==this->mMediators.end()){
+ ERROR("No matching mediator for class '%s'",Class);
+ return NULL;
+ }
+ else {
+ return MediatorIterator->second;
+ }
+}
+
+cUPnPClassObject* cUPnPObjectFactory::getObject(cUPnPObjectID ID){
+ cMediatorInterface* Mediator = this->findMediatorByID(ID);
+ if(Mediator) return Mediator->getObject(ID);
+ else {
+ return NULL;
+ }
+}
+
+cUPnPClassObject* cUPnPObjectFactory::createObject(const char* UPnPClass, const char* Title, bool Restricted){
+ cMediatorInterface* Mediator = this->findMediatorByClass(UPnPClass);
+ return Mediator->createObject(Title, Restricted);
+}
+
+int cUPnPObjectFactory::deleteObject(cUPnPClassObject* Object){
+ cMediatorInterface* Mediator = this->findMediatorByClass(Object->getClass());
+ return Mediator->deleteObject(Object);
+}
+
+int cUPnPObjectFactory::clearObject(cUPnPClassObject* Object){
+ cMediatorInterface* Mediator = this->findMediatorByClass(Object->getClass());
+ return Mediator->clearObject(Object);
+}
+
+int cUPnPObjectFactory::saveObject(cUPnPClassObject* Object){
+ cMediatorInterface* Mediator = this->findMediatorByClass(Object->getClass());
+ return Mediator->saveObject(Object);
+}
+
+ /**********************************************\
+ * *
+ * Mediators *
+ * *
+ \**********************************************/
+
+ /**********************************************\
+ * *
+ * Object mediator *
+ * *
+ \**********************************************/
+
+cUPnPObjectMediator::cUPnPObjectMediator(cMediaDatabase* MediaDatabase) :
+ mMediaDatabase(MediaDatabase){
+ this->mDatabase = cSQLiteDatabase::getInstance();
+}
+
+cUPnPObjectMediator::~cUPnPObjectMediator(){
+ delete this->mDatabase;
+ delete this->mMediaDatabase;
+}
+
+int cUPnPObjectMediator::saveObject(cUPnPClassObject* Object){
+ bool succesful = true;
+
+ this->mDatabase->startTransaction();
+ if(Object->getID() == -1) succesful = false;
+ else if(this->objectToDatabase(Object)) succesful = false;
+ else succesful = true;
+
+ if(succesful){
+ this->mDatabase->commitTransaction();
+ Object->setModified();
+ this->mMediaDatabase->cacheObject(Object);
+ this->mMediaDatabase->updateSystemID();
+ return 0;
+ }
+ else {
+ this->mDatabase->rollbackTransaction();
+ return -1;
+ }
+ return -1;
+}
+
+int cUPnPObjectMediator::deleteObject(cUPnPClassObject* Object){
+ cString Statement = NULL;
+ cString Format = "DELETE FROM %s WHERE %s=%s";
+ Statement = cString::sprintf(Format, SQLITE_TABLE_OBJECTS, SQLITE_COL_OBJECTID, *Object->getID());
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+ #ifdef SQLITE_CASCADE_DELETES
+ this->clearObject(Object);
+ #endif
+ delete Object; Object = NULL;
+ return 0;
+}
+
+int cUPnPObjectMediator::clearObject(cUPnPClassObject* Object){
+ cUPnPClassContainer* Container = Object->getContainer();
+ if(Container){
+ cList<cUPnPClassObject>* List = Container->getObjectList();
+ for(cUPnPClassObject* Child = List->First(); Child; Child = List->Next(Child)){
+ if(this->deleteObject(Child)) return -1;
+ }
+ }
+ return 0;
+}
+
+int cUPnPObjectMediator::initializeObject(cUPnPClassObject* Object, const char* Class, const char* Title, bool Restricted){
+ cUPnPObjectID ObjectID = this->mMediaDatabase->getNextObjectID();
+ if(Object->setID(ObjectID)){
+ ERROR("Error while setting ID");
+ return -1;
+ }
+ cUPnPClassObject* Root = this->mMediaDatabase->getObjectByID(0);
+ if(Root){
+ Root->getContainer()->addObject(Object);
+ }
+ else {
+ Object->setParent(NULL);
+ }
+ if(Object->setClass(Class)){
+ ERROR("Error while setting class");
+ return -1;
+ }
+ if(Object->setTitle(Title)){
+ ERROR("Error while setting title");
+ return -1;
+ }
+ if(Object->setRestricted(Restricted)){
+ ERROR("Error while setting restriction");
+ return -1;
+ }
+ char* escapedTitle;
+ escapeSQLite(Object->getTitle(), &escapedTitle);
+ cString Statement = cString::sprintf("INSERT INTO %s (%s, %s, %s, %s, %s) VALUES (%s, %s, '%s', '%s', %d)",
+ SQLITE_TABLE_OBJECTS,
+ SQLITE_COL_OBJECTID,
+ SQLITE_COL_PARENTID,
+ SQLITE_COL_CLASS,
+ SQLITE_COL_TITLE,
+ SQLITE_COL_RESTRICTED,
+ *Object->getID(),
+ *Object->getParentID(),
+ Object->getClass(),
+ escapedTitle,
+ Object->isRestricted()?1:0
+ );
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+ free(escapedTitle);
+ return 0;
+}
+
+cUPnPClassObject* cUPnPObjectMediator::getObject(cUPnPObjectID){ WARNING("Getting instance of class 'Object' forbidden"); return NULL; }
+
+cUPnPClassObject* cUPnPObjectMediator::createObject(const char*, bool){ WARNING("Getting instance of class 'Object' forbidden"); return NULL; }
+
+int cUPnPObjectMediator::objectToDatabase(cUPnPClassObject* Object){
+ cString Format = "UPDATE %s SET %s WHERE %s='%s'";
+ //cString Format = "INSERT OR REPLACE INTO %s (%s) VALUES (%s);";
+ cString Statement=NULL;
+ cString Set=NULL;
+ //cString Columns=NULL, Values=NULL;
+ char *Value=NULL;
+ cString Properties[] = {
+ SQLITE_COL_OBJECTID,
+ SQLITE_COL_PARENTID,
+ SQLITE_COL_CLASS,
+ SQLITE_COL_TITLE,
+ SQLITE_COL_RESTRICTED,
+ SQLITE_COL_CREATOR,
+ SQLITE_COL_WRITESTATUS,
+ NULL
+ };
+ for(cString* Property = Properties; *(*Property)!=NULL; Property++){
+ //Columns = cString::sprintf("%s%s%s", *Columns?*Columns:"", *Columns?",":"", *(*Property));
+ if(!Object->getProperty(*Property, &Value)){
+ ERROR("No such property '%s' in object with ID '%s'",*(*Property),*Object->getID());
+ return -1;
+ }
+ char *escapedValue;
+ escapeSQLite(Value, &escapedValue);
+ //Values = cString::sprintf("%s%s'%s'", *Values?*Values:"", *Values?",":"", Value?Value:"NULL");
+ Set = cString::sprintf("%s%s%s='%s'", *Set?*Set:"", *Set?",":"", *(*Property), escapedValue?escapedValue:"NULL");
+
+ }
+ Statement = cString::sprintf(Format, SQLITE_TABLE_OBJECTS, *Set, SQLITE_COL_OBJECTID, *Object->getID());
+ //Statement = cString::sprintf(Format, SQLITE_TABLE_OBJECTS, *Columns, *Values);
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+ return 0;
+}
+
+int cUPnPObjectMediator::databaseToObject(cUPnPClassObject* Object, cUPnPObjectID ID){
+ cString Format = "SELECT * FROM %s WHERE %s=%s";
+ cString Statement = NULL, Column = NULL, Value = NULL;
+ cRows* Rows; cRow* Row;
+ Statement = cString::sprintf(Format, SQLITE_TABLE_OBJECTS, SQLITE_COL_OBJECTID, *ID);
+// MESSAGE("Fehler hier");
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+ Rows = this->mDatabase->getResultRows();
+ if(!Rows->fetchRow(&Row)){
+ ERROR("No such object with ID '%s'",*ID);
+ return -1;
+ }
+ while(Row->fetchColumn(&Column, &Value)){
+ if(!strcasecmp(Column, SQLITE_COL_OBJECTID)){
+ if(Object->setID(atoi(Value))){
+ ERROR("Error while setting object ID");
+ return -1;
+ }
+ this->mMediaDatabase->cacheObject(Object);
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_PARENTID)){
+ cUPnPObjectID RefID = atoi(Value);
+ cUPnPClassContainer* ParentObject;
+ if(RefID == -1){
+ ParentObject = NULL;
+ }
+ else {
+ ParentObject = (cUPnPClassContainer*)this->mMediaDatabase->getObjectByID(RefID);
+ if(!ParentObject){
+ ERROR("No such parent with ID '%s' found.",*RefID);
+ return -1;
+ }
+ }
+ Object->setParent(ParentObject);
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_CLASS)){
+ if(Object->setClass(Value)){
+ ERROR("Error while setting class");
+ return -1;
+ }
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_TITLE)){
+ if(Object->setTitle(Value)){
+ ERROR("Error while setting title");
+ return -1;
+ }
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_RESTRICTED)){
+ if(Object->setRestricted(atoi(Value)==1?true:false)){
+ ERROR("Error while setting restriction");
+ return -1;
+ }
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_CREATOR)){
+ if(Object->setCreator(Value)){
+ ERROR("Error while setting creator");
+ return -1;
+ }
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_WRITESTATUS)){
+ if(Object->setWriteStatus(atoi(Value))){
+ ERROR("Error while setting write status");
+ return -1;
+ }
+ }
+ }
+ cUPnPResources::getInstance()->getResourcesOfObject(Object);
+ return 0;
+}
+
+ /**********************************************\
+ * *
+ * Item mediator *
+ * *
+ \**********************************************/
+
+cUPnPItemMediator::cUPnPItemMediator(cMediaDatabase* MediaDatabase) :
+ cUPnPObjectMediator(MediaDatabase){}
+
+int cUPnPItemMediator::objectToDatabase(cUPnPClassObject* Object){
+ if(cUPnPObjectMediator::objectToDatabase(Object)) return -1;
+ cString Format = "INSERT OR REPLACE INTO %s (%s) VALUES (%s);";
+ cString Statement=NULL, Columns=NULL, Values=NULL;
+ char *Value=NULL;
+ cString Properties[] = {
+ SQLITE_COL_OBJECTID,
+ SQLITE_COL_REFERENCEID,
+ NULL
+ };
+ for(cString* Property = Properties; *(*Property); Property++){
+ Columns = cString::sprintf("%s%s%s", *Columns?*Columns:"", *Columns?",":"", *(*Property));
+ if(!Object->getProperty(*Property, &Value)){
+ ERROR("No such property '%s' in object with ID '%s'",*(*Property),*Object->getID());
+ return -1;
+ }
+ char *escapedValue;
+ escapeSQLite(Value, &escapedValue);
+ Values = cString::sprintf("%s%s'%s'", *Values?*Values:"", *Values?",":"", escapedValue?escapedValue:"NULL");
+
+ }
+ Statement = cString::sprintf(Format, SQLITE_TABLE_ITEMS, *Columns, *Values);
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+ return 0;
+}
+
+int cUPnPItemMediator::databaseToObject(cUPnPClassObject* Object, cUPnPObjectID ID){
+ if(cUPnPObjectMediator::databaseToObject(Object,ID)){
+ ERROR("Error while loading object");
+ return -1;
+ }
+ cUPnPClassItem* Item = (cUPnPClassItem*) Object;
+ cString Format = "SELECT * FROM %s WHERE %s=%s";
+ cString Statement = NULL, Column = NULL, Value = NULL;
+ cRows* Rows; cRow* Row;
+ Statement = cString::sprintf(Format, SQLITE_TABLE_ITEMS, SQLITE_COL_OBJECTID, *ID);
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+ Rows = this->mDatabase->getResultRows();
+ if(!Rows->fetchRow(&Row)){
+ MESSAGE("No item properties found");
+ return 0;
+ }
+ while(Row->fetchColumn(&Column, &Value)){
+ if(!strcasecmp(Column, SQLITE_COL_REFERENCEID)){
+ cUPnPObjectID RefID = atoi(Value);
+ cUPnPClassItem* RefObject;
+ if(RefID == -1){
+ RefObject = NULL;
+ }
+ else {
+ RefObject = (cUPnPClassItem*)this->mMediaDatabase->getObjectByID(RefID);
+ if(!RefObject){
+ ERROR("No such reference item with ID '%s' found.",*RefID);
+ return -1;
+ }
+ }
+ Item->setReference(RefObject);
+ }
+ }
+ return 0;
+}
+
+cUPnPClassItem* cUPnPItemMediator::getObject(cUPnPObjectID ID){
+ MESSAGE("Getting Item with ID '%s'",*ID);
+ cUPnPClassItem* Object = new cUPnPClassItem;
+ if(this->databaseToObject(Object, ID)) return NULL;
+ return Object;
+}
+
+cUPnPClassItem* cUPnPItemMediator::createObject(const char* Title, bool Restricted){
+ MESSAGE("Creating Item '%s'",Title);
+ cUPnPClassItem* Object = new cUPnPClassItem;
+ if(this->initializeObject(Object, UPNP_CLASS_ITEM, Title, Restricted)) return NULL;
+ return Object;
+}
+
+ /**********************************************\
+ * *
+ * Container mediator *
+ * *
+ \**********************************************/
+
+cUPnPContainerMediator::cUPnPContainerMediator(cMediaDatabase* MediaDatabase) :
+ cUPnPObjectMediator(MediaDatabase){}
+
+int cUPnPContainerMediator::objectToDatabase(cUPnPClassObject* Object){
+ if(cUPnPObjectMediator::objectToDatabase(Object)) return -1;
+ cUPnPClassContainer* Container = (cUPnPClassContainer*)Object;
+ cString Format = "INSERT OR REPLACE INTO %s (%s) VALUES (%s);";
+ cString Statement=NULL, Columns=NULL, Values=NULL;
+ char *Value=NULL;
+ cString Properties[] = {
+ SQLITE_COL_OBJECTID,
+ SQLITE_COL_DLNA_CONTAINERTYPE,
+ SQLITE_COL_SEARCHABLE,
+ SQLITE_COL_CONTAINER_UID,
+ NULL
+ };
+ for(cString* Property = Properties; *(*Property); Property++){
+ Columns = cString::sprintf("%s%s%s", *Columns?*Columns:"", *Columns?",":"", *(*Property));
+ if(!Container->getProperty(*Property, &Value)){
+ ERROR("No such property '%s' in object with ID '%s'",*(*Property),*Container->getID());
+ return -1;
+ }
+ char *escapedValue;
+ escapeSQLite(Value, &escapedValue);
+ Values = cString::sprintf("%s%s'%s'", *Values?*Values:"", *Values?",":"", escapedValue?escapedValue:"NULL");
+ }
+ Statement = cString::sprintf(Format, SQLITE_TABLE_CONTAINERS, *Columns, *Values);
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+ for(unsigned int i=0; i<Container->getSearchClasses()->size(); i++){
+ cClass Class = Container->getSearchClasses()->at(i);
+ Columns = cString::sprintf("%s,%s,%s", SQLITE_COL_OBJECTID, SQLITE_COL_CLASS, SQLITE_COL_CLASSDERIVED);
+ Values = cString::sprintf("'%s','%s','%s'", *Container->getID(), *Class.ID, Class.includeDerived?"1":"0");
+ Statement = cString::sprintf(Format, SQLITE_TABLE_SEARCHCLASS, *Columns, *Values);
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+ }
+ // Create classes not necessary at the moment
+ return 0;
+}
+
+int cUPnPContainerMediator::databaseToObject(cUPnPClassObject* Object, cUPnPObjectID ID){
+ if(cUPnPObjectMediator::databaseToObject(Object,ID)){
+ ERROR("Error while loading object");
+ return -1;
+ }
+ cUPnPClassContainer* Container = (cUPnPClassContainer*)Object;
+ cString Format = "SELECT * FROM %s WHERE %s=%s";
+ cString Statement = NULL, Column = NULL, Value = NULL;
+ cRows* Rows; cRow* Row;
+ Statement = cString::sprintf(Format, SQLITE_TABLE_CONTAINERS, SQLITE_COL_OBJECTID, *ID);
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+ Rows = this->mDatabase->getResultRows();
+ if(!Rows->fetchRow(&Row)){
+ MESSAGE("No item properties found");
+ return 0;
+ }
+ while(Row->fetchColumn(&Column, &Value)){
+ if(!strcasecmp(Column, SQLITE_COL_DLNA_CONTAINERTYPE)){
+ if(Container->setContainerType(Value)){
+ ERROR("Error while setting container type");
+ return -1;
+ }
+ }
+ if(!strcasecmp(Column, SQLITE_COL_CONTAINER_UID)){
+ if(Container->setUpdateID((unsigned int)atoi(Value))){
+ ERROR("Error while setting update ID");
+ return -1;
+ }
+ }
+ if(!strcasecmp(Column, SQLITE_COL_SEARCHABLE)){
+ if(Container->setSearchable(atoi(Value)==1?true:false)){
+ ERROR("Error while setting searchable");
+ return -1;
+ }
+ }
+ }
+ Statement = cString::sprintf("SELECT %s FROM %s WHERE %s=%s", SQLITE_COL_OBJECTID,
+ SQLITE_TABLE_OBJECTS,
+ SQLITE_COL_PARENTID,
+ *ID);
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+ Rows = this->mDatabase->getResultRows();
+ while(Rows->fetchRow(&Row)){
+ while(Row->fetchColumn(&Column, &Value)){
+ if(!strcasecmp(Column, SQLITE_COL_OBJECTID)){
+ Container->addObject(this->mMediaDatabase->getObjectByID(atoi(Value)));
+ }
+ }
+ }
+ Statement = cString::sprintf(Format, SQLITE_TABLE_SEARCHCLASS, SQLITE_COL_OBJECTID, *ID);
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+ std::vector<cClass> SearchClasses;
+ Rows = this->mDatabase->getResultRows();
+ while(Rows->fetchRow(&Row)){
+ cClass Class;
+ while(Row->fetchColumn(&Column, &Value)){
+ if(!strcasecmp(Column, SQLITE_COL_CLASS)){
+ Class.ID = strdup0(*Value);
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_CLASSDERIVED)){
+ Class.includeDerived = atoi(Value)==1?"true":"false";
+ }
+ }
+ SearchClasses.push_back(Class);
+ }
+ if(Container->setSearchClasses(SearchClasses)){
+ ERROR("Error while setting search classes");
+ return -1;
+ }
+ return 0;
+}
+
+cUPnPClassContainer* cUPnPContainerMediator::createObject(const char* Title, bool Restricted){
+ MESSAGE("Creating Container '%s'",Title);
+ cUPnPClassContainer* Object = new cUPnPClassContainer;
+ if(this->initializeObject(Object, UPNP_CLASS_CONTAINER, Title, Restricted)) return NULL;
+ return Object;
+}
+
+cUPnPClassContainer* cUPnPContainerMediator::getObject(cUPnPObjectID ID){
+ MESSAGE("Getting Container with ID '%s'",*ID);
+ cUPnPClassContainer* Object = new cUPnPClassContainer;
+ if(this->databaseToObject(Object, ID)) return NULL;
+ return Object;
+}
+
+ /**********************************************\
+ * *
+ * Video item mediator *
+ * *
+ \**********************************************/
+
+cUPnPVideoItemMediator::cUPnPVideoItemMediator(cMediaDatabase* MediaDatabase) :
+ cUPnPItemMediator(MediaDatabase){}
+
+cUPnPClassVideoItem* cUPnPVideoItemMediator::createObject(const char* Title, bool Restricted){
+ MESSAGE("Creating Video item '%s'",Title);
+ cUPnPClassVideoItem* Object = new cUPnPClassVideoItem;
+ if(this->initializeObject(Object, UPNP_CLASS_VIDEO, Title, Restricted)) return NULL;
+ return Object;
+}
+
+cUPnPClassVideoItem* cUPnPVideoItemMediator::getObject(cUPnPObjectID ID){
+ MESSAGE("Getting Video item with ID '%s'",*ID);
+ cUPnPClassVideoItem* Object = new cUPnPClassVideoItem;
+ if(this->databaseToObject(Object, ID)) return NULL;
+ return Object;
+}
+
+int cUPnPVideoItemMediator::objectToDatabase(cUPnPClassObject* Object){
+ if(cUPnPItemMediator::objectToDatabase(Object)) return -1;
+ cUPnPClassVideoItem* VideoItem = (cUPnPClassVideoItem*)Object;
+ cString Format = "INSERT OR REPLACE INTO %s (%s) VALUES (%s);";
+ cString Statement=NULL, Columns=NULL, Values=NULL;
+ char *Value=NULL;
+ cString Properties[] = {
+ SQLITE_COL_OBJECTID,
+ SQLITE_COL_GENRE,
+ SQLITE_COL_LONGDESCRIPTION,
+ SQLITE_COL_PRODUCER,
+ SQLITE_COL_RATING,
+ SQLITE_COL_ACTOR,
+ SQLITE_COL_DIRECTOR,
+ SQLITE_COL_DESCRIPTION,
+ SQLITE_COL_PUBLISHER,
+ SQLITE_COL_LANGUAGE,
+ SQLITE_COL_RELATION,
+ NULL
+ };
+ for(cString* Property = Properties; *(*Property); Property++){
+ Columns = cString::sprintf("%s%s%s", *Columns?*Columns:"", *Columns?",":"", *(*Property));
+ if(!VideoItem->getProperty(*Property, &Value)){
+ ERROR("No such property '%s' in object with ID '%s'",*(*Property),* VideoItem->getID());
+ return -1;
+ }
+ char *escapedValue;
+ escapeSQLite(Value, &escapedValue);
+ Values = cString::sprintf("%s%s'%s'", *Values?*Values:"", *Values?",":"", escapedValue?escapedValue:"NULL");
+
+ }
+ Statement = cString::sprintf(Format, SQLITE_TABLE_VIDEOITEMS, *Columns, *Values);
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+ return 0;
+}
+
+int cUPnPVideoItemMediator::databaseToObject(cUPnPClassObject* Object, cUPnPObjectID ID){
+ if(cUPnPItemMediator::databaseToObject(Object,ID)){
+ ERROR("Error while loading object");
+ return -1;
+ }
+ cUPnPClassVideoItem* VideoItem = (cUPnPClassVideoItem*)Object;
+ cString Format = "SELECT * FROM %s WHERE %s=%s";
+ cString Statement = NULL, Column = NULL, Value = NULL;
+ cRows* Rows; cRow* Row;
+ Statement = cString::sprintf(Format, SQLITE_TABLE_VIDEOITEMS, SQLITE_COL_OBJECTID, *ID);
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+ Rows = this->mDatabase->getResultRows();
+ if(!Rows->fetchRow(&Row)){
+ MESSAGE("No item properties found");
+ return 0;
+ }
+ while(Row->fetchColumn(&Column, &Value)){
+ if(!strcasecmp(Column, SQLITE_COL_GENRE)){
+ if(VideoItem->setGenre(Value)){
+ ERROR("Error while setting genre");
+ return -1;
+ }
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_LONGDESCRIPTION)){
+ if(VideoItem->setLongDescription(Value)){
+ ERROR("Error while setting long description");
+ return -1;
+ }
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_PRODUCER)){
+ if(VideoItem->setProducers(Value)){
+ ERROR("Error while setting producers");
+ return -1;
+ }
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_RATING)){
+ if(VideoItem->setRating(Value)){
+ ERROR("Error while setting rating");
+ return -1;
+ }
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_ACTOR)){
+ if(VideoItem->setActors(Value)){
+ ERROR("Error while setting actors");
+ return -1;
+ }
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_DIRECTOR)){
+ if(VideoItem->setDirectors(Value)){
+ ERROR("Error while setting directors");
+ return -1;
+ }
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_DESCRIPTION)){
+ if(VideoItem->setDescription(Value)){
+ ERROR("Error while setting description");
+ return -1;
+ }
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_PUBLISHER)){
+ if(VideoItem->setPublishers(Value)){
+ ERROR("Error while setting publishers");
+ return -1;
+ }
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_LANGUAGE)){
+ if(VideoItem->setLanguage(Value)){
+ ERROR("Error while setting language");
+ return -1;
+ }
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_RELATION)){
+ if(VideoItem->setRelations(Value)){
+ ERROR("Error while setting relations");
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+ /**********************************************\
+ * *
+ * Video broadcast item mediator *
+ * *
+ \**********************************************/
+
+cUPnPVideoBroadcastMediator::cUPnPVideoBroadcastMediator(cMediaDatabase* MediaDatabase) :
+ cUPnPVideoItemMediator(MediaDatabase){}
+
+cUPnPClassVideoBroadcast* cUPnPVideoBroadcastMediator::createObject(const char* Title, bool Restricted){
+ MESSAGE("Creating Video broadcast '%s'",Title);
+ cUPnPClassVideoBroadcast* Object = new cUPnPClassVideoBroadcast;
+ if(this->initializeObject(Object, UPNP_CLASS_VIDEOBC, Title, Restricted)) return NULL;
+ return Object;
+}
+
+cUPnPClassVideoBroadcast* cUPnPVideoBroadcastMediator::getObject(cUPnPObjectID ID){
+ MESSAGE("Getting Video broadcast with ID '%s'",*ID);
+ cUPnPClassVideoBroadcast* Object = new cUPnPClassVideoBroadcast;
+ if(this->databaseToObject(Object, ID)) return NULL;
+ return Object;
+}
+
+int cUPnPVideoBroadcastMediator::objectToDatabase(cUPnPClassObject* Object){
+ if(cUPnPVideoItemMediator::objectToDatabase(Object)) return -1;
+ cUPnPClassVideoBroadcast* VideoBroadcast = (cUPnPClassVideoBroadcast*)Object;
+ cString Format = "INSERT OR REPLACE INTO %s (%s) VALUES (%s);";
+ cString Statement=NULL, Columns=NULL, Values=NULL;
+ char *Value=NULL;
+ cString Properties[] = {
+ SQLITE_COL_OBJECTID,
+ SQLITE_COL_ICON,
+ SQLITE_COL_REGION,
+ SQLITE_COL_CHANNELNAME,
+ SQLITE_COL_CHANNELNR,
+ NULL
+ };
+ for(cString* Property = Properties; *(*Property); Property++){
+ Columns = cString::sprintf("%s%s%s", *Columns?*Columns:"", *Columns?",":"", *(*Property));
+ if(!VideoBroadcast->getProperty(*Property, &Value)){
+ ERROR("No such property '%s' in object with ID '%s'",*(*Property),* VideoBroadcast->getID());
+ return -1;
+ }
+ char *escapedValue;
+ escapeSQLite(Value, &escapedValue);
+ Values = cString::sprintf("%s%s'%s'", *Values?*Values:"", *Values?",":"", escapedValue?escapedValue:"NULL");
+
+ }
+ Statement = cString::sprintf(Format, SQLITE_TABLE_VIDEOBROADCASTS, *Columns, *Values);
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+ return 0;
+}
+
+int cUPnPVideoBroadcastMediator::databaseToObject(cUPnPClassObject* Object, cUPnPObjectID ID){
+ if(cUPnPVideoItemMediator::databaseToObject(Object,ID)){
+ ERROR("Error while loading object");
+ return -1;
+ }
+ cUPnPClassVideoBroadcast* VideoBroadcast = (cUPnPClassVideoBroadcast*)Object;
+ cString Format = "SELECT * FROM %s WHERE %s=%s";
+ cString Statement = NULL, Column = NULL, Value = NULL;
+ cRows* Rows; cRow* Row;
+ Statement = cString::sprintf(Format, SQLITE_TABLE_VIDEOBROADCASTS, SQLITE_COL_OBJECTID, *ID);
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+ Rows = this->mDatabase->getResultRows();
+ if(!Rows->fetchRow(&Row)){
+ MESSAGE("No item properties found");
+ return 0;
+ }
+ while(Row->fetchColumn(&Column, &Value)){
+ if(!strcasecmp(Column, SQLITE_COL_ICON)){
+ if(VideoBroadcast->setIcon(Value)){
+ ERROR("Error while setting icon");
+ return -1;
+ }
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_REGION)){
+ if(VideoBroadcast->setRegion(Value)){
+ ERROR("Error while setting region");
+ return -1;
+ }
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_CHANNELNR)){
+ if(VideoBroadcast->setChannelNr(atoi(Value))){
+ ERROR("Error while setting channel number");
+ return -1;
+ }
+ }
+ else if(!strcasecmp(Column, SQLITE_COL_CHANNELNAME)){
+ if(VideoBroadcast->setChannelName(Value)){
+ ERROR("Error while setting channel name");
+ return -1;
+ }
+ }
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/database/object.h b/database/object.h
new file mode 100644
index 0000000..9b62c54
--- /dev/null
+++ b/database/object.h
@@ -0,0 +1,397 @@
+/*
+ * File: object.h
+ * Author: savop
+ *
+ * Created on 11. September 2009, 20:39
+ */
+
+#ifndef _OBJECT_H
+#define _OBJECT_H
+
+#include "database.h"
+#include "../common.h"
+#include "../misc/util.h"
+#include <string.h>
+#include <vdr/tools.h>
+#include <map>
+#include <vector>
+#include <upnp/ixml.h>
+
+struct cUPnPObjectID {
+ int _ID;
+ cUPnPObjectID():_ID(-1){}
+ cUPnPObjectID(long ID){ _ID = (int)ID; }
+ cUPnPObjectID(int ID){ _ID = ID; }
+ cUPnPObjectID &operator=(long ID){ _ID = ID; return *this; }
+ cUPnPObjectID &operator=(int ID){ _ID = ID; return *this; }
+ cUPnPObjectID &operator=(const cUPnPObjectID& ID){ if(this != &ID){ _ID = ID._ID; } return *this; }
+ cUPnPObjectID &operator++(){ _ID++; return *this; }
+ cUPnPObjectID operator++(int){ cUPnPObjectID old = *this; _ID++; return old; }
+ cUPnPObjectID operator--(int){ cUPnPObjectID old = *this; _ID--; return old; }
+ cUPnPObjectID &operator--(){ _ID--; return *this; }
+ bool operator!=(long ID){ return _ID != ID; }
+ bool operator==(long ID){ return _ID == ID; }
+ bool operator!=(int ID){ return _ID != ID; }
+ bool operator==(int ID){ return _ID == ID; }
+ bool operator!=(const cUPnPObjectID& ID){ return *this == ID; }
+ bool operator==(const cUPnPObjectID& ID){ return *this == ID; }
+ operator unsigned int(){ return (unsigned int)_ID; }
+ operator int(){ return _ID; }
+ operator long(){ return (long)_ID; }
+ const char* operator*(){ char* buf; return asprintf(&buf,"%d",_ID)?buf:NULL; }
+};
+
+struct cClass {
+ cString ID;
+ bool includeDerived;
+ bool operator==(const cClass &cmp){ return (!strcasecmp(cmp.ID,ID) && includeDerived==cmp.includeDerived); }
+ bool operator!=(const cClass &cmp){ return !(*this==cmp); }
+};
+
+class cUPnPResource : public cListObject {
+ friend class cUPnPResourceMediator;
+ friend class cUPnPResources;
+private:
+ unsigned int mResourceID;
+ cUPnPObjectID mObjectID;
+ int mResourceType;
+ cString mResource;
+ cString mDuration;
+ cString mResolution;
+ cString mProtocolInfo;
+ cString mContentType;
+ cString mImportURI;
+ unsigned long mSize;
+ unsigned int mBitrate;
+ unsigned int mSampleFrequency;
+ unsigned int mBitsPerSample;
+ unsigned int mNrAudioChannels;
+ unsigned int mColorDepth;
+ cUPnPResource();
+public:
+ unsigned int getID() const { return this->mResourceID; }
+ const char* getResource() const { return this->mResource; }
+ const char* getDuration() const { return this->mDuration; }
+ const char* getResolution() const { return this->mResolution; }
+ const char* getProtocolInfo() const { return this->mProtocolInfo; }
+ const char* getContentType() const { return this->mContentType; }
+ const char* getImportURI() const { return this->mImportURI; }
+ int getResourceType() const { return this->mResourceType; }
+ unsigned long getSize() const { return this->mSize; }
+ off64_t getFileSize() const;
+ time_t getLastModification() const;
+ unsigned int getBitrate() const { return this->mBitrate; }
+ unsigned int getSampleFrequency() const { return this->mSampleFrequency; }
+ unsigned int getBitsPerSample() const { return this->mBitsPerSample; }
+ unsigned int getNrAudioChannels() const { return this->mNrAudioChannels; }
+ unsigned int getColorDepth() const { return this->mColorDepth; }
+};
+
+class cUPnPClassObject;
+class cUPnPObjectMediator;
+class cUPnPContainerMediator;
+class cUPnPClassContainer;
+
+class cUPnPObjects : public cList<cUPnPClassObject> {
+public:
+ cUPnPObjects();
+ virtual ~cUPnPObjects();
+ void SortBy(const char* Property, bool Descending = false);
+};
+
+class cUPnPClassObject : public cListObject {
+ friend class cMediaDatabase;
+ friend class cUPnPObjectMediator;
+ friend class cUPnPClassContainer;
+private:
+ bool mDeleted; // is this Objected marked as deleted
+protected:
+ time_t mLastModified;
+ cUPnPObjectID mID; // The object ID
+ cUPnPClassObject* mParent;
+ cString mClass; // Class (Who am I?)
+ cString mTitle; // Object title
+ cString mCreator; // Creator of this object
+ bool mRestricted; // Ability of changing metadata?
+ int mWriteStatus; // Ability of writing resources?
+ cList<cUPnPResource>* mResources; // The resources of this object
+ cHash<cUPnPResource>* mResourcesID;
+ IXML_Document* mDIDLFragment;
+ cString mSortCriteria;
+ bool mSortDescending;
+ cUPnPClassObject();
+ int setID(cUPnPObjectID ID);
+ int setParent(cUPnPClassContainer* Parent);
+ int setClass(const char* Class);
+ void setModified(void){ this->mLastModified = time(NULL); }
+public:
+ time_t modified() const { return this->mLastModified; }
+ virtual ~cUPnPClassObject();
+ virtual int Compare(const cListObject& ListObject) const;
+ virtual cStringList* getPropertyList();
+ virtual bool getProperty(const char* Property, char** Value) const ;
+ virtual bool setProperty(const char* Property, const char* Value);
+ virtual cUPnPClassContainer* getContainer(){ return NULL; }
+ virtual IXML_Node* createDIDLFragment(IXML_Document* Document, cStringList* Filter) = 0;
+ bool isContainer(){ return this->getContainer()==NULL?false:true; }
+ void setSortCriteria(const char* Property, bool Descending = false);
+ void clearSortCriteria();
+ /******* Setter *******/
+ int setTitle(const char* Title);
+ int setCreator(const char* Creator);
+ int setRestricted(bool Restricted);
+ int setWriteStatus(int Status);
+ int setResources(cList<cUPnPResource>* Resources);
+ int addResource(cUPnPResource* Resource);
+ int removeResource(cUPnPResource* Resource);
+ /******* Getter *******/
+ cUPnPObjectID getID() const { return this->mID; }
+ cUPnPObjectID getParentID() const { return this->mParent?this->mParent->getID():cUPnPObjectID(-1); }
+ cUPnPClassContainer* getParent() const { return (cUPnPClassContainer*)this->mParent; }
+ const char* getTitle() const { return this->mTitle; }
+ const char* getClass() const { return this->mClass; }
+ const char* getCreator() const { return this->mCreator; }
+ bool isRestricted() const { return this->mRestricted; }
+ int getWriteStatus() const { return this->mWriteStatus; }
+ cUPnPResource* getResource(unsigned int ResourceID) const { return this->mResourcesID->Get(ResourceID); }
+ cList<cUPnPResource>* getResources() const { return this->mResources; }
+};
+
+class cUPnPClassItem : public cUPnPClassObject {
+ friend class cMediaDatabase;
+ friend class cUPnPObjectMediator;
+ friend class cUPnPItemMediator;
+protected:
+// cUPnPObjectID mReferenceID;
+ cUPnPClassItem* mReference;
+ cUPnPClassItem();
+public:
+ virtual ~cUPnPClassItem(){};
+ virtual cStringList* getPropertyList();
+ virtual IXML_Node* createDIDLFragment(IXML_Document* Document, cStringList* Filter);
+ virtual bool setProperty(const char* Property, const char* Value);
+ virtual bool getProperty(const char* Property, char** Value) const;
+ /******** Setter ********/
+ int setReference(cUPnPClassItem* Reference);
+ /******** Getter ********/
+ cUPnPClassItem* getReference() const { return this->mReference; }
+ cUPnPObjectID getReferenceID() const { return this->mReference?this->mReference->getID():cUPnPObjectID(-1); }
+};
+
+typedef std::vector<cClass> tClassVector;
+
+class cUPnPClassContainer : public cUPnPClassObject {
+ friend class cMediaDatabase;
+ friend class cUPnPObjectMediator;
+ friend class cUPnPContainerMediator;
+protected:
+ cString mContainerType;
+ tClassVector mSearchClasses;
+ tClassVector mCreateClasses;
+ bool mSearchable;
+ unsigned int mUpdateID;
+ cUPnPObjects* mChildren;
+ cHash<cUPnPClassObject>* mChildrenID;
+ void update();
+ int setUpdateID(unsigned int UID);
+ cUPnPClassContainer();
+public:
+ virtual ~cUPnPClassContainer();
+ virtual cStringList* getPropertyList();
+ virtual IXML_Node* createDIDLFragment(IXML_Document* Document, cStringList* Filter);
+ virtual bool setProperty(const char* Property, const char* Value);
+ virtual bool getProperty(const char* Property, char** Value) const;
+ virtual cUPnPClassContainer* getContainer(){ return this; }
+ void addObject(cUPnPClassObject* Object);
+ void removeObject(cUPnPClassObject* Object);
+ cUPnPClassObject* getObject(cUPnPObjectID ID) const;
+ cUPnPObjects* getObjectList() const { return this->mChildren; }
+ int addSearchClass(cClass SearchClass);
+ int delSearchClass(cClass SearchClass);
+ int addCreateClass(cClass CreateClass);
+ int delCreateClass(cClass CreateClass);
+ /******** Setter ********/
+ int setContainerType(const char* Type);
+ int setSearchClasses(std::vector<cClass> SearchClasses);
+ int setCreateClasses(std::vector<cClass> CreateClasses);
+ int setSearchable(bool Searchable);
+ /******** Getter ********/
+ const char* getContainerType() const { return this->mContainerType; }
+ const std::vector<cClass>* getSearchClasses() const { return &(this->mSearchClasses); }
+ const std::vector<cClass>* getCreateClasses() const { return &(this->mCreateClasses); }
+ bool isSearchable() const { return this->mSearchable; }
+ unsigned int getChildCount() const { return this->mChildren->Count(); }
+ unsigned int getUpdateID() const { return this->mUpdateID; }
+ bool isUpdated();
+};
+
+class cUPnPClassVideoItem : public cUPnPClassItem {
+ friend class cMediaDatabase;
+ friend class cUPnPObjectMediator;
+ friend class cUPnPVideoItemMediator;
+protected:
+ cString mGenre; // Genre
+ cString mDescription; // Description
+ cString mLongDescription; // a longer description
+ cString mPublishers; // CSV of Publishers
+ cString mLanguage; // RFC 1766 Language code
+ cString mRelations; // Relation to other contents
+ cString mProducers; // CSV of Producers
+ cString mRating; // Rating (for parential control)
+ cString mActors; // CSV of Actors
+ cString mDirectors; // CSV of Directors
+ cUPnPClassVideoItem();
+public:
+ virtual ~cUPnPClassVideoItem();
+ //virtual cString createDIDLFragment(cStringList* Filter);
+ virtual cStringList* getPropertyList();
+ virtual bool setProperty(const char* Property, const char* Value);
+ virtual bool getProperty(const char* Property, char** Value) const;
+ /******** Setter ********/
+ int setLongDescription(const char* LongDescription);
+ int setDescription(const char* Description);
+ int setPublishers(const char* Publishers);
+ int setGenre(const char* Genre);
+ int setLanguage(const char* Language);
+ int setRelations(const char* Relations);
+ int setDirectors(const char* Directors);
+ int setActors(const char* Actors);
+ int setProducers(const char* Producers);
+ int setRating(const char* Rating);
+ /******** Getter ********/
+ const char* getGenre() const { return this->mGenre; }
+ const char* getLongDescription() const { return this->mLongDescription; }
+ const char* getDescription() const { return this->mDescription; }
+ const char* getPublishers() const { return this->mPublishers; }
+ const char* getLanguage() const { return this->mLanguage; }
+ const char* getRelations() const { return this->mRelations; }
+ const char* getActors() const { return this->mActors; }
+ const char* getProducers() const { return this->mProducers; }
+ const char* getDirectors() const { return this->mDirectors; }
+ const char* getRating() const { return this->mRating; }
+};
+
+class cUPnPClassVideoBroadcast : public cUPnPClassVideoItem {
+ friend class cMediaDatabase;
+ friend class cUPnPObjectMediator;
+ friend class cUPnPVideoBroadcastMediator;
+protected:
+ cString mIcon;
+ cString mRegion;
+ int mChannelNr;
+ cString mChannelName;
+ cUPnPClassVideoBroadcast();
+public:
+ virtual ~cUPnPClassVideoBroadcast();
+ //virtual cString createDIDLFragment(cStringList* Filter);
+ virtual cStringList* getPropertyList();
+ virtual bool setProperty(const char* Property, const char* Value);
+ virtual bool getProperty(const char* Property, char** Value) const;
+ /******** Setter ********/
+ int setIcon(const char* IconURI);
+ int setRegion(const char* Region);
+ int setChannelNr(int ChannelNr);
+ int setChannelName(const char* ChannelName);
+ /******** Getter ********/
+ const char* getIcon() const { return this->mIcon; }
+ const char* getRegion() const { return this->mRegion; }
+ int getChannelNr() const { return this->mChannelNr; }
+ const char* getChannelName() const { return this->mChannelName; }
+};
+
+class cMediatorInterface {
+public:
+ virtual ~cMediatorInterface(){};
+ virtual cUPnPClassObject* createObject(const char* Title, bool Restricted) = 0;
+ virtual cUPnPClassObject* getObject(cUPnPObjectID ID) = 0;
+ virtual int saveObject(cUPnPClassObject* Object) = 0;
+ virtual int deleteObject(cUPnPClassObject* Object) = 0;
+ virtual int clearObject(cUPnPClassObject* Object) = 0;
+};
+
+typedef std::map<const char*, cMediatorInterface*, strCmp> tMediatorMap;
+
+class cUPnPObjectFactory {
+private:
+ static cUPnPObjectFactory* mInstance;
+ cSQLiteDatabase* mDatabase;
+ tMediatorMap mMediators;
+ cMediatorInterface* findMediatorByID(cUPnPObjectID ID);
+ cMediatorInterface* findMediatorByClass(const char* Class);
+ cUPnPObjectFactory();
+public:
+ static cUPnPObjectFactory* getInstance();
+ void registerMediator(const char* UPnPClass, cMediatorInterface* Mediator);
+ void unregisterMediator(const char* UPnPClass, bool freeMediator=true);
+ cUPnPClassObject* createObject(const char* UPnPClass, const char* Title, bool Restricted=true);
+ cUPnPClassObject* getObject(cUPnPObjectID ID);
+ int saveObject(cUPnPClassObject* Object);
+ int deleteObject(cUPnPClassObject* Object);
+ int clearObject(cUPnPClassObject* Object);
+};
+
+class cMediaDatabase;
+
+class cUPnPObjectMediator : public cMediatorInterface {
+protected:
+ cSQLiteDatabase* mDatabase;
+ cMediaDatabase* mMediaDatabase;
+ cUPnPObjectMediator(cMediaDatabase* MediaDatabase);
+ virtual int initializeObject(cUPnPClassObject* Object, const char* Class, const char* Title, bool Restricted);
+ virtual int objectToDatabase(cUPnPClassObject* Object);
+ virtual int databaseToObject(cUPnPClassObject* Object, cUPnPObjectID ID);
+public:
+ virtual ~cUPnPObjectMediator();
+ virtual cUPnPClassObject* createObject(const char* Title, bool Restricted);
+ virtual cUPnPClassObject* getObject(cUPnPObjectID);
+ virtual int saveObject(cUPnPClassObject* Object);
+ virtual int deleteObject(cUPnPClassObject* Object);
+ virtual int clearObject(cUPnPClassObject* Object);
+};
+
+class cUPnPItemMediator : public cUPnPObjectMediator {
+protected:
+ virtual int objectToDatabase(cUPnPClassObject* Object);
+ virtual int databaseToObject(cUPnPClassObject* Object, cUPnPObjectID ID);
+public:
+ cUPnPItemMediator(cMediaDatabase* MediaDatabase);
+ virtual ~cUPnPItemMediator(){};
+ virtual cUPnPClassItem* createObject(const char* Title, bool Restricted);
+ virtual cUPnPClassItem* getObject(cUPnPObjectID ID);
+};
+
+class cUPnPVideoItemMediator : public cUPnPItemMediator {
+protected:
+ virtual int objectToDatabase(cUPnPClassObject* Object);
+ virtual int databaseToObject(cUPnPClassObject* Object, cUPnPObjectID ID);
+public:
+ cUPnPVideoItemMediator(cMediaDatabase* MediaDatabase);
+ virtual ~cUPnPVideoItemMediator(){};
+ virtual cUPnPClassVideoItem* createObject(const char* Title, bool Restricted);
+ virtual cUPnPClassVideoItem* getObject(cUPnPObjectID ID);
+};
+
+class cUPnPVideoBroadcastMediator : public cUPnPVideoItemMediator {
+protected:
+ virtual int objectToDatabase(cUPnPClassObject* Object);
+ virtual int databaseToObject(cUPnPClassObject* Object, cUPnPObjectID ID);
+public:
+ cUPnPVideoBroadcastMediator(cMediaDatabase* MediaDatabase);
+ virtual ~cUPnPVideoBroadcastMediator(){};
+ virtual cUPnPClassVideoBroadcast* createObject(const char* Title, bool Restricted);
+ virtual cUPnPClassVideoBroadcast* getObject(cUPnPObjectID ID);
+};
+
+class cUPnPContainerMediator : public cUPnPObjectMediator {
+protected:
+ virtual int objectToDatabase(cUPnPClassObject* Object);
+ virtual int databaseToObject(cUPnPClassObject* Object, cUPnPObjectID ID);
+public:
+ cUPnPContainerMediator(cMediaDatabase* MediaDatabase);
+ virtual ~cUPnPContainerMediator(){};
+ virtual cUPnPClassContainer* createObject(const char* Title, bool Restricted);
+ virtual cUPnPClassContainer* getObject(cUPnPObjectID ID);
+};
+
+#endif /* _OBJECT_H */
+
diff --git a/database/resources.cpp b/database/resources.cpp
new file mode 100644
index 0000000..f681a54
--- /dev/null
+++ b/database/resources.cpp
@@ -0,0 +1,282 @@
+/*
+ * File: resources.cpp
+ * Author: savop
+ *
+ * Created on 30. September 2009, 15:17
+ */
+
+#include <string.h>
+#include <vdr/channels.h>
+#include "../upnpcomponents/dlna.h"
+#include <vdr/tools.h>
+#include "resources.h"
+
+cUPnPResources* cUPnPResources::mInstance = NULL;
+
+cUPnPResources::cUPnPResources(){
+ this->mResources = new cHash<cUPnPResource>;
+ this->mMediator = new cUPnPResourceMediator;
+ this->mDatabase = cSQLiteDatabase::getInstance();
+}
+
+cUPnPResources::~cUPnPResources(){
+ delete this->mResources;
+ delete this->mMediator;
+}
+
+cUPnPResources* cUPnPResources::getInstance(){
+ if(!cUPnPResources::mInstance)
+ cUPnPResources::mInstance = new cUPnPResources();
+ if(cUPnPResources::mInstance) return cUPnPResources::mInstance;
+ else return NULL;
+}
+
+int cUPnPResources::loadResources(){
+ cString Statement = cString::sprintf("SELECT %s FROM %s",
+ SQLITE_COL_RESOURCEID,
+ SQLITE_TABLE_RESOURCES
+ );
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+ cRows* Rows = this->mDatabase->getResultRows(); cRow* Row;
+ cString Column = NULL, Value = NULL;
+ while(Rows->fetchRow(&Row)){
+ while(Row->fetchColumn(&Column, &Value)){
+ if(!strcasecmp(Column, SQLITE_COL_RESOURCEID)){
+ unsigned int ResourceID = (unsigned int)atoi(Value);
+ this->getResource(ResourceID);
+ }
+ }
+ }
+ return 0;
+}
+
+int cUPnPResources::getResourcesOfObject(cUPnPClassObject* Object){
+ cString Statement = cString::sprintf("SELECT %s FROM %s WHERE %s='%s'",
+ SQLITE_COL_RESOURCEID,
+ SQLITE_TABLE_RESOURCES,
+ SQLITE_COL_OBJECTID,
+ *Object->getID()
+ );
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+ cRows* Rows = this->mDatabase->getResultRows(); cRow* Row;
+ cString Column = NULL, Value = NULL;
+ while(Rows->fetchRow(&Row)){
+ while(Row->fetchColumn(&Column, &Value)){
+ if(!strcasecmp(Column, SQLITE_COL_RESOURCEID)){
+ unsigned int ResourceID = (unsigned int)atoi(Value);
+ Object->addResource(this->getResource(ResourceID));
+ }
+ }
+ }
+ return 0;
+}
+
+cUPnPResource* cUPnPResources::getResource(unsigned int ResourceID){
+ cUPnPResource* Resource;
+ if((Resource = this->mResources->Get(ResourceID))){
+ MESSAGE("Found cached resource");
+ return Resource;
+ }
+ else if((Resource = this->mMediator->getResource(ResourceID))){
+ MESSAGE("Found resource in database");
+ this->mResources->Add(Resource, ResourceID);
+ return Resource;
+ }
+ else {
+ ERROR("No such resource with ID '%d'", ResourceID);
+ return NULL;
+ }
+}
+
+int cUPnPResources::createFromChannel(cUPnPClassVideoBroadcast* Object, cChannel* Channel){
+ if(!Object || !Channel){
+ ERROR("Invalid input arguments");
+ return -1;
+ }
+
+ DLNAProfile* Profile = cDlna::getInstance()->getProfileOfChannel(Channel);
+ const char* ProtocolInfo = cDlna::getInstance()->getProtocolInfo(Profile);
+
+ MESSAGE("Protocol info: %s", ProtocolInfo);
+
+ // Adapted from streamdev
+ int index = 0;
+ for(int i=0; Channel->Apid(i)!=0; i++, index++){
+ MESSAGE("Analog channel %d", i);
+ cString ResourceFile = cString::sprintf("%s:%d", *Channel->GetChannelID().ToString(), index);
+ cUPnPResource* Resource = this->mMediator->newResource(Object, UPNP_RESOURCE_CHANNEL,ResourceFile, Profile->mime, ProtocolInfo);
+ Resource->mBitrate = 0;
+ Resource->mBitsPerSample = 0;
+ Resource->mColorDepth = 0;
+ Resource->mDuration = NULL;
+ Resource->mImportURI = NULL;
+ Resource->mResolution = NULL;
+ Resource->mSampleFrequency = 0;
+ Resource->mSize = 0;
+ Resource->mNrAudioChannels = 0;
+ Object->addResource(Resource);
+ this->mMediator->saveResource(Resource);
+ this->mResources->Add(Resource, Resource->getID());
+ }
+ for(int i=0; Channel->Dpid(i)!=0; i++, index++){
+ MESSAGE("Digital channel %d", i);
+ cString ResourceFile = cString::sprintf("%s:%d", *Channel->GetChannelID().ToString(), index);
+ cUPnPResource* Resource = this->mMediator->newResource(Object, UPNP_RESOURCE_CHANNEL,ResourceFile, Profile->mime, ProtocolInfo);
+ Resource->mBitrate = 0;
+ Resource->mBitsPerSample = 0;
+ Resource->mColorDepth = 0;
+ Resource->mDuration = NULL;
+ Resource->mImportURI = NULL;
+ Resource->mResolution = NULL;
+ Resource->mSampleFrequency = 0;
+ Resource->mSize = 0;
+ Object->addResource(Resource);
+ this->mMediator->saveResource(Resource);
+ this->mResources->Add(Resource, Resource->getID());
+ }
+
+ return 0;
+}
+
+cUPnPResourceMediator::cUPnPResourceMediator(){
+ this->mDatabase = cSQLiteDatabase::getInstance();
+}
+
+cUPnPResourceMediator::~cUPnPResourceMediator(){}
+
+cUPnPResource* cUPnPResourceMediator::getResource(unsigned int ResourceID){
+ cUPnPResource* Resource = new cUPnPResource;
+ Resource->mResourceID = ResourceID;
+ cString Statement = cString::sprintf("SELECT * FROM %s WHERE %s=%d",
+ SQLITE_TABLE_RESOURCES,
+ SQLITE_COL_RESOURCEID,
+ ResourceID
+ );
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return NULL;
+ }
+ cRows* Rows = this->mDatabase->getResultRows(); cRow* Row;
+ if(!Rows->fetchRow(&Row)){
+ ERROR("No such resource found");
+ return NULL;
+ }
+ cString Column = NULL, Value = NULL;
+ while(Row->fetchColumn(&Column, &Value)){
+ if(!strcasecmp(SQLITE_COL_OBJECTID, Column)){
+ Resource->mObjectID = atoi(Value);
+ }
+ else if(!strcasecmp(SQLITE_COL_PROTOCOLINFO, Column)){
+ Resource->mProtocolInfo = Value;
+ }
+ else if(!strcasecmp(SQLITE_COL_RESOURCE, Column)){
+ Resource->mResource = Value;
+ }
+ else if(!strcasecmp(SQLITE_COL_SIZE, Column)){
+ Resource->mSize = atol(Value);
+ }
+ else if(!strcasecmp(SQLITE_COL_DURATION, Column)){
+ Resource->mDuration = Value;
+ }
+ else if(!strcasecmp(SQLITE_COL_BITRATE, Column)){
+ Resource->mBitrate = atoi(Value);
+ }
+ else if(!strcasecmp(SQLITE_COL_SAMPLEFREQUENCE, Column)){
+ Resource->mSampleFrequency = atoi(Value);
+ }
+ else if(!strcasecmp(SQLITE_COL_BITSPERSAMPLE, Column)){
+ Resource->mBitsPerSample = atoi(Value);
+ }
+ else if(!strcasecmp(SQLITE_COL_NOAUDIOCHANNELS, Column)){
+ Resource->mNrAudioChannels = atoi(Value);
+ }
+ else if(!strcasecmp(SQLITE_COL_COLORDEPTH, Column)){
+ Resource->mColorDepth = atoi(Value);
+ }
+ else if(!strcasecmp(SQLITE_COL_RESOLUTION, Column)){
+ Resource->mResolution = Value;
+ }
+ else if(!strcasecmp(SQLITE_COL_CONTENTTYPE, Column)){
+ Resource->mContentType = Value;
+ }
+ else if(!strcasecmp(SQLITE_COL_RESOURCETYPE, Column)){
+ Resource->mResourceType = atoi(Value);
+ }
+ }
+ return Resource;
+}
+
+int cUPnPResourceMediator::saveResource(cUPnPResource* Resource){
+ cString Format = "UPDATE %s SET %s WHERE %s=%d";
+ cString Sets = cString::sprintf("%s='%s',"
+ "%s='%s',"
+ "%s='%s',"
+ "%s=%ld,"
+ "%s='%s',"
+ "%s=%d,"
+ "%s=%d,"
+ "%s=%d,"
+ "%s=%d,"
+ "%s=%d,"
+ "%s='%s',"
+ "%s='%s',"
+ "%s=%d",
+ SQLITE_COL_OBJECTID, *Resource->mObjectID?*Resource->mObjectID:"NULL",
+ SQLITE_COL_PROTOCOLINFO, *Resource->mProtocolInfo?*Resource->mProtocolInfo:"NULL",
+ SQLITE_COL_RESOURCE, *Resource->mResource?*Resource->mResource:"NULL",
+ SQLITE_COL_SIZE, Resource->mSize,
+ SQLITE_COL_DURATION, *Resource->mDuration?*Resource->mDuration:"NULL",
+ SQLITE_COL_BITRATE, Resource->mBitrate,
+ SQLITE_COL_SAMPLEFREQUENCE, Resource->mSampleFrequency,
+ SQLITE_COL_BITSPERSAMPLE, Resource->mBitsPerSample,
+ SQLITE_COL_NOAUDIOCHANNELS, Resource->mNrAudioChannels,
+ SQLITE_COL_COLORDEPTH, Resource->mColorDepth,
+ SQLITE_COL_RESOLUTION, *Resource->mResolution?*Resource->mResolution:"NULL",
+ SQLITE_COL_CONTENTTYPE, *Resource->mContentType,
+ SQLITE_COL_RESOURCETYPE, Resource->mResourceType
+ );
+
+ cString Statement = cString::sprintf(Format, SQLITE_TABLE_RESOURCES, *Sets, SQLITE_COL_RESOURCEID, Resource->mResourceID);
+
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+
+ return 0;
+}
+
+cUPnPResource* cUPnPResourceMediator::newResource(cUPnPClassObject* Object, int ResourceType, cString ResourceFile, cString ContentType, cString ProtocolInfo){
+ cUPnPResource* Resource = new cUPnPResource;
+ cString Statement = cString::sprintf("INSERT INTO %s (%s,%s,%s,%s,%s) VALUES ('%s','%s','%s','%s','%d')",
+ SQLITE_TABLE_RESOURCES,
+ SQLITE_COL_OBJECTID,
+ SQLITE_COL_RESOURCE,
+ SQLITE_COL_PROTOCOLINFO,
+ SQLITE_COL_CONTENTTYPE,
+ SQLITE_COL_RESOURCETYPE,
+ *Object->getID(),
+ *ResourceFile,
+ *ProtocolInfo,
+ *ContentType,
+ ResourceType
+ );
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return NULL;
+ }
+ Resource->mResourceID = (unsigned int)this->mDatabase->getLastInsertRowID();
+ Resource->mObjectID = Object->getID();
+ Resource->mResource = ResourceFile;
+ Resource->mProtocolInfo = ProtocolInfo;
+ Resource->mContentType = ContentType;
+ Resource->mResourceType = ResourceType;
+
+ return Resource;
+} \ No newline at end of file
diff --git a/database/resources.h b/database/resources.h
new file mode 100644
index 0000000..46ec0d5
--- /dev/null
+++ b/database/resources.h
@@ -0,0 +1,51 @@
+/*
+ * File: resources.h
+ * Author: savop
+ *
+ * Created on 30. September 2009, 15:17
+ */
+
+#ifndef _RESOURCES_H
+#define _RESOURCES_H
+
+#include "database.h"
+#include "object.h"
+#include <vdr/channels.h>
+#include <vdr/recording.h>
+
+class cUPnPResourceMediator;
+class cMediaDatabase;
+
+class cUPnPResources {
+private:
+ cHash<cUPnPResource>* mResources;
+ static cUPnPResources* mInstance;
+ cUPnPResourceMediator* mMediator;
+ cSQLiteDatabase* mDatabase;
+ cUPnPResources();
+public:
+ int getResourcesOfObject(cUPnPClassObject* Object);
+ int loadResources();
+ cUPnPResource* getResource(unsigned int ResourceID);
+ virtual ~cUPnPResources();
+ static cUPnPResources* getInstance();
+ int createFromChannel(cUPnPClassVideoBroadcast* Object, cChannel* Channel);
+ int createFromRecording(cUPnPClassVideoItem* Object, cRecording* Recording);
+ int createFromFile(cUPnPClassItem* Object, cString File);
+};
+
+class cUPnPResourceMediator {
+ friend class cUPnPResources;
+private:
+ cSQLiteDatabase* mDatabase;
+ cUPnPResourceMediator();
+ unsigned int getNextResourceID();
+public:
+ virtual ~cUPnPResourceMediator();
+ cUPnPResource* getResource(unsigned int ResourceID);
+ int saveResource(cUPnPResource* Resource);
+ cUPnPResource* newResource(cUPnPClassObject* Object, int ResourceType, cString ResourceFile, cString ContentType, cString ProtocolInfo);
+};
+
+#endif /* _RESOURCES_H */
+