summaryrefslogtreecommitdiff
path: root/database
diff options
context:
space:
mode:
authorDenis Loh <denis.loh@gmail.com>2009-10-25 22:44:37 +0100
committerDenis Loh <denis.loh@gmail.com>2009-10-25 22:44:37 +0100
commitc672d0018e24161434e63400f7dc23999d3f9c8a (patch)
treea16cea90b3f7dc45f47adc61568d854f79fe1513 /database
parentbde12e9e1467f185068b0ae139b2380377a47eb4 (diff)
downloadvdr-plugin-upnp-c672d0018e24161434e63400f7dc23999d3f9c8a.tar.gz
vdr-plugin-upnp-c672d0018e24161434e63400f7dc23999d3f9c8a.tar.bz2
Solved some issues with database filling and loading
Diffstat (limited to 'database')
-rw-r--r--database/database.cpp3
-rw-r--r--database/database.h26
-rw-r--r--database/metadata.cpp142
-rw-r--r--database/metadata.h2
-rw-r--r--database/object.cpp15
-rw-r--r--database/object.h23
6 files changed, 176 insertions, 35 deletions
diff --git a/database/database.cpp b/database/database.cpp
index 22c41cd..9c08c00 100644
--- a/database/database.cpp
+++ b/database/database.cpp
@@ -169,6 +169,7 @@ void cSQLiteDatabase::startTransaction(){
}
}
this->execStatement("BEGIN TRANSACTION");
+ MESSAGE("Start new transaction");
this->mActiveTransaction = true;
}
@@ -185,6 +186,7 @@ void cSQLiteDatabase::rollbackTransaction(){
int cSQLiteDatabase::initializeTables(){
int ret = 0;
this->startTransaction();
+ if(this->execStatement(SQLITE_CREATE_TABLE_ITEMFINDER)==-1) ret = -1;
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;
@@ -213,6 +215,7 @@ int cSQLiteDatabase::initializeTables(){
int cSQLiteDatabase::initializeTriggers(){
int ret = 0;
this->startTransaction();
+ if(this->execStatement(SQLITE_TRIGGER_D_OBJECTS_ITEMFINDER)==-1) ret = -1;
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;
diff --git a/database/database.h b/database/database.h
index 9df2c71..49c208d 100644
--- a/database/database.h
+++ b/database/database.h
@@ -12,8 +12,8 @@
#include <vdr/tools.h>
#include "../common.h"
-//#define SQLITE_PRINT_STATEMENTS
-//#define SQLITE_PRINT_FETCHES
+#define SQLITE_PRINT_STATEMENTS
+#define SQLITE_PRINT_FETCHES
#define SQLITE_CASCADE_DELETES
#define PK_OBJECTS TOSTRING(1)
@@ -40,6 +40,7 @@
#define SQLITE_TABLE_SEARCHCLASS "SearchClass"
#define SQLITE_TABLE_PRIMARY_KEYS "PrimaryKeys"
#define SQLITE_TABLE_SYSTEM "System"
+#define SQLITE_TABLE_ITEMFINDER "ItemFinder"
#define SQLITE_TYPE_TEXT "TEXT"
#define SQLITE_TYPE_INTEGER "INTEGER"
@@ -110,6 +111,7 @@
#define SQLITE_COL_ARTIST "Artist"
#define SQLITE_COL_DLNA_CONTAINERTYPE "DLNAContainer"
#define SQLITE_COL_CHILDCOUNT "ChildCount"
+#define SQLITE_COL_ITEMFINDER "ItemFastID"
#define SQLITE_UPNP_OBJECTID SQLITE_COL_OBJECTID " " SQLITE_TYPE_INTEGER " " SQLITE_NOT_NULL " " SQLITE_CONFLICT_CLAUSE " "\
SQLITE_UNIQUE " " SQLITE_CONFLICT_CLAUSE
@@ -294,6 +296,22 @@
/**********************************************\
* *
+ * Fast item finder *
+ * *
+ \**********************************************/
+
+#define SQLITE_CREATE_TABLE_ITEMFINDER "CREATE TABLE IF NOT EXISTS "\
+ SQLITE_TABLE_ITEMFINDER " "\
+ "("\
+ SQLITE_UPNP_OBJECTID ","\
+ SQLITE_COL_ITEMFINDER " " SQLITE_TYPE_TEXT " " SQLITE_NOT_NULL " " SQLITE_UNIQUE \
+ ");"
+
+#define SQLITE_TRIGGER_D_OBJECTS_ITEMFINDER SQLITE_DELETE_TRIGGER(SQLITE_TABLE_OBJECTS,\
+ SQLITE_TABLE_ITEMFINDER)
+
+ /**********************************************\
+ * *
* Objects *
* *
\**********************************************/
@@ -325,7 +343,7 @@
"((SELECT " SQLITE_COL_PARENTID " FROM " SQLITE_TABLE_OBJECTS " "\
"WHERE " SQLITE_COL_PARENTID "=-1) IS NOT NULL) "\
"AND "\
- "(NEW." SQLITE_COL_PARENTID "=-1) "\
+ "(NEW." SQLITE_COL_PARENTID "=-1)"\
") THEN "\
"RAISE(" SQLITE_TRANSACTION_TYPE ","\
"'INSERT on table " SQLITE_TABLE_OBJECTS " violates constraint. "\
@@ -344,7 +362,7 @@
"WHERE " SQLITE_COL_PARENTID "=-1 "\
"AND " SQLITE_COL_OBJECTID "!=NEW." SQLITE_COL_OBJECTID " ) IS NOT NULL) "\
"AND "\
- "(NEW." SQLITE_COL_PARENTID "=-1) "\
+ "(NEW." SQLITE_COL_PARENTID "=-1) AND (OLD." SQLITE_COL_PARENTID "!=-1) "\
") THEN "\
"RAISE(" SQLITE_TRANSACTION_TYPE ","\
"'UPDATE on table " SQLITE_TABLE_OBJECTS " violates constraint. "\
diff --git a/database/metadata.cpp b/database/metadata.cpp
index 75adf98..438d002 100644
--- a/database/metadata.cpp
+++ b/database/metadata.cpp
@@ -16,6 +16,7 @@
#include <vdr/channels.h>
#include <vdr/epg.h>
#include <upnp/upnp.h>
+#include <vdr/device.h>
#define KEY_SYSTEM_UPDATE_ID "SystemUpdateID"
@@ -51,6 +52,10 @@ bool cMediaDatabase::init(){
ERROR("Loading channels failed");
return false;
}
+// if(this->loadRecordings()){
+// ERROR("Loading records failed");
+// return false;
+// }
return true;
}
@@ -101,18 +106,65 @@ cUPnPObjectID cMediaDatabase::getNextObjectID(){
}
cRows* Rows = this->mDatabase->getResultRows();
cRow* Row;
+ int ret = 0;
if(!Rows->fetchRow(&Row)){
ERROR("No rows found");
+ ret = 0;
+ }
+ else {
+ while(Row->fetchColumn(&Column, &Value)){
+ if(!strcasecmp(Column, "Key")){
+ this->mLastInsertObjectID = atoi(Value);
+ ret = this->mLastInsertObjectID;
+ }
+ }
+ }
+ delete Rows;
+ return ret;
+}
+
+int cMediaDatabase::addFastFind(cUPnPClassObject* Object, const char* FastFind){
+ if(!Object || !FastFind){
+ MESSAGE("Invalid fast find parameters");
+ return -1;
+ }
+ cString Statement = cString::sprintf("INSERT OR REPLACE INTO %s (%s, %s) VALUES ('%s', '%s')",
+ SQLITE_TABLE_ITEMFINDER,
+ SQLITE_COL_OBJECTID,
+ SQLITE_COL_ITEMFINDER,
+ *Object->getID(),
+ FastFind
+ );
+ if(this->mDatabase->execStatement(Statement)){
+ ERROR("Error while executing statement");
+ return -1;
+ }
+ return 0;
+}
+
+cUPnPClassObject* cMediaDatabase::getObjectByFastFind(const char* FastFind){
+ if(!FastFind) return NULL;
+ MESSAGE("Try to find Object with identifier %s", FastFind);
+ cString Statement, Column, Value;
+ const char* Format = "SELECT %s FROM %s WHERE %s='%s'";
+ Statement = cString::sprintf(Format, SQLITE_COL_OBJECTID, SQLITE_TABLE_ITEMFINDER, SQLITE_COL_ITEMFINDER, FastFind);
+ 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 NULL;
+ }
while(Row->fetchColumn(&Column, &Value)){
- if(!strcasecmp(Column, "Key")){
- this->mLastInsertObjectID = atoi(Value);
- return this->mLastInsertObjectID;
+ if(!strcasecmp(Column, SQLITE_COL_OBJECTID)){
+ return this->getObjectByID(atoi(Value));
}
}
delete Rows;
- return 0;
+ return NULL;
}
cUPnPClassObject* cMediaDatabase::getObjectByID(cUPnPObjectID ID){
@@ -201,25 +253,38 @@ int cMediaDatabase::loadChannels(){
MESSAGE("Loading channels");
cUPnPClassContainer* TV = (cUPnPClassContainer*)this->getObjectByID(3);
if(TV){
- // Iterating channels
- cList<cUPnPClassObject>* List = TV->getObjectList();
bool noResource = false;
// TODO: Add to setup
// if an error occured while loading resources, add the channel anyway
bool addWithoutResources = false;
- for(cChannel* Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)){
+ cChannel* Channel = NULL;
+ for(int Index = 0; (Channel = Channels.Get(Index)); Index = Channels.GetNextNormal(Index)){
+ // Iterating the channels
+// for(Channel = Channels.First(); Channel; Channel = Channels.(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; }
- }
+
+ tChannelID ChannelID = Channel->GetChannelID();
+ MESSAGE("Determine if the channel %s is already listed", *ChannelID.ToString());
+ cUPnPClassVideoBroadcast* ChannelItem = NULL;
+
+ ChannelItem = (cUPnPClassVideoBroadcast*)this->getObjectByFastFind(ChannelID.ToString());
+
+ inList = (ChannelItem && TV->getObject(ChannelItem->getID())) ? true : false;
+
if(!inList){
- if(!Channel->GroupSep()){
+ if(Channel->GroupSep()){
+ 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 if(Channel->Vpid()==0){
+ // TODO: add radio support
+ MESSAGE("Skipping radio '%s'", Channel->Name());
+ }
+ else {
noResource = false;
- 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());
@@ -231,17 +296,16 @@ int cMediaDatabase::loadChannels(){
ERROR("Unable to get resources for this channel");
noResource = true;
}
- if(noResource && addWithoutResources) {
+ if(!noResource || addWithoutResources) {
TV->addObject(ChannelItem);
- if(this->mFactory->saveObject(ChannelItem)) return -1;
+ if(this->mFactory->saveObject(ChannelItem) ||
+ this->addFastFind(ChannelItem, ChannelID.ToString())){
+ this->mFactory->deleteObject(ChannelItem);
+ return -1;
+ }
+ MESSAGE("Successfuly added channel");
}
}
- 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());
@@ -255,12 +319,38 @@ int cMediaDatabase::loadChannels(){
// MESSAGE("Loading recordings");
// cUPnPClassContainer* Records = (cUPnPClassContainer*)this->getObjectByID(4);
// if(Records){
-// // Iterating channels
-// cList<cUPnPClassObject>* List = Records->getObjectList();
-// for(cRecording* Record = Recordings.First(); Record; Record = Recordings.Next(Record)){
+// cRecording* Recording = NULL;
+// for(Recording = Recordings.First(); Recording; Recording = Recordings.Next(Recording)){
+// // Iterating the records
+// bool inList = false;
+//
+// MESSAGE("Determine if the channel %s is already listed", Recording->FileName());
+// const cRecordingInfo* RecInfo = Recording->Info();
+//
+// MESSAGE("%s", *RecInfo->Components()->Component(0)->ToString());
+// cUPnPClassMovie *MovieItem = NULL;
+//
+// MovieItem = (cUPnPClassMovie*)this->getObjectByFastFind(Recording->FileName());
+//
+// inList = (MovieItem && Records->getObject(MovieItem->getID())) ? true : false;
+//
+// if(inList){
+//
+// MESSAGE("Adding movie '%s' File name:%s", RecInfo->Title(), Recording->FileName());
+//
+// MovieItem = (cUPnPClassMovie*)this->mFactory->createObject(UPNP_CLASS_MOVIE, RecInfo->Title());
+// MovieItem->setDescription(RecInfo->ShortText());
+// MovieItem->setLongDescription(RecInfo->Description());
+// MovieItem->setStorageMedium(UPNP_STORAGE_HDD);
+//
//
+// }
+// else {
+// MESSAGE("Skipping %s, already in Database", Recording->FileName());
+// }
// }
// }
+// return 0;
//}
void cMediaDatabase::Action(){
diff --git a/database/metadata.h b/database/metadata.h
index 32d663e..6e3732c 100644
--- a/database/metadata.h
+++ b/database/metadata.h
@@ -51,6 +51,8 @@ public:
const char* getContainerUpdateIDs();
cMediaDatabase();
virtual ~cMediaDatabase();
+ int addFastFind(cUPnPClassObject* Object, const char* FastFind);
+ cUPnPClassObject* getObjectByFastFind(const char* FastFind);
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 = "");
diff --git a/database/object.cpp b/database/object.cpp
index 748078d..e533fbc 100644
--- a/database/object.cpp
+++ b/database/object.cpp
@@ -36,7 +36,7 @@ cUPnPResource::cUPnPResource(){
}
off64_t cUPnPResource::getFileSize() const {
- return (this->mSize) ? this->mSize : -1;
+ return (this->mSize) ? this->mSize : (off64_t)-1;
}
time_t cUPnPResource::getLastModification() const {
@@ -105,6 +105,7 @@ void cUPnPObjects::SortBy(const char* Property, bool Descending){
cUPnPClassObject::cUPnPClassObject(){
this->mID = -1;
+ this->mLastID = -1;
this->mResources = new cList<cUPnPResource>;
this->mResourcesID = new cHash<cUPnPResource>;
this->mParent = NULL;
@@ -154,6 +155,7 @@ int cUPnPClassObject::setID(cUPnPObjectID ID){
ERROR("Invalid object ID '%s'",*ID);
return -1;
}
+ this->mLastID = (this->mID==-1) ? ID : this->mID;
this->mID = ID;
return 0;
}
@@ -520,16 +522,16 @@ bool cUPnPClassContainer::getProperty(const char* Property, char** Value) const
void cUPnPClassContainer::addObject(cUPnPClassObject* Object){
MESSAGE("Adding object (ID:%s) to container (ID:%s)", *Object->getID(), *this->getID());
+ Object->setParent(this);
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;
+ MESSAGE("Removed object (ID:%s) from container (ID:%s)", *Object->getID(), *this->getID());
}
cUPnPClassObject* cUPnPClassContainer::getObject(cUPnPObjectID ID) const {
@@ -1104,6 +1106,7 @@ cUPnPClassObject* cUPnPObjectMediator::getObject(cUPnPObjectID){ WARNING("Gettin
cUPnPClassObject* cUPnPObjectMediator::createObject(const char*, bool){ WARNING("Getting instance of class 'Object' forbidden"); return NULL; }
int cUPnPObjectMediator::objectToDatabase(cUPnPClassObject* Object){
+ MESSAGE("Updating object #%s", *Object->getID());
cString Format = "UPDATE %s SET %s WHERE %s='%s'";
//cString Format = "INSERT OR REPLACE INTO %s (%s) VALUES (%s);";
cString Statement=NULL;
@@ -1128,16 +1131,19 @@ int cUPnPObjectMediator::objectToDatabase(cUPnPClassObject* Object){
}
char *escapedValue;
escapeSQLite(Value, &escapedValue);
+ MESSAGE("Set %s to %s", *(*Property), 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, *Set, SQLITE_COL_OBJECTID, *Object->mLastID);
//Statement = cString::sprintf(Format, SQLITE_TABLE_OBJECTS, *Columns, *Values);
if(this->mDatabase->execStatement(Statement)){
ERROR("Error while executing statement");
return -1;
}
+ // The update was successful --> the current ID is now also the LastID
+ Object->mLastID = Object->mID;
return 0;
}
@@ -1146,7 +1152,6 @@ int cUPnPObjectMediator::databaseToObject(cUPnPClassObject* Object, cUPnPObjectI
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;
diff --git a/database/object.h b/database/object.h
index 9b62c54..9da62db 100644
--- a/database/object.h
+++ b/database/object.h
@@ -104,6 +104,7 @@ class cUPnPClassObject : public cListObject {
friend class cUPnPObjectMediator;
friend class cUPnPClassContainer;
private:
+ cUPnPObjectID mLastID;
bool mDeleted; // is this Objected marked as deleted
protected:
time_t mLastModified;
@@ -271,6 +272,28 @@ public:
const char* getRating() const { return this->mRating; }
};
+class cUPnPClassMovie : public cUPnPClassVideoItem {
+ friend class cMediaDatabase;
+ friend class cUPnPObjectMediator;
+ friend class cUPnPMovieMediator;
+protected:
+ int mDVDRegionCode;
+ cString mStorageMedium;
+ cUPnPClassMovie();
+public:
+ virtual ~cUPnPClassMovie();
+ //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 setDVDRegionCode(int RegionCode);
+ int setStorageMedium(const char* StorageMedium);
+ /******** Getter ********/
+ int getDVDRegionCode() const { return this->mDVDRegionCode; }
+ const char* getStorageMedium() const { return this->mStorageMedium; }
+};
+
class cUPnPClassVideoBroadcast : public cUPnPClassVideoItem {
friend class cMediaDatabase;
friend class cUPnPObjectMediator;