New base for the database backend.

This commit is contained in:
codestation
2014-01-17 01:57:21 -04:30
parent bafae021ed
commit c4961a8b15
5 changed files with 938 additions and 2 deletions

View File

@@ -7,7 +7,8 @@
QT += core \
gui \
widgets \
network
network \
sql
TARGET = qcma
@@ -31,6 +32,7 @@ SOURCES += src/main.cpp \
src/clientmanager.cpp \
src/filterlineedit.cpp \
src/dds.cpp \
src/sqlitedb.cpp \
# forms
src/forms/backupitem.cpp \
src/forms/backupmanagerform.cpp \
@@ -56,6 +58,7 @@ HEADERS += \
src/clientmanager.h \
src/filterlineedit.h \
src/dds.h \
src/sqlitedb.h \
# forms
src/forms/backupitem.h \
src/forms/backupmanagerform.h \

View File

@@ -28,7 +28,7 @@
AVDecoder::AvInit init;
AVDecoder::AVDecoder() :
pFormatCtx(NULL), file(NULL)
pFormatCtx(NULL), pCodecCtx(NULL), file(NULL)
{
}
@@ -95,6 +95,16 @@ bool AVDecoder::open(const QString filename)
return true;
}
const char *AVDecoder::getMetadataEntry(const char *key, const char *default_value)
{
AVDictionaryEntry *entry;
if((entry = av_dict_get(pFormatCtx->metadata, key, NULL, 0)) != NULL) {
return entry->value;
}
return default_value;
}
void AVDecoder::getAudioMetadata(metadata_t &metadata)
{
AVDictionaryEntry *entry;
@@ -164,6 +174,42 @@ void AVDecoder::getVideoMetadata(metadata_t &metadata)
}
}
int AVDecoder::getWidth()
{
return pCodecCtx->width;
}
int AVDecoder::getHeight()
{
return pCodecCtx->height;
}
int AVDecoder::getDuration()
{
return pFormatCtx->duration / 1000;
}
int AVDecoder::getBitrate()
{
return pFormatCtx->bit_rate;
}
int AVDecoder::getCodecBitrate()
{
return pCodecCtx->bit_rate;
}
bool AVDecoder::loadCodec(codec_type codec)
{
int stream_index = av_find_best_stream(pFormatCtx, (AVMediaType)codec, -1, -1, NULL, 0);
if(stream_index >= 0) {
pCodecCtx = pFormatCtx->streams[stream_index]->codec;
return true;
} else {
return false;
}
}
QByteArray AVDecoder::getAudioThumbnail(int width, int height)
{
QByteArray data;

View File

@@ -39,13 +39,23 @@ public:
AVDecoder();
~AVDecoder();
enum codec_type {CODEC_VIDEO = AVMEDIA_TYPE_VIDEO, CODEC_AUDIO = AVMEDIA_TYPE_AUDIO};
bool open(const QString filename);
void close();
bool loadCodec(codec_type codec);
QByteArray getAudioThumbnail(int width, int height);
QByteArray getVideoThumbnail(int width, int height);
void getAudioMetadata(metadata_t &metadata);
void getVideoMetadata(metadata_t &metadata);
const char *getMetadataEntry(const char *key, const char *default_value = NULL);
int getWidth();
int getHeight();
int getDuration();
int getBitrate();
int getCodecBitrate();
// simulate a static constructor to initialize libav only once
class AvInit
@@ -66,6 +76,7 @@ private:
static int64_t seekFunction(void* opaque, int64_t offset, int whence);
AVFormatContext *pFormatCtx;
AVCodecContext *pCodecCtx;
QFile *file;
};

810
src/sqlitedb.cpp Normal file
View File

