diff options
author | Denis Loh <denis.loh@gmail.com> | 2009-10-24 14:24:17 +0200 |
---|---|---|
committer | Denis Loh <denis.loh@gmail.com> | 2009-10-24 14:24:17 +0200 |
commit | 1cf955a715830130b7add8c1183d65b0f442fd23 (patch) | |
tree | c9d03961e9f83b1100ef6010a4a53063f127aa5d /database | |
download | vdr-plugin-upnp-1cf955a715830130b7add8c1183d65b0f442fd23.tar.gz vdr-plugin-upnp-1cf955a715830130b7add8c1183d65b0f442fd23.tar.bz2 |
Initial commit
Diffstat (limited to 'database')
-rw-r--r-- | database/database.cpp | 274 | ||||
-rw-r--r-- | database/database.h | 834 | ||||
-rw-r--r-- | database/metadata.cpp | 389 | ||||
-rw-r--r-- | database/metadata.h | 60 | ||||
-rw-r--r-- | database/object.cpp | 1702 | ||||
-rw-r--r-- | database/object.h | 397 | ||||
-rw-r--r-- | database/resources.cpp | 282 | ||||
-rw-r--r-- | database/resources.h | 51 |
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 */ + |