From 99d216d4748e409593dcfa8d1c96330ab7b961ef Mon Sep 17 00:00:00 2001 From: codestation Date: Wed, 5 Feb 2014 23:30:12 -0430 Subject: [PATCH] Enable SQLiteDB backend. Multiple fixed and finished the implementation of the SQLite backend. Try to load the database before trying to attempt a rescan. Delete the metadala list for backends that allocate them on demand. Moved some generic functions to the databse class. Renamed utils header to avoid rare QtCreator bug. --- qcma.pro | 12 +- src/avdecoder.cpp | 2 +- src/clientmanager.cpp | 15 +- src/cmaclient.cpp | 2 +- src/cmaevent.cpp | 21 +- src/cmaobject.cpp | 2 +- src/{utils.cpp => cmautils.cpp} | 4 +- src/{utils.h => cmautils.h} | 2 +- src/database.cpp | 28 ++ src/database.h | 34 +- src/forms/backupitem.cpp | 2 +- src/forms/backupmanagerform.cpp | 10 +- src/forms/backupmanagerform.h | 2 +- src/mainwidget.cpp | 5 +- src/qlistdb.cpp | 72 +-- src/qlistdb.h | 29 +- src/sqlitedb.cpp | 819 ++++++++++++++++++++++---------- src/sqlitedb.h | 85 ++-- 18 files changed, 747 insertions(+), 399 deletions(-) rename src/{utils.cpp => cmautils.cpp} (98%) rename src/{utils.h => cmautils.h} (96%) diff --git a/qcma.pro b/qcma.pro index 84cf31c..4adebd9 100644 --- a/qcma.pro +++ b/qcma.pro @@ -20,7 +20,7 @@ SOURCES += src/main.cpp \ src/capability.cpp \ src/cmaobject.cpp \ src/cmarootobject.cpp \ - src/utils.cpp \ + src/cmautils.cpp \ src/mainwidget.cpp \ src/singleapplication.cpp \ src/sforeader.cpp \ @@ -34,20 +34,20 @@ SOURCES += src/main.cpp \ src/sqlitedb.cpp \ src/httpdownloader.cpp \ src/qlistdb.cpp \ + src/database.cpp \ # forms src/forms/backupitem.cpp \ src/forms/backupmanagerform.cpp \ src/forms/configwidget.cpp \ src/forms/confirmdialog.cpp \ src/forms/pinform.cpp \ - src/forms/progressform.cpp \ - src/database.cpp + src/forms/progressform.cpp HEADERS += \ src/capability.h \ src/cmaobject.h \ src/cmarootobject.h \ - src/utils.h \ + src/cmautils.h \ src/mainwidget.h \ src/singleapplication.h \ src/sforeader.h \ @@ -61,14 +61,14 @@ HEADERS += \ src/sqlitedb.h \ src/httpdownloader.h \ src/qlistdb.h \ + src/database.h \ # forms src/forms/backupitem.h \ src/forms/backupmanagerform.h \ src/forms/configwidget.h \ src/forms/confirmdialog.h \ src/forms/pinform.h \ - src/forms/progressform.h \ - src/database.h + src/forms/progressform.h FORMS += \ src/forms/configwidget.ui \ diff --git a/src/avdecoder.cpp b/src/avdecoder.cpp index bcaf1d0..c5b0142 100644 --- a/src/avdecoder.cpp +++ b/src/avdecoder.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "utils.h" +#include "cmautils.h" #include "avdecoder.h" #include "database.h" diff --git a/src/clientmanager.cpp b/src/clientmanager.cpp index 395fb47..7cce9d9 100644 --- a/src/clientmanager.cpp +++ b/src/clientmanager.cpp @@ -19,7 +19,7 @@ #include "clientmanager.h" #include "cmaclient.h" -#include "utils.h" +#include "cmautils.h" #include "forms/progressform.h" #include @@ -122,13 +122,12 @@ void ClientManager::start() void ClientManager::refreshDatabase() { - bool prepared; - if(!m_db->reload(prepared)) { - if(prepared) { - emit messageSent(tr("Cannot refresh the database while is in use")); - } else { - emit messageSent(tr("No PS Vita system has been registered")); - } + if(m_db->load()) { + return; + } + + if(!m_db->rescan()) { + emit messageSent(tr("No PS Vita system has been registered")); } else { progress.showDelayed(1000); } diff --git a/src/cmaclient.cpp b/src/cmaclient.cpp index 1a4b23f..b843230 100644 --- a/src/cmaclient.cpp +++ b/src/cmaclient.cpp @@ -21,7 +21,7 @@ #include "capability.h" #include "avdecoder.h" #include "cmaevent.h" -#include "utils.h" +#include "cmautils.h" #include #include diff --git a/src/cmaevent.cpp b/src/cmaevent.cpp index d9c8f08..d504b67 100644 --- a/src/cmaevent.cpp +++ b/src/cmaevent.cpp @@ -18,7 +18,7 @@ */ #include "cmaevent.h" -#include "utils.h" +#include "cmautils.h" #include #include @@ -179,7 +179,8 @@ quint16 CmaEvent::processAllObjects(metadata_t &parent_metadata, quint32 handle) m_db->deleteEntry(ohfi); } - QDir dir(m_db->getAbsolutePath(parent_metadata.ohfi)); + QString fullpath = m_db->getAbsolutePath(parent_metadata.ohfi); + QDir dir(fullpath); if(dataType & Folder) { if(!dir.mkpath(name)) { @@ -205,12 +206,10 @@ quint16 CmaEvent::processAllObjects(metadata_t &parent_metadata, quint32 handle) } } - QFileInfo info(dir, name); - int new_ohfi = m_db->insertObjectEntry(info.absoluteFilePath(), parent_metadata.ohfi); + int new_ohfi = m_db->insertObjectEntry(fullpath, name, parent_metadata.ohfi); + qDebug("Added object %s with OHFI %i to database", name, new_ohfi); free(name); - qDebug() << QString("Added object %1 with OHFI %2 to database").arg(info.absoluteFilePath(), QString::number(new_ohfi)); - if(dataType & Folder) { metadata_t folder_metadata; m_db->getObjectMetadata(new_ohfi, folder_metadata); @@ -389,6 +388,7 @@ void CmaEvent::vitaEventSendObjectMetadata(vita_event_t *event, int eventId) } else { VitaMTP_ReportResult(device, eventId, PTP_RC_OK); } + m_db->freeMetadata(meta); } void CmaEvent::vitaEventSendObject(vita_event_t *event, int eventId) @@ -470,6 +470,8 @@ void CmaEvent::vitaEventSendObject(vita_event_t *event, int eventId) } while(metadata && metadata->ohfiParent >= OHFI_BASE_VALUE); // get everything under this "folder" + m_db->freeMetadata(start); + VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, handle); VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Data); // TODO: Send thumbnail @@ -562,13 +564,14 @@ void CmaEvent::vitaEventSendObjectStatus(vita_event_t *event, int eventId) QMutexLocker locker(&m_db->mutex); + qDebug("Checking for path %s under ohfi %i", objectstatus.title, objectstatus.ohfiRoot); int ohfi = m_db->getPathId(objectstatus.title, objectstatus.ohfiRoot); if(ohfi == 0) { // not in database, don't return metadata qDebug("Object %s not in database (OHFI: %i). Sending OK response for non-existence", objectstatus.title, objectstatus.ohfiRoot); VitaMTP_ReportResult(device, eventId, PTP_RC_OK); } else { - metadata_t metadata; + metadata_t metadata = metadata_t(); m_db->getObjectMetadata(ohfi, metadata); metadata.next_metadata = NULL; qDebug("Sending metadata for OHFI %d", ohfi); @@ -780,7 +783,7 @@ void CmaEvent::vitaEventOperateObject(vita_event_t *event, int eventId) qWarning("Unable to create temporary folder: %s", operateobject.title); VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Operate_Object); } else { - int new_ohfi = m_db->insertObjectEntry(QDir(dir).absoluteFilePath(operateobject.title), operateobject.ohfi); + int new_ohfi = m_db->insertObjectEntry(fullpath, operateobject.title, operateobject.ohfi); qDebug("Created folder %s with OHFI %d", operateobject.title, new_ohfi); VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, new_ohfi); } @@ -794,7 +797,7 @@ void CmaEvent::vitaEventOperateObject(vita_event_t *event, int eventId) qWarning("Unable to create temporary file: %s", operateobject.title); VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Operate_Object); } else { - int new_ohfi = m_db->insertObjectEntry(file.fileName(), operateobject.ohfi); + int new_ohfi = m_db->insertObjectEntry(fullpath, operateobject.title, operateobject.ohfi); //qDebug("Created file %s with OHFI %d under parent %s", newobj->metadata.path, new_ohfi, root->metadata.path); VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, new_ohfi); } diff --git a/src/cmaobject.cpp b/src/cmaobject.cpp index 68856a9..6e0a3fc 100644 --- a/src/cmaobject.cpp +++ b/src/cmaobject.cpp @@ -21,7 +21,7 @@ #include "sforeader.h" #include "avdecoder.h" #include "database.h" -#include "utils.h" +#include "cmautils.h" #include #include diff --git a/src/utils.cpp b/src/cmautils.cpp similarity index 98% rename from src/utils.cpp rename to src/cmautils.cpp index 3e1680b..0275180 100644 --- a/src/utils.cpp +++ b/src/cmautils.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "utils.h" +#include "cmautils.h" #include "avdecoder.h" #include @@ -162,7 +162,7 @@ QByteArray getThumbnail(const QString &path, DataType type, metadata_t *metadata return data; } -QString readable_size(quint64 size, bool use_gib) +QString readable_size(qint64 size, bool use_gib) { QStringList list; list << "KiB" << "MiB"; diff --git a/src/utils.h b/src/cmautils.h similarity index 96% rename from src/utils.h rename to src/cmautils.h index 35f690f..a69af6d 100644 --- a/src/utils.h +++ b/src/cmautils.h @@ -46,7 +46,7 @@ public: #endif bool removeRecursively(const QString &path); -QString readable_size(quint64 size, bool use_gib = false); +QString readable_size(qint64 size, bool use_gib = false); bool getDiskSpace(const QString &dir, quint64 *free, quint64 *total); QByteArray getThumbnail(const QString &path, DataType type, metadata_t *metadata); diff --git a/src/database.cpp b/src/database.cpp index c9785c2..2d415c9 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -2,6 +2,7 @@ #include "avdecoder.h" #include +#include const file_type audio_list[] = { {"mp3", FILE_FORMAT_MP3, CODEC_TYPE_MP3}, @@ -28,6 +29,33 @@ Database::Database(QObject *parent) : mutex(QMutex::Recursive) { } +#include +void Database::process() +{ + qDebug("Starting database_thread: 0x%016" PRIxPTR, (uintptr_t)QThread::currentThreadId()); + clear(); + cancel_operation = false; + int count = create(); + cancel_operation = false; + qDebug("Added %i entries to the database", count); + if(count < 0) { + clear(); + } + emit updated(count); + mutex.unlock(); +} + +void Database::cancelOperation() +{ + QMutexLocker locker(&cancel); + cancel_operation = true; +} + +bool Database::continueOperation() +{ + QMutexLocker locker(&cancel); + return !cancel_operation; +} int Database::checkFileType(const QString path, int ohfi_root) { diff --git a/src/database.h b/src/database.h index 0f1851f..95b98f9 100644 --- a/src/database.h +++ b/src/database.h @@ -43,6 +43,11 @@ class Database : public QObject Q_OBJECT public: explicit Database(QObject *parent = 0); + + virtual bool load() = 0; + virtual bool rescan() = 0; + virtual void setUUID(const QString &uuid) = 0; + virtual int childObjectCount(int parent_ohfi) = 0; virtual bool deleteEntry(int ohfi, int root_ohfi = 0) = 0; virtual QString getAbsolutePath(int ohfi) = 0; @@ -51,14 +56,12 @@ public: virtual int getObjectMetadatas(int parent_ohfi, metadata_t **metadata, int index = 0, int max_number = 0) = 0; virtual qint64 getObjectSize(int ohfi) = 0; virtual int getPathId(const char *name, int ohfi) = 0; - virtual int insertObjectEntry(const QString &path, int parent_ohfi) = 0; + virtual int insertObjectEntry(const QString &path, const QString &name, int parent_ohfi) = 0; virtual bool renameObject(int ohfi, const QString &name) = 0; virtual void setObjectSize(int ohfi, qint64 size) = 0; virtual int getParentId(int ohfi) = 0; virtual int getRootId(int ohfi) = 0; - - virtual bool reload(bool &prepared) = 0; - virtual void setUUID(const QString uuid) = 0; + virtual void freeMetadata(metadata_t *metadata) = 0; static int checkFileType(const QString path, int ohfi_root); static void loadMusicMetadata(const QString &path, metadata_t &metadata); @@ -66,6 +69,29 @@ public: static void loadVideoMetadata(const QString &path, metadata_t &metadata); QMutex mutex; + +protected: + bool continueOperation(); + +private: + + virtual void clear() = 0; + virtual int create() = 0; + + // control variables + QMutex cancel; + bool cancel_operation; + +signals: + void fileAdded(QString); + void directoryAdded(QString); + void updated(int); + +protected slots: + void process(); + +public slots: + void cancelOperation(); }; #endif // DATABASE_H diff --git a/src/forms/backupitem.cpp b/src/forms/backupitem.cpp index 3cb6198..8257ffb 100644 --- a/src/forms/backupitem.cpp +++ b/src/forms/backupitem.cpp @@ -19,7 +19,7 @@ #include "backupitem.h" #include "ui_backupitem.h" -#include "utils.h" +#include "cmautils.h" #include "dds.h" #include diff --git a/src/forms/backupmanagerform.cpp b/src/forms/backupmanagerform.cpp index e8ec4e3..3f83244 100644 --- a/src/forms/backupmanagerform.cpp +++ b/src/forms/backupmanagerform.cpp @@ -22,7 +22,7 @@ #include "cmaobject.h" #include "sforeader.h" #include "confirmdialog.h" -#include "utils.h" +#include "cmautils.h" #include "filterlineedit.h" #include @@ -83,7 +83,7 @@ void BackupManagerForm::removeEntry(BackupItem *item) } } -void BackupManagerForm::setBackupUsage(quint64 size) +void BackupManagerForm::setBackupUsage(qint64 size) { ui->usageLabel->setText(tr("Backup disk usage: %1").arg(readable_size(size, true))); } @@ -163,10 +163,12 @@ void BackupManagerForm::loadBackupListing(int index) #else horiz_header->setResizeMode(QHeaderView::Stretch); #endif - setBackupUsage(m_db->getObjectSize(ohfi)); + qint64 backup_size = m_db->getObjectSize(ohfi); + setBackupUsage(backup_size); QString path = m_db->getAbsolutePath(ohfi); QList item_list; + metadata_t *first = meta; while(meta) { QString base_path = path + QDir::separator() + meta->name; QString parent_path = sys_dir ? base_path + QDir::separator() + "sce_sys" : base_path; @@ -215,6 +217,8 @@ void BackupManagerForm::loadBackupListing(int index) meta = meta->next_metadata; } + m_db->freeMetadata(first); + qSort(item_list.begin(), item_list.end(), BackupItem::lessThan); int row; diff --git a/src/forms/backupmanagerform.h b/src/forms/backupmanagerform.h index 2290fbb..2613df3 100644 --- a/src/forms/backupmanagerform.h +++ b/src/forms/backupmanagerform.h @@ -41,7 +41,7 @@ public: private: void setupForm(); - void setBackupUsage(quint64 size); + void setBackupUsage(qint64 size); Ui::BackupManagerForm *ui; diff --git a/src/mainwidget.cpp b/src/mainwidget.cpp index 1e8e494..e6c178e 100644 --- a/src/mainwidget.cpp +++ b/src/mainwidget.cpp @@ -19,7 +19,7 @@ #include "mainwidget.h" #include "cmaclient.h" -#include "utils.h" +#include "cmautils.h" #include "qlistdb.h" #include "sqlitedb.h" @@ -123,7 +123,7 @@ void MainWidget::prepareApplication() if(QSettings().value("useMemoryStorage", true).toBool()) { db = new QListDB(); } else { - db = NULL; // new SQLiteDB(); + db = new SQLiteDB(); } configForm = new ConfigWidget(this); @@ -267,4 +267,5 @@ MainWidget::~MainWidget() #ifndef ENABLE_KDE_NOTIFIER trayIcon->hide(); #endif + delete db; } diff --git a/src/qlistdb.cpp b/src/qlistdb.cpp index 8b9aa01..b698551 100644 --- a/src/qlistdb.cpp +++ b/src/qlistdb.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "utils.h" +#include "cmautils.h" #include "qlistdb.h" #include "cmaobject.h" @@ -34,8 +34,8 @@ QListDB::QListDB(QObject *parent) : QString uuid = QSettings().value("lastAccountId", "ffffffffffffffff").toString(); CMARootObject::uuid = uuid; thread = new QThread(); - timer = new QTimer(); moveToThread(thread); + timer = new QTimer(); thread->start(); timer->setInterval(0); @@ -45,7 +45,7 @@ QListDB::QListDB(QObject *parent) : QListDB::~QListDB() { - destroy(); + clear(); timer->stop(); delete timer; thread->quit(); @@ -53,53 +53,41 @@ QListDB::~QListDB() delete thread; } -void QListDB::setUUID(const QString uuid) +void QListDB::setUUID(const QString &uuid) { CMARootObject::uuid = uuid; QSettings().setValue("lastAccountId", uuid); } -bool QListDB::reload(bool &prepared) +bool QListDB::load() { - if(mutex.tryLock()) { + // not implemented + return false; +} + +bool QListDB::rescan() +{ + if(mutex.tryLock(1000)) { if(CMARootObject::uuid != "ffffffffffffffff") { timer->start(); - prepared = true; + return true; } else { mutex.unlock(); - prepared = false; return false; } - return true; - } else { - return false; } + return false; } -void QListDB::process() +void QListDB::clear() { - destroy(); - cancel_operation = false; - int count = create(); - cancel_operation = false; - qDebug("Added %i entries to the database", count); - if(count < 0) { - destroy(); + for(map_list::iterator root = object_list.begin(); root != object_list.end(); ++root) { + CMARootObject *first = static_cast((*root).takeFirst()); + delete first; + qDeleteAll(*root); } - emit updated(count); - mutex.unlock(); -} -void QListDB::cancelOperation() -{ - QMutexLocker locker(&cancel); - cancel_operation = true; -} - -bool QListDB::continueOperation() -{ - QMutexLocker locker(&cancel); - return !cancel_operation; + object_list.clear(); } int QListDB::create() @@ -147,6 +135,8 @@ int QListDB::create() return -1; } + qDebug("Added %i objects for OHFI %#02X", dir_count, ohfi_array[i]); + total_objects += dir_count; object_list[ohfi_array[i]] = list; } @@ -236,19 +226,6 @@ int QListDB::recursiveScanRootDirectory(root_list &list, CMAObject *parent, int return total_objects; } -void QListDB::destroy() -{ - //QMutexLocker locker(&mutex); - - for(map_list::iterator root = object_list.begin(); root != object_list.end(); ++root) { - CMARootObject *first = static_cast((*root).takeFirst()); - delete first; - qDeleteAll(*root); - } - - object_list.clear(); -} - bool QListDB::removeInternal(root_list &list, int ohfi) { bool found = false; @@ -487,7 +464,7 @@ int QListDB::getPathId(const char *name, int ohfi) return 0; } -int QListDB::insertObjectEntry(const QString &path, int parent_ohfi) +int QListDB::insertObjectEntry(const QString &path, const QString &name, int parent_ohfi) { QMutexLocker locker(&mutex); @@ -505,7 +482,8 @@ int QListDB::insertObjectEntry(const QString &path, int parent_ohfi) parent_obj = parent_obj->parent; } - newobj->initObject(path, parent_obj->metadata.dataType); + QFileInfo info(path, name); + newobj->initObject(info, parent_obj->metadata.dataType); cat_list->append(newobj); return newobj->metadata.ohfi; } diff --git a/src/qlistdb.h b/src/qlistdb.h index 9c6298f..da45c86 100644 --- a/src/qlistdb.h +++ b/src/qlistdb.h @@ -38,8 +38,13 @@ public: explicit QListDB(QObject *parent = 0); ~QListDB(); + bool load(); + bool rescan(); + void close(); + void clear(); + bool reload(bool &prepared); - void setUUID(const QString uuid); + void setUUID(const QString &uuid); int childObjectCount(int parent_ohfi); bool deleteEntry(int ohfi, int root_ohfi = 0); @@ -51,9 +56,12 @@ public: int getPathId(const char *name, int ohfi); QString getRelativePath(int ohfi); int getRootId(int ohfi); - int insertObjectEntry(const QString &path, int parent_ohfi); + int insertObjectEntry(const QString &path, const QString &name, int parent_ohfi); bool renameObject(int ohfi, const QString &name); void setObjectSize(int ohfi, qint64 size); + void freeMetadata(metadata_t *metadata) { + Q_UNUSED(metadata); + } private: typedef struct { @@ -65,7 +73,6 @@ private: typedef QMap map_list; int create(); - void destroy(); int scanRootDirectory(root_list &list,int ohfi_type); int recursiveScanRootDirectory(root_list &list, CMAObject *parent, int ohfi_type); bool hasFilter(const CMARootObject *object,int ohfi); @@ -78,26 +85,10 @@ private: bool find(int ohfi, find_data &data); int acceptFilteredObject(const CMAObject *parent, const CMAObject *current, int type); CMAObject *ohfiToObject(int ohfi); - bool continueOperation(); - - // control variables - QMutex cancel; - bool cancel_operation; QTimer *timer; QThread *thread; map_list object_list; - -signals: - void fileAdded(QString); - void directoryAdded(QString); - void updated(int); - -protected slots: - void process(); - -public slots: - void cancelOperation(); }; #endif // QLISTDB_H diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index 192a6fe..162dc04 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -1,4 +1,23 @@ -#include "utils.h" +/* + * QCMA: Cross-platform content manager assistant for the PS Vita + * + * Copyright (C) 2014 Codestation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "cmautils.h" #include "sqlitedb.h" #include "sforeader.h" #include "avdecoder.h" @@ -26,13 +45,14 @@ static const char create_adjacent[] = "CREATE TABLE IF NOT EXISTS adjacent_objec static const char create_obj_node[] = "CREATE TABLE IF NOT EXISTS object_node (" "object_id INTEGER PRIMARY KEY AUTOINCREMENT," "type INTEGER NOT NULL," + "data_type INTEGER NOT NULL," "title TEXT," "child_count INTEGER NOT NULL DEFAULT 0," "reference_count INTEGER NOT NULL DEFAULT 0);"; static const char create_sources[] = "CREATE TABLE IF NOT EXISTS sources (" "object_id INTEGER PRIMARY KEY REFERENCES object_node(object_id) ON DELETE CASCADE," - "path TEXT UNIQUE NOT NULL CHECK (LENGTH(path) > 0)," + "path TEXT NOT NULL CHECK (LENGTH(path) > 0)," "size INTEGER," "date_created TIMESTAMP," "date_modified TIMESTAMP)"; @@ -114,19 +134,23 @@ static const char *trigger_list[] = { create_trigger_node, create_trigger_adjins, create_trigger_adjdel }; +static const int ohfi_array[] = { VITA_OHFI_MUSIC, VITA_OHFI_PHOTO, VITA_OHFI_VIDEO, + VITA_OHFI_BACKUP, VITA_OHFI_VITAAPP, VITA_OHFI_PSPAPP, + VITA_OHFI_PSPSAVE, VITA_OHFI_PSXAPP, VITA_OHFI_PSMAPP + }; + SQLiteDB::SQLiteDB(QObject *parent) : Database(parent) { uuid = QSettings().value("lastAccountId", "ffffffffffffffff").toString(); -} + thread = new QThread(); + moveToThread(thread); + timer = new QTimer(); + thread->start(); + timer->setInterval(0); + timer->setSingleShot(true); + connect(timer, SIGNAL(timeout()), this, SLOT(process())); -SQLiteDB::~SQLiteDB() -{ - db.close(); -} - -bool SQLiteDB::open() -{ // fetch a configured database path if it exists QString db_path = QStandardPaths::writableLocation(QStandardPaths::DataLocation); db_path = QSettings().value("databasePath", db_path).toString(); @@ -134,24 +158,66 @@ bool SQLiteDB::open() db = QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName(db_path + QDir::separator() + "qcma.sqlite"); - - return db.open(); } -void SQLiteDB::remove() +SQLiteDB::~SQLiteDB() { + db.close(); + timer->stop(); + delete timer; + thread->quit(); + thread->wait(); + delete thread; +} + + +void SQLiteDB::setUUID(const QString &uuid) +{ + this->uuid = uuid; + QSettings().setValue("lastAccountId", uuid); +} + +bool SQLiteDB::load() +{ + bool success = false; + + if(db.open()) { + qDebug() << "Database created/registered"; + success = !initialize(); + } else { + const QSqlError error = db.lastError(); + qWarning() << "Error opening connection to the database:" << error.text(); + } + + return success; +} + +bool SQLiteDB::rescan() +{ + if(mutex.tryLock(1000)) { + if(uuid != "ffffffffffffffff") { + timer->start(); + return true; + } else { + mutex.unlock(); + return false; + } + } + return false; +} + +void SQLiteDB::clear() +{ + db.close(); + //QSqlDatabase::removeDatabase("QSQLITE"); QString db_path = QStandardPaths::writableLocation(QStandardPaths::DataLocation); db_path = QSettings().value("databasePath", db_path).toString(); - QDir(QDir::root()).mkpath(db_path); - QFile(db_path + QDir::separator() + "qcma.sqlite").remove(); + load(); } bool SQLiteDB::initialize() { - if (!db.isOpen()) { - return false; - } QSqlQuery query; for(unsigned int i = 0; i < sizeof(table_list) / sizeof(const char *); i++) { @@ -169,7 +235,7 @@ bool SQLiteDB::initialize() } // force object_id to start at 256 - if(query.exec("INSERT INTO object_node (object_id, type) VALUES (255, 0)")) { + if(query.exec("INSERT INTO object_node (object_id, data_type, type) VALUES (255, 0, 0)")) { query.exec("DELETE FROM object_node WHERE object_id = 255"); } return true; @@ -184,97 +250,160 @@ int SQLiteDB::create() { int total_objects = 0; - const int ohfi_array[] = { VITA_OHFI_MUSIC, VITA_OHFI_VIDEO, VITA_OHFI_PHOTO, - VITA_OHFI_BACKUP, VITA_OHFI_VITAAPP, VITA_OHFI_PSPAPP, - VITA_OHFI_PSPSAVE, VITA_OHFI_PSXAPP, VITA_OHFI_PSMAPP - }; - - QSettings settings; - QString base_path; - + db.transaction(); for(int i = 0, max = sizeof(ohfi_array) / sizeof(int); i < max; i++) { - switch(ohfi_array[i]) { - case VITA_OHFI_MUSIC: - base_path = settings.value("musicPath").toString(); - break; - case VITA_OHFI_VIDEO: - base_path = settings.value("videoPath").toString(); - break; - case VITA_OHFI_PHOTO: - base_path = settings.value("photoPath").toString(); - break; - case VITA_OHFI_BACKUP: - base_path = settings.value("appsPath").toString() + QDir::separator() + "SYSTEM" + QDir::separator() + uuid; - break; - case VITA_OHFI_VITAAPP: - base_path = settings.value("appsPath").toString() + QDir::separator() + "APP" + QDir::separator() + uuid; - break; - case VITA_OHFI_PSPAPP: - base_path = settings.value("appsPath").toString() + QDir::separator() + "PGAME" + QDir::separator() + uuid; - break; - case VITA_OHFI_PSPSAVE: - base_path = settings.value("appsPath").toString() + QDir::separator() + "PSAVEDATA" + QDir::separator() + uuid; - break; - case VITA_OHFI_PSXAPP: - base_path = settings.value("appsPath").toString() + QDir::separator() + "PSGAME" + QDir::separator() + uuid; - break; - case VITA_OHFI_PSMAPP: - base_path = settings.value("appsPath").toString() + QDir::separator() + "PSM" + QDir::separator() + uuid; - break; - } - - int dir_count = recursiveScanRootDirectory(base_path, ohfi_array[i], ohfi_array[i]); + QString base_path = getBasePath(ohfi_array[i]); + int dir_count = recursiveScanRootDirectory(base_path, NULL, ohfi_array[i], ohfi_array[i]); if(dir_count < 0) { + db.rollback(); return -1; } + + //qDebug("Added %i objects for OHFI %#02X", dir_count, ohfi_array[i]); + total_objects += dir_count; } + db.commit(); return total_objects; } -int SQLiteDB::recursiveScanRootDirectory(const QString &base_path, int parent, int parent_type) +QString SQLiteDB::getBasePath(int root_ohfi) +{ + QString base_path; + QSettings settings; + + switch(root_ohfi) { + case VITA_OHFI_MUSIC: + base_path = settings.value("musicPath").toString(); + break; + case VITA_OHFI_VIDEO: + base_path = settings.value("videoPath").toString(); + break; + case VITA_OHFI_PHOTO: + base_path = settings.value("photoPath").toString(); + break; + case VITA_OHFI_BACKUP: + base_path = settings.value("appsPath").toString() + "/SYSTEM/" + uuid; + break; + case VITA_OHFI_VITAAPP: + base_path = settings.value("appsPath").toString() + "/APP/" + uuid; + break; + case VITA_OHFI_PSPAPP: + base_path = settings.value("appsPath").toString() + "/PGAME/" + uuid; + break; + case VITA_OHFI_PSPSAVE: + base_path = settings.value("appsPath").toString() + "/PSAVEDATA/" + uuid; + break; + case VITA_OHFI_PSXAPP: + base_path = settings.value("appsPath").toString() + "/PSGAME/" + uuid; + break; + case VITA_OHFI_PSMAPP: + base_path = settings.value("appsPath").toString() + "/PSM/" + uuid; + break; + } + return base_path; +} + +int SQLiteDB::recursiveScanRootDirectory(const QString &base_path, const QString &rel_path, int parent_ohfi, int root_ohfi) { int total_objects = 0; - QDir dir(base_path); + QDir dir(base_path + "/" + rel_path); dir.setSorting(QDir::Name); QFileInfoList qsl = dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot); foreach(const QFileInfo &info, qsl) { - //qDebug() << "Processing " << info.fileName(); - if(info.isDir()) { - int ohfi = insertDirectoryEntry(info.absoluteFilePath(), parent_type, parent); - total_objects += recursiveScanRootDirectory(info.absoluteFilePath(), ohfi, parent_type); - } else if(info.isFile()) { - switch(parent_type) { - case VITA_OHFI_MUSIC: - insertMusicEntry(info.absoluteFilePath(), OBJECT_MUSIC | (parent_type & ~OBJECT_FOLDER), parent); - break; - case VITA_OHFI_PHOTO: - insertPhotoEntry(info.absoluteFilePath(), OBJECT_PHOTO | (parent_type & ~OBJECT_FOLDER), parent); - break; - case VITA_OHFI_VIDEO: - insertVideoEntry(info.absoluteFilePath(), OBJECT_VIDEO | (parent_type & ~OBJECT_FOLDER), parent); - break; - case VITA_OHFI_PSPSAVE: - insertSavedataEntry(info.absoluteFilePath(), OBJECT_SAVEDATA | (parent_type & ~OBJECT_FOLDER), parent); - break; - case VITA_OHFI_VITAAPP: - case VITA_OHFI_PSPAPP: - case VITA_OHFI_PSXAPP: - case VITA_OHFI_PSMAPP: - case VITA_OHFI_BACKUP: - insertApplicationEntry(info.absoluteFilePath(), OBJECT_APPLICATION | (parent_type & ~OBJECT_FOLDER), parent, parent_type); - } - total_objects++; + if(!continueOperation()) { + return -1; + } + + //qDebug() << "Processing " << info.fileName(); + + QString rel_name = rel_path.isNull() ? info.fileName() : rel_path + "/" + info.fileName(); + + int ohfi = insertObjectEntryInternal(base_path, rel_name, parent_ohfi, root_ohfi); + + if(ohfi > 0) { + // update progress dialog + if(info.isDir()) { + emit directoryAdded(base_path + "/" + rel_name); + total_objects += recursiveScanRootDirectory(base_path, rel_name, ohfi, root_ohfi); + qint64 dirsize = getChildenTotalSize(ohfi); + setObjectSize(ohfi, dirsize); + } else if(info.isFile()) { + emit fileAdded(info.fileName()); + total_objects++; + } } } return total_objects; } +int SQLiteDB::insertObjectEntry(const QString &path, const QString &name, int parent_ohfi) +{ + int ohfi; + int type; + + if((type = getObjectType(parent_ohfi)) == 0) { + return 0; + } + db.transaction(); + if((ohfi = insertObjectEntryInternal(path, name, parent_ohfi, type)) == 0) { + db.rollback(); + } + db.commit(); + + return ohfi; +} + +int SQLiteDB::insertObjectEntryInternal(const QString &path, const QString &name, int parent_ohfi, int root_ohfi) +{ + int ohfi; + QFileInfo info(path, name); + + if(info.isDir()) { + ohfi = insertDefaultEntry(path, name, info.fileName(), parent_ohfi, Folder); + } else { + ohfi = insertFileEntry(path, name, parent_ohfi, root_ohfi); + } + + return ohfi; +} + +int SQLiteDB::insertFileEntry(const QString &path, const QString &name, int parent_ohfi, int root_ohfi) +{ + int ohfi = 0; + + switch(root_ohfi) { + case VITA_OHFI_MUSIC: + ohfi = insertMusicEntry(path, name, parent_ohfi, File | Music); + break; + case VITA_OHFI_PHOTO: + ohfi = insertPhotoEntry(path, name, parent_ohfi, File | Photo); + break; + case VITA_OHFI_VIDEO: + ohfi = insertVideoEntry(path, name, parent_ohfi, File | Video); + break; + case VITA_OHFI_PSPSAVE: + ohfi = insertSavedataEntry(path, name, parent_ohfi, File | SaveData); + break; + case VITA_OHFI_VITAAPP: + case VITA_OHFI_PSPAPP: + case VITA_OHFI_PSXAPP: + case VITA_OHFI_PSMAPP: + case VITA_OHFI_BACKUP: + ohfi = insertApplicationEntry(path, name, parent_ohfi, File | App, root_ohfi); + break; + default: + qFatal("Invalid parent type"); + } + + return ohfi; +} + int SQLiteDB::getPathId(const QString &path) { QSqlQuery query(QString("SELECT object_id from sources WHERE path = %1").arg(path)); @@ -313,12 +442,6 @@ bool SQLiteDB::deleteEntry(int ohfi) return ret; } -bool SQLiteDB::deleteEntry(const QString &path) -{ - QSqlQuery query(QString("DELETE FROM object_node WHERE object_id == (SELECT object_id FROM sources WHERE path == %1)").arg(path)); - return query.exec(); -} - bool SQLiteDB::updateAdjacencyList(int ohfi, int parent) { QSqlQuery query; @@ -328,8 +451,6 @@ bool SQLiteDB::updateAdjacencyList(int ohfi, int parent) if(query.exec() && query.next()) { return true; - } else { - qDebug() << query.lastError(); } query.prepare("INSERT INTO adjacent_objects (parent_id, child_id)" @@ -343,67 +464,61 @@ bool SQLiteDB::updateAdjacencyList(int ohfi, int parent) return ret; } -uint SQLiteDB::insertDirectoryEntry(const QString &path, int type, int parent) +int SQLiteDB::insertDefaultEntry(const QString &path, const QString &name, const QString &title, int parent, int type) { - uint ohfi; - db.transaction(); - QString dirname = QFileInfo(path).fileName(); + int ohfi = 0; - if((ohfi = insertObjectEntry(dirname.toUtf8().constData(), type)) == 0) { - db.rollback(); + if((ohfi = insertNodeEntry(title, VITA_DIR_TYPE_MASK_REGULAR, type)) == 0) { return 0; } if(parent && !updateAdjacencyList(ohfi, parent)) { - db.rollback(); return 0; } - if(!insertSourceEntry(ohfi, path)) { - db.rollback(); + if(!name.isNull() && !insertSourceEntry(ohfi, path, name)) { return 0; } - db.commit(); return ohfi; } -uint SQLiteDB::insertObjectEntry(const char *title, int type) +int SQLiteDB::insertNodeEntry(const QString &title, int type, int data_type) { QSqlQuery query; - //query.prepare("SELECT object_id FROM object_node WHERE type == :type and title == :title"); - //query.bindValue(0, type); - //query.bindValue(1, title); - - //if(!query.exec() || !query.next()) { - query.prepare("INSERT INTO object_node (type, title) VALUES (:type, :title)"); + query.prepare("INSERT INTO object_node (type, data_type, title) VALUES (:type, :data_type, :title)"); query.bindValue(0, type); - query.bindValue(1, title); + query.bindValue(1, data_type); + query.bindValue(2, title); if(!query.exec() || !query.exec("SELECT last_insert_rowid()") || !query.next()) { qDebug() << query.lastError(); return 0; } - //} return query.value(0).toInt(); } -bool SQLiteDB::insertSourceEntry(uint object_id, const QString &path) +bool SQLiteDB::insertSourceEntry(uint object_id, const QString &path, const QString &name) { - qint64 size; - uint date_created, date_modified; + QVariant size, date_created, date_modified; - QFileInfo info(path); - size = info.size(); - date_created = info.created().toTime_t(); - date_modified = info.lastModified().toTime_t(); + QFileInfo info(path, name); + if(info.isFile()) { + size = QVariant(info.size()); + date_created = QVariant(info.created().toTime_t()); + } else { + size = QVariant(QVariant::LongLong); + date_created = QVariant(QVariant::UInt); + } + + date_modified = QVariant(info.lastModified().toTime_t()); QSqlQuery query; query.prepare("REPLACE INTO sources (object_id, path, size, date_created, date_modified)" "VALUES (:object_id, :path, :size, :date_created, :date_modified)"); query.bindValue(0, object_id); - query.bindValue(1, path); + query.bindValue(1, name); query.bindValue(2, size); query.bindValue(3, date_created); query.bindValue(4, date_modified); @@ -414,22 +529,22 @@ bool SQLiteDB::insertSourceEntry(uint object_id, const QString &path) return ret; } -uint SQLiteDB::insertMusicEntry(const QString &path, int type, int parent) +uint SQLiteDB::insertMusicEntry(const QString &path, const QString &name, int parent, int type) { bool ok; - uint ohfi; + int ohfi; AVDecoder decoder; quint64 duration; const char *artist, *album, *albumartist, *genre, *track, *title; int file_format, audio_codec, audio_bitrate, genre_id, artist_id, track_id, album_id, track_number; - int file_type = checkFileType(path, VITA_OHFI_MUSIC); + int file_type = checkFileType(name, VITA_OHFI_MUSIC); if(file_type < 0) { - qDebug() << "Excluding from database:" << path; + //qDebug() << "Excluding from database:" << path; return 0; } - if(!decoder.open(path)) { + if(!decoder.open(path + "/" + name)) { return 0; } @@ -456,30 +571,16 @@ uint SQLiteDB::insertMusicEntry(const QString &path, int type, int parent) file_format = audio_list[file_type].file_format; audio_codec = audio_list[file_type].file_codec; - QByteArray basename = QFileInfo(path).baseName().toUtf8(); + QByteArray basename = QFileInfo(name).baseName().toUtf8(); title = decoder.getMetadataEntry("title", basename.constData()); - db.transaction(); - - if((ohfi = insertObjectEntry(title, type)) == 0) { - db.rollback(); - return 0; - } - - if(parent && !updateAdjacencyList(ohfi, parent)) { - db.rollback(); - return 0; - } - - if(!insertSourceEntry(ohfi, path)) { - db.rollback(); + if((ohfi = insertDefaultEntry(path, name, title, parent, type)) == 0) { return 0; } if(genre) { - genre_id = insertObjectEntry(genre, type | OBJECT_GENRE); + genre_id = insertNodeEntry(genre, VITA_DIR_TYPE_MASK_GENRES, type); if(!updateAdjacencyList(ohfi, genre_id)) { - db.rollback(); return 0; } } else { @@ -487,9 +588,8 @@ uint SQLiteDB::insertMusicEntry(const QString &path, int type, int parent) } if(artist) { - track_id = insertObjectEntry(artist, type | OBJECT_ARTIST); + track_id = insertNodeEntry(artist, VITA_DIR_TYPE_MASK_ARTISTS, type); if(!updateAdjacencyList(ohfi, track_id)) { - db.rollback(); return 0; } } else { @@ -497,9 +597,8 @@ uint SQLiteDB::insertMusicEntry(const QString &path, int type, int parent) } if(albumartist) { - artist_id = insertObjectEntry(albumartist, type | OBJECT_ALBUM_ARTIST); + artist_id = insertNodeEntry(albumartist, VITA_DIR_TYPE_MASK_REGULAR, type); if(!updateAdjacencyList(ohfi, artist_id)) { - db.rollback(); return 0; } } else { @@ -507,18 +606,15 @@ uint SQLiteDB::insertMusicEntry(const QString &path, int type, int parent) } if(album) { - album_id = insertObjectEntry(album, type | OBJECT_ALBUM); + album_id = insertNodeEntry(album, VITA_DIR_TYPE_MASK_ALBUMS, type); if(track_id && !updateAdjacencyList(ohfi, album_id)) { - db.rollback(); return 0; } if(track_id && !updateAdjacencyList(album_id, track_id)) { - db.rollback(); return 0; } if(artist_id && !updateAdjacencyList(album_id, artist_id)) { - db.rollback(); return 0; } } else { @@ -544,15 +640,13 @@ uint SQLiteDB::insertMusicEntry(const QString &path, int type, int parent) query.bindValue(11, track_number); if(!query.exec()) { - db.rollback(); return 0; } - db.commit(); return ohfi; } -uint SQLiteDB::insertVideoEntry(const QString &path, int type, int parent) +uint SQLiteDB::insertVideoEntry(const QString &path, const QString &name, int parent, int type) { int ohfi; AVDecoder decoder; @@ -560,13 +654,13 @@ uint SQLiteDB::insertVideoEntry(const QString &path, int type, int parent) int file_format, parental_level, width, height, video_codec, video_bitrate, audio_codec, audio_bitrate; const char *explanation, *copyright, *title; - if(!decoder.open(path) || !decoder.loadCodec(AVDecoder::CODEC_VIDEO)) { + if(!decoder.open(path + "/" + name) || !decoder.loadCodec(AVDecoder::CODEC_VIDEO)) { return 0; } - int file_type = checkFileType(path, VITA_OHFI_VIDEO); + int file_type = checkFileType(name, VITA_OHFI_VIDEO); if(file_type < 0) { - qDebug() << "Excluding from database:" << path; + //qDebug() << "Excluding from database:" << path; return 0; } @@ -588,24 +682,12 @@ uint SQLiteDB::insertVideoEntry(const QString &path, int type, int parent) audio_bitrate = 0; } - QByteArray basename = QFileInfo(path).baseName().toUtf8(); + QByteArray basename = QFileInfo(name).baseName().toUtf8(); //title = decoder.getMetadataEntry("title", basename.constData()); title = basename.constData(); - db.transaction(); - if((ohfi = insertObjectEntry(title, type)) == 0) { - db.rollback(); - return 0; - } - - if(parent && !updateAdjacencyList(ohfi, parent)) { - db.rollback(); - return 0; - } - - if(!insertSourceEntry(ohfi, path)) { - db.rollback(); + if((ohfi = insertDefaultEntry(path, name, title, parent, type)) == 0) { return 0; } @@ -629,53 +711,38 @@ uint SQLiteDB::insertVideoEntry(const QString &path, int type, int parent) if(!query.exec()) { qDebug() << query.lastError().text(); - db.rollback(); return 0; } - db.commit(); return ohfi; } -uint SQLiteDB::insertPhotoEntry(const QString &path, int type, int parent) +uint SQLiteDB::insertPhotoEntry(const QString &path, const QString &name, int parent, int type) { int ohfi; QImage img; uint date_created; int width, height, file_format, photo_codec; - int file_type = checkFileType(path, VITA_OHFI_PHOTO); + int file_type = checkFileType(name, VITA_OHFI_PHOTO); if(file_type < 0) { - qDebug() << "Excluding from database:" << path; + //qDebug() << "Excluding from database:" << path; return 0; } - if(!img.load(path)) { + if(!img.load(path + "/" + name)) { return 0; } - date_created = QFileInfo(path).created().toTime_t(); + date_created = QFileInfo(path + "/" + name).created().toTime_t(); width = img.width(); height = img.height(); file_format = photo_list[file_type].file_format; photo_codec = photo_list[file_type].file_codec; - QByteArray basename = QFileInfo(path).baseName().toUtf8(); + QByteArray basename = QFileInfo(name).baseName().toUtf8(); - db.transaction(); - - if((ohfi = insertObjectEntry(basename.constData(), type)) == 0) { - db.rollback(); - return 0; - } - - if(parent && !updateAdjacencyList(ohfi, parent)) { - db.rollback(); - return 0; - } - - if(!insertSourceEntry(ohfi, path)) { - db.rollback(); + if((ohfi = insertDefaultEntry(path, name, basename, parent, type)) == 0) { return 0; } @@ -692,15 +759,13 @@ uint SQLiteDB::insertPhotoEntry(const QString &path, int type, int parent) query.bindValue(5, height); if(!query.exec()) { - db.rollback(); return 0; } - db.commit(); return ohfi; } -uint SQLiteDB::insertSavedataEntry(const QString &path, int type, int parent) +uint SQLiteDB::insertSavedataEntry(const QString &path, const QString &name, int parent, int type) { int ohfi; SfoReader reader; @@ -709,39 +774,22 @@ uint SQLiteDB::insertSavedataEntry(const QString &path, int type, int parent) const char *savedata_detail = NULL; const char *savedata_directory = NULL; - QString file_name = QFileInfo(path).fileName(); + QString file_name = QFileInfo(name).fileName(); QByteArray utf8name = file_name.toUtf8(); - db.transaction(); - - if(file_name.endsWith(".sfo", Qt::CaseInsensitive) && reader.load(path)) { + if(file_name.endsWith(".sfo", Qt::CaseInsensitive) && reader.load(path + "/" + name)) { title = reader.value("TITLE", utf8name.constData()); savedata_detail = reader.value("SAVEDATA_DETAIL", ""); savedata_directory = reader.value("SAVEDATA_DIRECTORY", utf8name.constData()); - date_updated = QFileInfo(path).lastModified().toTime_t(); + date_updated = QFileInfo(path + "/" + name).lastModified().toTime_t(); } - if((ohfi = insertObjectEntry(title, type)) == 0) { - db.rollback(); - return 0; - } - - if(parent && !updateAdjacencyList(ohfi, parent)) { - db.rollback(); - return 0; - } - - if(!insertSourceEntry(ohfi, path)) { - db.rollback(); - return 0; - } - - if(!path.endsWith(".sfo", Qt::CaseInsensitive)) { + if((ohfi = insertDefaultEntry(path, name, title, parent, type)) == 0) { return 0; } if(!title) { - return 0; + return ohfi; } QSqlQuery query; @@ -756,36 +804,31 @@ uint SQLiteDB::insertSavedataEntry(const QString &path, int type, int parent) query.bindValue(4, date_updated); if(!query.exec()) { - db.rollback(); return 0; } - db.commit(); return ohfi; } -uint SQLiteDB::insertApplicationEntry(const QString &path, int type, int parent, int app_type) +uint SQLiteDB::insertApplicationEntry(const QString &path, const QString &name, int parent, int type, int app_type) { int ohfi; + QString title_id; - QString base_name = QFileInfo(path).baseName(); + if(name.endsWith(".sfo", Qt::CaseInsensitive)) { + QDir curdir(path + "/" + name); + if(curdir.cdUp() && curdir.cdUp()) { + title_id = QFileInfo(curdir.absolutePath()).fileName(); + } + } - db.transaction(); - - if((ohfi = insertObjectEntry(base_name.toUtf8().constData(), type)) == 0) { - db.rollback(); + if((ohfi = insertDefaultEntry(path, name, QFileInfo(name).fileName(), parent, type)) == 0) { return 0; } - if(parent && !updateAdjacencyList(ohfi, parent)) { - db.rollback(); - return 0; - } - - if(!insertSourceEntry(ohfi, path)) { - db.rollback(); - return 0; + if(title_id.isNull()) { + return ohfi; } QSqlQuery query; @@ -794,81 +837,341 @@ uint SQLiteDB::insertApplicationEntry(const QString &path, int type, int parent, "VALUES (:object_id, :title, :app_type)"); query.bindValue(0, ohfi); - query.bindValue(1, base_name); + query.bindValue(1, title_id); query.bindValue(2, app_type); if(!query.exec()) { - db.rollback(); return 0; } - db.commit(); return ohfi; } - - -bool SQLiteDB::getObjectMetadata(int ohfi, metadata_t &metadata) -{ - return false; -} - int SQLiteDB::childObjectCount(int parent_ohfi) { - return 0; + QSqlQuery query; + query.prepare("SELECT count(child_id) FROM adjacent_objects WHERE parent_id = :object_id"); + query.bindValue(0, parent_ohfi); + if(!query.exec() || !query.next()) { + qDebug() << query.lastError(); + return -1; + } else { + return query.value(0).toInt(); + } } bool SQLiteDB::deleteEntry(int ohfi, int root_ohfi) { - return false; + Q_UNUSED(root_ohfi); + qDebug("Deleting node: %i", ohfi); + + QSqlQuery source_query("DELETE FROM sources WHERE object_id = :object_id"); + source_query.bindValue(0, ohfi); + + db.transaction(); + if(!source_query.exec()) { + db.rollback(); + return false; + } + + QSqlQuery object_query("DELETE FROM object_node WHERE object_id = :object_id"); + object_query.bindValue(0, ohfi); + if(!object_query.exec()) { + db.rollback(); + return false; + } + + db.commit(); + return true; +} + +void SQLiteDB::fillMetadata(const QSqlQuery &query, metadata_t &metadata) +{ + metadata.ohfi = query.value("ohfi").toInt(); + metadata.ohfiParent = query.value("parent").toInt(); + metadata.name = strdup(query.value("name").toByteArray().constData()); + metadata.path = strdup(query.value("path").toByteArray().constData()); + metadata.type = VITA_DIR_TYPE_MASK_REGULAR; + metadata.dataType = (DataType)query.value("data_type").toInt(); + metadata.size = query.value("size").toULongLong(); + metadata.dateTimeCreated = query.value("date_created").toInt(); + metadata.next_metadata = NULL; +} + +bool SQLiteDB::getObjectMetadata(int ohfi, metadata_t &metadata) +{ + QSqlQuery query( + "SELECT " + "t0.object_id as ohfi," + "t1.parent_id as parent," + "t2.path as path," + "t0.title as name," + "t0.type as type," + "t0.data_type as data_type," + "t2.size as size," + "t2.date_created as date_created " + "FROM object_node t0 " + "JOIN adjacent_objects t1 ON t1.child_id = t0.object_id " + "JOIN sources t2 ON t2.object_id = t0.object_id " + "WHERE t0.object_id = :object_id"); + query.bindValue(0, ohfi); + + if(query.exec() && query.next()) { + fillMetadata(query, metadata); + return true; + } else { + qDebug() << query.lastError(); + return false; + } } int SQLiteDB::getObjectMetadatas(int parent_ohfi, metadata_t **metadata, int index, int max_number) { - return 0; + if(metadata == NULL) { + return childObjectCount(parent_ohfi); + } + + int count = 0; + QString query_str( + "SELECT " + "t0.child_id as ohfi," + "t0.parent_id as parent," + "t2.path as path," + "t1.title as name," + "t1.type as type," + "t1.data_type as data_type," + "t2.size as size," + "t2.date_created as date_created " + "FROM adjacent_objects t0 " + "JOIN object_node t1 ON t0.child_id = t1.object_id " + "JOIN sources t2 ON t0.child_id = t2.object_id " + "WHERE t0.parent_id = :parent_id "); + + if(max_number > 0) { + query_str += "LIMIT :limit OFFSET :offset"; + } + + QSqlQuery query(query_str); + query.bindValue(0, parent_ohfi); + + if(max_number > 0) { + query.bindValue(1, max_number); + query.bindValue(2, index); + } + + if(query.exec()) { + metadata_t **last = &*metadata; + while(query.next()) { + metadata_t *meta = new metadata_t(); + fillMetadata(query, *meta); + *last = meta; + last = &meta->next_metadata; + count++; + } + } else { + qDebug() << query.lastError(); + } + return count; } qint64 SQLiteDB::getObjectSize(int ohfi) { - return -1; + if(ohfi < OHFI_BASE_VALUE) { + return getChildenTotalSize(ohfi); + } + + QSqlQuery query; + query.prepare("SELECT size FROM sources WHERE object_id = :object_id"); + query.bindValue(0, ohfi); + if(!query.exec() || !query.next()) { + qDebug() << query.lastError(); + return -1; + } else { + return query.value(0).toInt(); + } } int SQLiteDB::getPathId(const char *name, int ohfi) { - return 0; -} + //FIXME: use ohfi to filter by category + Q_UNUSED(ohfi); + QSqlQuery query; -int SQLiteDB::insertObjectEntry(const QString &path, int parent_ohfi) -{ - return 0; + query.prepare("SELECT object_id FROM sources WHERE path = :path"); + query.bindValue(0, name); + if(!query.exec() || !query.next()) { + qDebug() << query.lastError(); + return 0; + } else { + return query.value(0).toInt(); + } } QString SQLiteDB::getAbsolutePath(int ohfi) { - return NULL; + int root_ohfi = ohfi < OHFI_BASE_VALUE ? ohfi : getRootId(ohfi); + QString base_path = getBasePath(root_ohfi); + QString rel_path = getRelativePath(ohfi); + return rel_path.isNull() ? base_path : base_path + "/" + rel_path; } QString SQLiteDB::getRelativePath(int ohfi) { - return NULL; + QSqlQuery query; + query.prepare("SELECT path FROM sources WHERE object_id = :object_id"); + query.bindValue(0, ohfi); + if(!query.exec() || !query.next()) { + qDebug() << query.lastError(); + return NULL; + } else { + return query.value(0).toString(); + } +} + +bool SQLiteDB::updateObjectPath(int ohfi, const QString &name) +{ + int parent_ohfi = getParentId(ohfi); + QString parent_path, file; + + if(name.isNull()) { + QSqlQuery name_query("SELECT title FROM object_node WHERE object_id = :object_id"); + name_query.bindValue(0, ohfi); + if(!name_query.exec() || !name_query.next()) { + return false; + } + file = name_query.value(0).toString(); + } else { + file = name; + } + + if(parent_ohfi >= OHFI_BASE_VALUE) { + parent_path = getRelativePath(parent_ohfi); + if(parent_path.isNull()) { + parent_path = file; + } else { + parent_path += "/" + file; + } + } else { + parent_path = file; + } + + QSqlQuery query("UPDATE sources SET path = :path WHERE object_id = :object_id"); + query.bindValue(0, parent_path); + query.bindValue(1, ohfi); + + if(!query.exec()) { + return false; + } + + DataType type = (DataType)getObjectType(ohfi); + + if(type & Folder) { + QSqlQuery child_query("SELECT child_id, data_type FROM adjacent_objects " + "JOIN object_node ON object_id = child_id" + "WHERE parent_id = :parent_id"); + child_query.bindValue(0, ohfi); + + if(query.exec()) { + while(query.next()) { + int child_ohfi = query.value(0).toInt(); + if(!updateObjectPath(child_ohfi, NULL)) { + return false; + } + } + } + } + + return true; } bool SQLiteDB::renameObject(int ohfi, const QString &name) { - return false; + QSqlQuery query("UPDATE object_node SET title = :title WHERE object_id = :object_id"); + query.bindValue(0, name); + query.bindValue(1, ohfi); + + if(!query.exec()) { + return false; + } + + return updateObjectPath(ohfi, name); } void SQLiteDB::setObjectSize(int ohfi, qint64 size) { + QSqlQuery query; + query.prepare("UPDATE sources SET size = :size WHERE object_id = :object_id"); + query.bindValue(0, size); + query.bindValue(1, ohfi); + if(!query.exec()) { + qDebug() << query.lastError(); + } +} +qint64 SQLiteDB::getChildenTotalSize(int ohfi) +{ + QSqlQuery query; + query.prepare("SELECT SUM(t0.size) FROM sources t0 " + "JOIN adjacent_objects t1 ON t0.object_id = t1.child_id " + "where t1.parent_id = :parent_id"); + query.bindValue(0, ohfi); + if(!query.exec() || !query.next()) { + qDebug() << query.lastError(); + return -1; + } else { + return query.value(0).toLongLong(); + } } int SQLiteDB::getRootId(int ohfi) { - return 0; + QSqlQuery query; + int root_ohfi = ohfi; + + query.prepare("SELECT parent_id FROM adjacent_objects WHERE child_id = :child_id"); + while(root_ohfi >= OHFI_BASE_VALUE) { + query.bindValue(0, root_ohfi); + if(!query.exec() || !query.next()) { + qDebug() << query.lastError(); + root_ohfi = 0; + } else { + root_ohfi = query.value(0).toInt(); + } + } + return root_ohfi; +} + +int SQLiteDB::getObjectType(int ohfi) +{ + QSqlQuery query; + query.prepare("SELECT type FROM object_node WHERE object_id = :object_id"); + query.bindValue(0, ohfi); + if(!query.exec() || !query.next()) { + qDebug() << query.lastError(); + return 0; + } else { + return query.value(0).toInt(); + } } int SQLiteDB::getParentId(int ohfi) { - return 0; + QSqlQuery query; + query.prepare("SELECT parent_id FROM adjacent_objects WHERE child_id = :child_id"); + query.bindValue(0, ohfi); + if(!query.exec() || !query.next()) { + qDebug() << query.lastError(); + return 0; + } else { + return query.value(0).toInt(); + } +} + +void SQLiteDB::freeMetadata(metadata_t *metadata) +{ + while(metadata) { + metadata_t *current = metadata; + metadata = metadata->next_metadata; + delete current; + } } diff --git a/src/sqlitedb.h b/src/sqlitedb.h index 95ccf7f..c45cdf9 100644 --- a/src/sqlitedb.h +++ b/src/sqlitedb.h @@ -1,3 +1,22 @@ +/* + * QCMA: Cross-platform content manager assistant for the PS Vita + * + * Copyright (C) 2014 Codestation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #ifndef SQLITEDB_H #define SQLITEDB_H @@ -5,27 +24,11 @@ #include +#include #include #include #include - -#define OBJECT_FILE 0x10000000 -#define OBJECT_FOLDER 0x20000000 -#define OBJECT_SPECIAL 0x40000000 - -#define OBJECT_MUSIC 0x00000100 -#define OBJECT_PHOTO 0x00000200 -#define OBJECT_VIDEO 0x00000400 - -#define OBJECT_ALBUM 0x00000002 -#define OBJECT_ARTIST 0x00000005 -#define OBJECT_ALBUM_ARTIST 0x00000008 -#define OBJECT_GENRE 0x0000000B - -#define OBJECT_SAVEDATA 0x00040000 -#define OBJECT_SAVEDATA_FILE 0x00000002 - -#define OBJECT_APPLICATION 0x00080000 +#include class SQLiteDB : public Database { @@ -34,9 +37,16 @@ public: explicit SQLiteDB(QObject *parent = 0); ~SQLiteDB(); + bool load(); + bool rescan(); + void close(); + + bool reload(bool &prepared); + void setUUID(const QString &uuid); + bool open(); int create(); - void remove(); + void clear(); bool initialize(); QSqlError getLastError(); @@ -50,35 +60,40 @@ public: int getPathId(const char *name, int ohfi); QString getRelativePath(int ohfi); int getRootId(int ohfi); - int insertObjectEntry(const QString &path, int parent_ohfi); + int insertObjectEntry(const QString &path, const QString &name, int parent_ohfi); bool renameObject(int ohfi, const QString &name); void setObjectSize(int ohfi, qint64 size); + void freeMetadata(metadata_t *metadata); int getPathId(const QString &path); QString getPathFromId(int ohfi); bool updateSize(int ohfi, quint64 size); bool deleteEntry(int ohfi); - bool deleteEntry(const QString &path); - uint insertObjectEntry(const char *title, int type); - bool insertSourceEntry(uint object_id, const QString &path); - uint insertMusicEntry(const QString &path, int type, int parent); - uint insertVideoEntry(const QString &path, int type, int parent); - uint insertPhotoEntry(const QString &path, int type, int parent); - uint insertSavedataEntry(const QString &path, int type, int parent); - uint insertApplicationEntry(const QString &path, int type, int parent, int app_type); + bool insertSourceEntry(uint object_id, const QString &path, const QString &name); + uint insertMusicEntry(const QString &path, const QString &name, int parent, int type); + uint insertVideoEntry(const QString &path, const QString &name, int parent, int type); + uint insertPhotoEntry(const QString &path, const QString &name, int parent, int type); + uint insertSavedataEntry(const QString &path, const QString &name, int parent, int type); + uint insertApplicationEntry(const QString &path, const QString &name, int parent, int type, int app_type); private: - int recursiveScanRootDirectory(const QString &base_path, int parent, int type); - uint insertDirectoryEntry(const QString &path, int type, int parent); + int recursiveScanRootDirectory(const QString &base_path, const QString &rel_path, int parent_ohfi, int root_ohfi); + int insertObjectEntryInternal(const QString &path, const QString &name, int parent_ohfi, int type); + int insertDefaultEntry(const QString &path, const QString &name, const QString &title, int parent, int type); + int insertNodeEntry(const QString &title, int type, int data_type); bool updateAdjacencyList(int ohfi, int parent); + QString getBasePath(int root_ohfi); + int insertFileEntry(const QString &path, const QString &name, int parent, int parent_type); + int getObjectType(int ohfi); + void fillMetadata(const QSqlQuery &query, metadata_t &metadata); + qint64 getChildenTotalSize(int ohfi); + bool updateObjectPath(int ohfi, const QString &name); + + QTimer *timer; + QThread *thread; QString uuid; QSqlDatabase db; - -signals: - -public slots: - }; #endif // SQLITEDB_H