]> git.sur5r.net Git - minitube/blob - src/database.cpp
Imported Upstream version 2.1.3
[minitube] / src / database.cpp
1 /* $BEGIN_LICENSE
2
3 This file is part of Minitube.
4 Copyright 2009, Flavio Tordini <flavio.tordini@gmail.com>
5
6 Minitube is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Minitube is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
18
19 $END_LICENSE */
20
21 #include "database.h"
22 #include "constants.h"
23 #include <QDesktopServices>
24
25 static const int DATABASE_VERSION = 1;
26 static const QString dbName = QLatin1String(Constants::UNIX_NAME) + ".db";
27 static Database *databaseInstance = 0;
28
29 Database::Database() {
30
31     QString dataLocation = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
32     QDir().mkpath(dataLocation);
33     dbLocation = dataLocation + "/" + dbName;
34
35     QMutexLocker locker(&lock);
36
37     if(QFile::exists(dbLocation)) {
38         // check db version
39         int databaseVersion = getAttribute("version").toInt();
40         if (databaseVersion != DATABASE_VERSION)
41             qWarning("Wrong database version: %d", databaseVersion);
42     } else createDatabase();
43 }
44
45 Database::~Database() {
46     closeConnections();
47 }
48
49 void Database::createDatabase() {
50
51     qWarning() << "Creating the database";
52
53     const QSqlDatabase db = getConnection();
54
55     QSqlQuery("create table subscriptions ("
56               "id integer primary key autoincrement,"
57               "user_id varchar,"
58               "user_name varchar,"
59               "name varchar,"
60               "description varchar,"
61               "thumb_url varchar,"
62               "country varchar,"
63               "added integer,"
64               "checked integer," // last check for videos on YT APIs
65               "updated integer," // most recent video added
66               "watched integer," // last time the user watched this channel
67               "loaded integer," // last time channel metadata was loaded from YT APIs
68               "notify_count integer," // new videos since "watched"
69               "views integer)" // number of times the user watched this channel
70               , db);
71     QSqlQuery("create unique index idx_user_id on subscriptions(user_id)", db);
72
73     QSqlQuery("create table subscriptions_videos ("
74               "id integer primary key autoincrement,"
75               "video_id varchar,"
76               "channel_id integer,"
77               "published integer,"
78               "added integer,"
79               "watched integer,"
80               "title varchar,"
81               "author varchar,"
82               "user_id varchar,"
83               "description varchar,"
84               "url varchar,"
85               "thumb_url varchar,"
86               "views integer,"
87               "duration integer)"
88               , db);
89     QSqlQuery("create unique index idx_video_id on subscriptions_videos(video_id)", db);
90
91     QSqlQuery("create table attributes (name varchar, value)", db);
92     QSqlQuery("insert into attributes (name, value) values ('version', "
93               + QString::number(DATABASE_VERSION) + ")", db);
94 }
95
96 QString Database::getDbLocation() {
97     static QString dataLocation = QDesktopServices::storageLocation(
98                 QDesktopServices::DataLocation);
99     return dataLocation + "/" + dbName;
100 }
101
102 bool Database::exists() {
103     static bool fileExists = false;
104     if (!fileExists)
105         fileExists = QFile::exists(getDbLocation());
106     return fileExists;
107 }
108
109 Database& Database::instance() {
110     static QMutex mutex;
111     QMutexLocker locker(&mutex);
112     if (!databaseInstance) databaseInstance = new Database();
113     return *databaseInstance;
114 }
115
116 QSqlDatabase Database::getConnection() {
117     QThread *currentThread = QThread::currentThread();
118     if (!currentThread) {
119         qDebug() << "current thread is null";
120         return QSqlDatabase();
121     }
122
123     const QString threadName = currentThread->objectName();
124     // qDebug() << "threadName" << threadName << currentThread;
125     if (connections.contains(currentThread)) {
126         return connections.value(currentThread);
127     } else {
128         // qDebug() << "Creating db connection for" << threadName;
129         QSqlDatabase connection = QSqlDatabase::addDatabase("QSQLITE", threadName);
130         connection.setDatabaseName(dbLocation);
131         if(!connection.open()) {
132             qWarning() << QString("Cannot connect to database %1 in thread %2").arg(dbLocation, threadName);
133         }
134         connections.insert(currentThread, connection);
135         return connection;
136     }
137 }
138
139 QVariant Database::getAttribute(QString name) {
140     QSqlQuery query("select value from attributes where name=?", getConnection());
141     query.bindValue(0, name);
142
143     bool success = query.exec();
144     if (!success) qDebug() << query.lastQuery() << query.boundValues().values() << query.lastError().text();
145     if (query.next())
146         return query.value(0);
147     return QVariant();
148 }
149
150 void Database::setAttribute(QString name, QVariant value) {
151     QSqlQuery query(getConnection());
152     query.prepare("update attributes set value=? where name=?");
153     query.bindValue(0, value);
154     query.bindValue(1, name);
155     bool success = query.exec();
156     if (!success) qDebug() << query.lastError().text();
157 }
158
159 /**
160   * After calling this method you have to reacquire a valid instance using instance()
161   */
162 void Database::drop() {
163     /// closeConnections();
164     if (!QFile::remove(dbLocation)) {
165         qWarning() << "Cannot delete database" << dbLocation;
166
167         // fallback to delete records in tables
168         const QSqlDatabase db = getConnection();
169         QSqlQuery query(db);
170         if (!query.exec("select name from sqlite_master where type='table'")) {
171             qWarning() << query.lastQuery() << query.lastError().text();
172         }
173
174         while (query.next()) {
175             QString tableName = query.value(0).toString();
176             if (tableName.startsWith("sqlite_") || tableName == "attributes") continue;
177             QString dropSQL = "delete from " + tableName;
178             QSqlQuery query2(db);
179             if (!query2.exec(dropSQL))
180                 qWarning() << query2.lastQuery() << query2.lastError().text();
181         }
182
183         query.exec("delete from sqlite_sequence");
184
185     }
186     if (databaseInstance) delete databaseInstance;
187     databaseInstance = 0;
188 }
189
190 void Database::closeConnections() {
191     foreach(QSqlDatabase connection, connections.values()) {
192         // qDebug() << "Closing connection" << connection;
193         connection.close();
194     }
195     connections.clear();
196 }
197
198 void Database::closeConnection() {
199     QThread *currentThread = QThread::currentThread();
200     if (!connections.contains(currentThread)) return;
201     QSqlDatabase connection = connections.take(currentThread);
202     // qDebug() << "Closing connection" << connection;
203     connection.close();
204 }
205
206 void Database::shutdown() {
207     if (!databaseInstance) return;
208     QSqlQuery("vacuum", databaseInstance->getConnection());
209     databaseInstance->closeConnections();
210 }