@@ -0,0 +1,810 @@
#include "sqlitedb.h"
#include "sforeader.h"
#include "avdecoder.h"
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#include <QStandardPaths>
#else
#include <QDesktopServices>
#define QStandardPaths QDesktopServices
#define writableLocation storageLocation
#endif
#include <QSettings>
#include <QSqlQuery>
static const char create_adjacent[] = "CREATE TABLE IF NOT EXISTS adjacent_objects ("
"parent_id INTEGER NOT NULL REFERENCES object_node(object_id) ON DELETE CASCADE,"
"child_id INTEGER NOT NULL REFERENCES object_node(object_id) ON DELETE CASCADE)";
static const char create_obj_node[] = "CREATE TABLE IF NOT EXISTS object_node ("
"object_id INTEGER PRIMARY KEY AUTOINCREMENT,"
"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),"
"size INTEGER,"
"date_created TIMESTAMP,"
"date_modified TIMESTAMP)";
static const char create_music[] = "CREATE TABLE IF NOT EXISTS music ("
"object_id INTEGER PRIMARY KEY REFERENCES object_node(object_id) ON DELETE CASCADE,"
"file_format INTEGER,"
"audio_bitrate INTEGER,"
"audio_codec INTEGER,"
"duration INTEGER,"
"genre_id INTEGER REFERENCES object_node(object_id) ON DELETE SET NULL,"
"track_id INTEGER REFERENCES object_node(object_id) ON DELETE SET NULL,"
"artist_id INTEGER REFERENCES object_node(object_id) ON DELETE SET NULL,"
"album_id INTEGER REFERENCES object_node(object_id) ON DELETE SET NULL,"
"artist TEXT,"
"album TEXT,"
"track_number INTEGER)";
static const char create_photos[] = "CREATE TABLE IF NOT EXISTS photos ("
"object_id INTEGER PRIMARY KEY REFERENCES object_node(object_id) ON DELETE CASCADE,"
"date_created TIMESTAMP,"
"file_format INTEGER,"
"photo_codec INTEGER,"
"width INTEGER,"
"height INTEGER)";
static const char create_videos[] = "CREATE TABLE IF NOT EXISTS videos ("
"object_id INTEGER PRIMARY KEY REFERENCES object_node(object_id) ON DELETE CASCADE,"
"file_format INTEGER,"
"parental_level INTEGER,"
"explanation TEXT,"
"copyright TEXT,"
"width INTEGER,"
"height INTEGER,"
"video_codec INTEGER,"
"video_bitrate INTEGER,"
"audio_codec INTEGER,"
"audio_bitrate INTEGER,"
"duration INTEGER)";
static const char create_savedata[] = "CREATE TABLE IF NOT EXISTS savedata ("
"object_id INTEGER PRIMARY KEY REFERENCES object_node(object_id) ON DELETE CASCADE,"
"detail TEXT,"
"dir_name TEXT,"
"title TEXT,"
"date_updated TIMESTAMP)";
static const char create_trigger_node[] = "CREATE TRIGGER IF NOT EXISTS trg_objnode_deletechilds BEFORE DELETE ON object_node "
"FOR EACH ROW BEGIN "
"DELETE FROM object_node WHERE object_id IN "
"(SELECT child_id FROM adjacent_objects WHERE parent_id == OLD.object_id);"
"END";
static const char create_trigger_adjins[] = "CREATE TRIGGER IF NOT EXISTS trg_adjacentobjects_ins AFTER INSERT ON adjacent_objects "
"FOR EACH ROW BEGIN "
"UPDATE object_node SET child_count = child_count + 1 WHERE object_id = NEW.parent_id;"
"UPDATE object_node SET reference_count = reference_count + 1 WHERE object_id = NEW.child_id;"
"END";
static const char create_trigger_adjdel[] = "CREATE TRIGGER IF NOT EXISTS trg_adjacentobjects_del AFTER DELETE ON adjacent_objects "
"FOR EACH ROW BEGIN "
"UPDATE object_node SET child_count = child_count - 1 WHERE object_id = OLD.parent_id;"
"UPDATE object_node SET reference_count = reference_count - 1 WHERE object_id = OLD.child_id;"
"DELETE FROM object_node WHERE object_id = OLD.parent_id AND child_count <= 0;"
"DELETE FROM object_node WHERE object_id = OLD.child_id AND reference_count <= 0;"
"END";
typedef struct {
const char *file_ext;
int file_format;
int file_codec;
} file_type;
#define FILE_FORMAT_MP4 1
#define FILE_FORMAT_WAV 2
#define FILE_FORMAT_MP3 3
#define FILE_FORMAT_JPG 4
#define FILE_FORMAT_PNG 5
#define FILE_FORMAT_GIF 6
#define FILE_FORMAT_BMP 7
#define FILE_FORMAT_TIF 8
#define CODEC_TYPE_MPEG4 2
#define CODEC_TYPE_AVC 3
#define CODEC_TYPE_MP3 12
#define CODEC_TYPE_AAC 13
#define CODEC_TYPE_PCM 15
#define CODEC_TYPE_JPG 17
#define CODEC_TYPE_PNG 18
#define CODEC_TYPE_TIF 19
#define CODEC_TYPE_BMP 20
#define CODEC_TYPE_GIF 21
static const file_type audio_list[] = {
{"mp3", FILE_FORMAT_MP3, CODEC_TYPE_MP3},
{"mp4", FILE_FORMAT_MP4, CODEC_TYPE_AAC},
{"wav", FILE_FORMAT_WAV, CODEC_TYPE_PCM}
};
static const file_type photo_list[] = {
{"jpg", FILE_FORMAT_JPG, CODEC_TYPE_JPG},
{"jpeg", FILE_FORMAT_JPG, CODEC_TYPE_JPG},
{"png", FILE_FORMAT_PNG, CODEC_TYPE_PNG},
{"tif", FILE_FORMAT_TIF, CODEC_TYPE_TIF},
{"tiff", FILE_FORMAT_TIF, CODEC_TYPE_TIF},
{"bmp", FILE_FORMAT_BMP, CODEC_TYPE_BMP},
{"gif", FILE_FORMAT_GIF, CODEC_TYPE_GIF},
};
static const file_type video_list[] = {
{"mp4", FILE_FORMAT_MP4, 0}
};
static const char *table_list[] = {
create_adjacent, create_obj_node, create_sources,
create_music, create_photos, create_videos, create_savedata
};
static const char *trigger_list[] = {
create_trigger_node, create_trigger_adjins, create_trigger_adjdel
};
SQLiteDB::SQLiteDB(QObject *parent) :
QObject(parent)
{
uuid = QSettings().value("lastAccountId", "ffffffffffffffff").toString();
}
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();
QDir(QDir::root()).mkpath(db_path);
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(db_path + QDir::separator() + "qcma.sqlite");
return db.open();
}
void SQLiteDB::remove()
{
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();
}
bool SQLiteDB::initialize()
{
if (!db.isOpen()) {
return false;
}
QSqlQuery query;
for(unsigned int i = 0; i < sizeof(table_list) / sizeof(const char *); i++) {
if(!query.exec(table_list[i])) {
return false;
}
}
for(unsigned int i = 0; i < sizeof(trigger_list) / sizeof(const char *); i++) {
if(!query.exec(trigger_list[i])) {
return false;
}
}
// force object_id to start at 256
if(query.exec("INSERT INTO object_node (object_id, type) VALUES (255, 0)")) {
query.exec("DELETE FROM object_node WHERE object_id = 255");
}
return true;
}
QSqlError SQLiteDB::getLastError()
{
return db.lastError();
}
int SQLiteDB::checkFileType(const QString path, int ohfi_root)
{
switch(ohfi_root) {
case VITA_OHFI_MUSIC:
for(int i = 0, max = sizeof(audio_list) / sizeof(file_type); i < max; i++) {
if(path.endsWith(audio_list[i].file_ext, Qt::CaseInsensitive)) {
return i;
}
}
break;
case VITA_OHFI_PHOTO:
for(int i = 0, max = sizeof(photo_list) / sizeof(file_type); i< max; i++) {
if(path.endsWith(photo_list[i].file_ext, Qt::CaseInsensitive)) {
return i;
}
}
break;
case VITA_OHFI_VIDEO:
for(int i = 0, max = sizeof(video_list) / sizeof(file_type); i< max; i++) {
if(path.endsWith(video_list[i].file_ext, Qt::CaseInsensitive)) {
return i;
}
}
break;
}
return -1;
}
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;
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:
continue;
base_path = settings.value("appsPath").toString() + QDir::separator() + "SYSTEM" + QDir::separator() + uuid;
break;
case VITA_OHFI_VITAAPP:
continue;
base_path = settings.value("appsPath").toString() + QDir::separator() + "APP" + QDir::separator() + uuid;
break;
case VITA_OHFI_PSPAPP:
continue;
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:
continue;
base_path = settings.value("appsPath").toString() + QDir::separator() + "PSGAME" + QDir::separator() + uuid;
break;
case VITA_OHFI_PSMAPP:
continue;
base_path = settings.value("appsPath").toString() + QDir::separator() + "PSM" + QDir::separator() + uuid;
break;
}
int dir_count = recursiveScanRootDirectory(base_path, ohfi_array[i], OBJECT_FOLDER);
if(dir_count < 0) {
return -1;
}
total_objects += dir_count;
}
return total_objects;
}
int SQLiteDB::recursiveScanRootDirectory(const QString &base_path, int parent, int parent_type)
{
int total_objects = 0;
QDir dir(base_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;
}
total_objects++;
}
}
return total_objects;
}
int SQLiteDB::getPathId(const QString &path)
{
if (!db.isOpen()) {
return -1;
}
QSqlQuery query(QString("SELECT object_id from sources WHERE path = %1").arg(path));
if(query.next()) {
return query.value(0).toInt();
} else {
return -1;
}
}
QString SQLiteDB::getPathFromId(int ohfi)
{
if (!db.isOpen()) {
return QString();
}
QSqlQuery query(QString("SELECT path FROM sources WHERE object_id = %1").arg(ohfi));
if(query.next()) {
return query.value(0).toString();
} else {
return QString();
}
}
bool SQLiteDB::updateSize(int ohfi, quint64 size)
{
if (!db.isOpen()) {
return false;
}
QSqlQuery query(QString("UPDATE sources SET size = %1 WHERE object_id == %2").arg(size).arg(ohfi));
return query.exec();
}
bool SQLiteDB::deleteEntry(int ohfi)
{
QSqlQuery query(QString("DELETE FROM object_node WHERE object_id == %1").arg(ohfi));
return query.exec();
}
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;
query.prepare("SELECT * FROM adjacent_objects WHERE parent_id == :parent_id AND child_id == :child_id");
query.bindValue(0, parent);
query.bindValue(1, ohfi);
if(query.exec() && query.next()) {
return true;
}
query.prepare("INSERT INTO adjacent_objects (parent_id, child_id)"
"VALUES (:parentid, :child_id)");
query.bindValue(0, parent);
query.bindValue(1, ohfi);
return query.exec();
}
uint SQLiteDB::insertDirectoryEntry(const QString &path, int type, int parent)
{
uint ohfi;
db.transaction();
QString dirname = QFileInfo(path).fileName();
if((ohfi = insertObjectEntry(dirname.toUtf8().constData(), type)) == 0) {
db.rollback();
return 0;
}
if(parent && !updateAdjacencyList(ohfi, parent)) {
db.rollback();
return 0;
}
if(!insertSourceEntry(ohfi, path)) {
db.rollback();
return 0;
}
db.commit();
return ohfi;
}
uint SQLiteDB::insertObjectEntry(const char *title, int 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.bindValue(0, type);
query.bindValue(1, title);
if(!query.exec() || !query.exec("SELECT last_insert_rowid()") || !query.next()) {
return 0;
}
}
return query.value(0).toInt();
}
bool SQLiteDB::insertSourceEntry(uint object_id, const QString &path)
{
qint64 size;
uint date_created, date_modified;
QFileInfo info(path);
size = info.size();
date_created = info.created().toTime_t();
date_modified = 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(2, size);
query.bindValue(3, date_created);
query.bindValue(4, date_modified);
return query.exec();
}
uint SQLiteDB::insertMusicEntry(const QString &path, int type, int parent)
{
bool ok;
uint 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, type);
if(file_type < 0) {
qDebug() << "Excluding from database:" << path;
return 0;
}
if(!decoder.open(path)) {
return 0;
}
album = decoder.getMetadataEntry("album");
genre = decoder.getMetadataEntry("genre");
artist = decoder.getMetadataEntry("artist");
albumartist = decoder.getMetadataEntry("album_artist");
track = decoder.getMetadataEntry("track");
track_number = QString(track).toInt(&ok);
if(!ok) {
// set track number to 1 by default
track_number = 1;
}
audio_bitrate = decoder.getBitrate();
duration = decoder.getDuration();
file_format = audio_list[file_type].file_format;
audio_codec = audio_list[file_type].file_codec;
QByteArray basename = QFileInfo(path).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();
return 0;
}
if(genre) {
genre_id = insertObjectEntry(genre, type | OBJECT_GENRE);
if(!updateAdjacencyList(ohfi, genre_id)) {
db.rollback();
return 0;
}
} else {
genre_id = 0;
}
if(artist) {
track_id = insertObjectEntry(artist, type | OBJECT_ARTIST);
if(!updateAdjacencyList(ohfi, track_id)) {
db.rollback();
return 0;
}
} else {
track_id = 0;
}
if(albumartist) {
artist_id = insertObjectEntry(albumartist, type | OBJECT_ALBUM_ARTIST);
if(!updateAdjacencyList(ohfi, artist_id)) {
db.rollback();
return 0;
}
} else {
artist_id = 0;
}
if(album) {
album_id = insertObjectEntry(album, type | OBJECT_ALBUM);
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 {
album_id = 0;
}
QSqlQuery query;
query.prepare("REPLACE INTO music"
"(object_id, file_format, audio_codec, audio_bitrate, duration, genre_id, artist_id, album_id, track_id, artist, album, track_number)"
"VALUES (:object_id, :file_format, :audio_codec, :audio_bitrate, :duration, :genre_id, :artist_id, :album_id, :track_id, :artist, :album, :track_number)");
query.bindValue(0, ohfi);
query.bindValue(1, file_format);
query.bindValue(2, audio_codec);
query.bindValue(3, audio_bitrate);
query.bindValue(4, duration);
query.bindValue(5, genre_id ? genre_id : QVariant(QVariant::Int));
query.bindValue(6, artist_id ? artist_id : QVariant(QVariant::Int));
query.bindValue(7, album_id ? album_id : QVariant(QVariant::Int));
query.bindValue(8, track_id ? track_id : QVariant(QVariant::Int));
query.bindValue(9, artist);
query.bindValue(10, album);
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)
{
int ohfi;
AVDecoder decoder;
quint64 duration;
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)) {
return 0;
}
int file_type = checkFileType(path, type);
if(file_type < 0) {
qDebug() << "Excluding from database:" << path;
return 0;
}
parental_level = 0;
explanation = decoder.getMetadataEntry("comments", "");
copyright = decoder.getMetadataEntry("copyright", "");
width = decoder.getWidth();
height = decoder.getHeight();
duration = decoder.getDuration();
video_codec = 3;
video_bitrate = decoder.getCodecBitrate();
file_format = video_list[file_type].file_format;
if(decoder.loadCodec(AVDecoder::CODEC_AUDIO)) {
audio_codec = 13;
audio_bitrate = decoder.getCodecBitrate();
} else {
audio_codec = 0;
audio_bitrate = 0;
}
QByteArray basename = QFileInfo(path).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();
return 0;
}
QSqlQuery query;
query.prepare("REPLACE INTO videos"
"(object_id, file_format, parental_level, explanation, copyright, width, height, video_codec, video_bitrate, audio_codec, audio_bitrate, duration)"
"VALUES (:object_id, :file_format, :parental_level, :explanation, :copyright, :width, :height, :video_codec, :video_bitrate, :audio_codec, :audio_bitrate, :duration)");
query.bindValue(0, ohfi);
query.bindValue(1, file_format);
query.bindValue(2, parental_level);
query.bindValue(3, explanation);
query.bindValue(4, copyright);
query.bindValue(5, width);
query.bindValue(6, height);
query.bindValue(7, video_codec);
query.bindValue(8, video_bitrate);
query.bindValue(9, audio_codec);
query.bindValue(10, audio_bitrate);
query.bindValue(11, duration);
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)
{
int ohfi;
QImage img;
uint date_created;
int width, height, file_format, photo_codec;
int file_type = checkFileType(path, type);
if(file_type < 0) {
qDebug() << "Excluding from database:" << path;
return 0;
}
if(!img.load(path)) {
return 0;
}
date_created = QFileInfo(path).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();
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();
return 0;
}
QSqlQuery query;
query.prepare("REPLACE INTO photos"
"(object_id, date_created, file_format, photo_codec, width, height)"
"VALUES (:object_id, :date_created, :file_format, :photo_codec, :width, :height)");
query.bindValue(0, ohfi);
query.bindValue(1, date_created);
query.bindValue(2, file_format);
query.bindValue(3, photo_codec);
query.bindValue(4, width);
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)
{
int ohfi;
uint date_updated;
const char *title, *savedata_detail, *savedata_directory;
SfoReader reader;
QString base_name = QFileInfo(path).baseName();
QString sfo_file = QDir(path).absoluteFilePath("PARAM.SFO");
if(!reader.load(sfo_file)) {
return 0;
}
title = reader.value("TITLE", base_name.toUtf8().constData());
savedata_detail = reader.value("SAVEDATA_DETAIL", "");
savedata_directory = reader.value("SAVEDATA_DIRECTORY", base_name.toUtf8().constData());
date_updated = QFileInfo(sfo_file).lastModified().toTime_t();
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();
return 0;
}
QSqlQuery query;
query.prepare("REPLACE INTO savedata"
"(object_id, detail, dir_name, title, date_updated)"
"VALUES (:object_id, :detail, :dir_name, :title, :updated)");
query.bindValue(0, ohfi);
query.bindValue(1, savedata_detail);
query.bindValue(2, savedata_directory);
query.bindValue(3, title);
query.bindValue(4, date_updated);
if(!query.exec()) {
db.rollback();
return 0;
}
db.commit();
return ohfi;
}

66
src/sqlitedb.h Normal file
View File

@@ -0,0 +1,66 @@
#ifndef SQLITEDB_H
#define SQLITEDB_H
#include <vitamtp.h>
#include <QObject>
#include <QSqlDatabase>
#include <QSqlError>
#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
class SQLiteDB : public QObject
{
Q_OBJECT
public:
explicit SQLiteDB(QObject *parent = 0);
~SQLiteDB();
bool open();
int create();
void remove();
bool initialize();
QSqlError getLastError();
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);
private:
int recursiveScanRootDirectory(const QString &base_path, int parent, int type);
uint insertDirectoryEntry(const QString &path, int type, int parent);
int checkFileType(const QString path, int ohfi_root);
bool updateAdjacencyList(int ohfi, int parent);
QString uuid;
QSqlDatabase db;
signals:
public slots:
};
#endif // SQLITEDB_H