2 * SomePlayer - An alternate music player for Maemo 5
3 * Copyright (C) 2010 Nikolay (somebody) Tischenko <niktischenko@gmail.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "dbstorage.h"
26 using namespace SomePlayer::Storage;
27 using namespace SomePlayer::DataObjects;
29 DbStorage::DbStorage(QString path) {
30 QString dbname = path + _DATABASE_NAME_;
31 db = QSqlDatabase::addDatabase("QSQLITE");
32 db.setDatabaseName(dbname);
34 // throw big exception
36 // create database structure
37 _create_database_structure();
41 void DbStorage::_prepare_queries() {
42 _get_artists_query = new QSqlQuery(db);
43 _get_artists_query->prepare("SELECT name FROM artist ORDER BY uname");
45 _get_albums_for_artist_sort_name_query = new QSqlQuery(db);
46 _get_albums_for_artist_sort_name_query->prepare("SELECT name, year FROM album WHERE artist_id in (SELECT id from artist WHERE uname = :uname) ORDER BY uname;");
48 _get_albums_for_artist_sort_year_query = new QSqlQuery(db);
49 _get_albums_for_artist_sort_year_query->prepare("SELECT name, year FROM album WHERE artist_id in (SELECT id from artist WHERE uname = :uname) ORDER BY year;");
51 _get_tracks_for_album_query = new QSqlQuery(db);
52 _get_tracks_for_album_query->prepare("SELECT id, title, source, count, length FROM tracks WHERE artist_id IN "
53 "(SELECT id FROM artist WHERE uname = :artist_uname) AND album_id IN "
54 "(SELECT id FROM album WHERE uname = :album_uname);");
56 _get_favorites_query = new QSqlQuery(db);
57 _get_favorites_query->prepare("SELECT track_id as id, title, artist, album.name as album, source, count, length FROM "
58 "(SELECT tracks.id AS track_id, artist.name AS artist, title, count, source, tracks.album_id, length FROM "
59 "tracks JOIN artist ON tracks.artist_id = artist.id) "
60 "JOIN album ON album_id = album.id WHERE track_id IN "
61 "(SELECT track_id FROM favorites);");
63 _get_most_played_query = new QSqlQuery(db);
64 _get_most_played_query->prepare("SELECT track_id as id, title, artist, album.name as album, source, count, length FROM "
65 "(SELECT tracks.id AS track_id, artist.name AS artist, title, count, source, tracks.album_id, length FROM "
66 "tracks JOIN artist ON tracks.artist_id = artist.id) "
67 "JOIN album ON album_id = album.id ORDER BY count DESC "
70 _get_never_played_query = new QSqlQuery(db);
71 _get_never_played_query->prepare("SELECT track_id as id, title, artist, album.name as album, source, count, length FROM "
72 "(SELECT tracks.id AS track_id, artist.name AS artist, title, count, source, tracks.album_id, length FROM "
73 "tracks JOIN artist ON tracks.artist_id = artist.id) "
74 "JOIN album ON album_id = album.id "
77 _get_recently_added_query = new QSqlQuery(db);
78 _get_recently_added_query->prepare("SELECT track_id as id, title, artist, album.name as album, source, count, length FROM "
79 "(SELECT tracks.id AS track_id, artist.name AS artist, title, count, source, tracks.album_id, length FROM "
80 "tracks JOIN artist ON tracks.artist_id = artist.id) "
81 "JOIN album ON album_id = album.id "
83 "(SELECT track_id FROM adding_date ORDER BY date DESC LIMIT 0, :max)");
85 _get_track_count_query = new QSqlQuery(db);
86 _get_track_count_query->prepare("SELECT count from tracks WHERE id = :id");
88 _get_tracks_by_pattern_query = new QSqlQuery(db);
89 _get_tracks_by_pattern_query->prepare("SELECT id, title, artist, album, source, count, length, year FROM "
91 "utitle LIKE (SELECT '%' || :ptitle || '%') "
92 "ORDER BY artist_uname, year");
94 _get_track_id_by_source_query = new QSqlQuery(db);
95 _get_track_id_by_source_query->prepare("SELECT id FROM tracks WHERE source = :source");
97 _get_directories_query = new QSqlQuery(db);
98 _get_directories_query->prepare("SELECT id, path FROM directories");
100 _get_artists_count_query = new QSqlQuery(db);
101 _get_artists_count_query->prepare("SELECT COUNT(id) AS cnt FROM artist");
103 _get_albums_count_query = new QSqlQuery(db);
104 _get_albums_count_query->prepare("SELECT COUNT(id) AS cnt FROM album");
106 _get_tracks_count_query = new QSqlQuery(db);
107 _get_tracks_count_query->prepare("SELECT COUNT(id) AS cnt FROM tracks");
109 _get_tracks_source_from_query = new QSqlQuery(db);
110 _get_tracks_source_from_query->prepare("SELECT source FROM tracks WHERE directory = :directory_id");
112 _check_artist_query = new QSqlQuery(db);
113 _check_artist_query->prepare("SELECT id FROM artist WHERE uname = :uname");
115 _check_album_query = new QSqlQuery(db);
116 _check_album_query->prepare("SELECT id FROM album WHERE uname = :uname AND artist_id = :artist_id");
118 _check_directory_query = new QSqlQuery(db);
119 _check_directory_query->prepare("SELECT id from directories WHERE path = :path");
121 _insert_artist_query = new QSqlQuery(db);
122 _insert_artist_query->prepare("INSERT INTO artist (name, uname) values (:name, :uname)");
124 _insert_album_query = new QSqlQuery(db);
125 _insert_album_query->prepare("INSERT INTO album (name, uname, artist_id, year) values (:name, :uname, :artist_id, :year)");
127 _insert_track_query = new QSqlQuery(db);
128 _insert_track_query->prepare("INSERT INTO tracks (title, utitle, artist_id, album_id, source, directory, length) values (:title, :utitle, :artist_id, :album_id, :source, :directory_id, :length)");
130 _insert_date_query = new QSqlQuery(db);
131 _insert_date_query->prepare("INSERT INTO adding_date (track_id, date) values (:track_id, strftime('%s', 'now'))");
133 _insert_favorites_query = new QSqlQuery(db);
134 _insert_favorites_query->prepare("INSERT INTO favorites (track_id) values (:track_id)");
136 _insert_directory_query = new QSqlQuery(db);
137 _insert_directory_query->prepare("INSERT INTO directories (path) values (:path)");
139 _update_track_count_query = new QSqlQuery(db);
140 _update_track_count_query->prepare("UPDATE tracks SET count = :count where id = :id");
142 _remove_track_query = new QSqlQuery(db);
143 _remove_track_query->prepare("DELETE FROM tracks WHERE id = :id");
145 _remove_empty_albums_query = new QSqlQuery(db);
146 _remove_empty_albums_query->prepare("DELETE FROM album WHERE album.id IN "
148 "(SELECT COUNT(tracks.id) AS cnt, album.id FROM "
149 "album LEFT OUTER JOIN tracks ON album.id = tracks.album_id "
150 "GROUP BY album.id) WHERE cnt = 0)");
152 _remove_empty_artists_query = new QSqlQuery(db);
153 _remove_empty_artists_query->prepare("DELETE FROM artist WHERE artist.id IN "
155 "(SELECT COUNT(tracks.id) AS cnt, artist.id FROM "
156 "artist LEFT OUTER JOIN tracks ON artist.id = tracks.artist_id "
157 "GROUP BY artist.id) WHERE cnt = 0)");
159 _remove_tracks_from_query = new QSqlQuery(db);
160 _remove_tracks_from_query->prepare("DELETE FROM tracks WHERE directory = :directory_id");
162 _remove_directory_query = new QSqlQuery(db);
163 _remove_directory_query->prepare("DELETE FROM directories WHERE path = :path");
165 _remove_track_from_favorites_query = new QSqlQuery(db);
166 _remove_track_from_favorites_query->prepare("DELETE FROM favorites WHERE track_id in (SELECT id FROM tracks WHERE source = :source)");
169 void DbStorage::_create_database_structure() {
170 QSqlQuery *query = new QSqlQuery(db);
171 query->exec("create table artist (id integer primary key, "
175 query->exec("create table album (id integer primary key, "
176 "artist_id integer, "
180 "foreign key(artist_id) references arist(id) "
182 query->exec("create table tracks (id integer primary key, "
183 "artist_id integer, "
188 "directory integer, "
189 "count integer default 0, "
190 "length integer default 0, "
191 "foreign key(artist_id) references artist(id), "
192 "foreign key(album_id) references album(id) "
194 query->exec("create table favorites (track_id integer, "
195 "foreign key(track_id) references tracks(id) "
197 query->exec("create table adding_date (track_id integer, "
199 "foreign key(track_id) references tracks(id) "
201 query->exec("create view entire as "
202 "select id, title, artist_name as artist, album_name as album, source, count, length, album_year as year, utitle, artist_uname, album_uname from "
204 "(select artist.id as aartist_id, "
205 "artist.uname as artist_uname, "
206 "artist.name as artist_name, "
207 "album.uname as album_uname, "
208 "album.year as album_year, "
209 "album.id as aalbum_id, "
210 "album.name as album_name from "
213 "on album.artist_id = artist.id) "
214 "on aartist_id = tracks.artist_id "
215 "and aalbum_id = tracks.album_id");
217 query->exec("create table directories (id integer primary key, path text)");
220 DbStorage::~DbStorage() {
221 delete _get_albums_for_artist_sort_name_query;
222 delete _get_albums_for_artist_sort_year_query;
223 delete _get_artists_query;
224 delete _get_favorites_query;
225 delete _get_most_played_query;
226 delete _get_never_played_query;
227 delete _get_recently_added_query;
228 delete _get_tracks_for_album_query;
229 delete _get_tracks_by_pattern_query;
230 delete _get_directories_query;
231 delete _get_artists_count_query;
232 delete _get_albums_count_query;
233 delete _get_tracks_count_query;
234 delete _get_tracks_source_from_query;
235 delete _check_album_query;
236 delete _check_artist_query;
237 delete _check_directory_query;
238 delete _get_track_id_by_source_query;
239 delete _insert_album_query;
240 delete _insert_artist_query;
241 delete _insert_date_query;
242 delete _insert_track_query;
243 delete _insert_favorites_query;
244 delete _insert_directory_query;
245 delete _update_track_count_query;
246 delete _remove_track_query;
247 delete _remove_directory_query;
248 delete _remove_tracks_from_query;
249 delete _remove_track_from_favorites_query;
253 QList<QString> DbStorage::getArtists() {
254 QList<QString> artists;
255 QSqlQuery *query = _get_artists_query;
257 while (query->next()) {
258 QString name = query->value(0).toString();
259 artists.append(name);
264 QMap<QString, int> DbStorage::getAlbumsForArtist(QString artist) {
265 QMap<QString, int> albums;
267 QString sort = config.getValue("ui/albumsorting").toString();
268 QSqlQuery *query = NULL;
269 if (sort == "date") {
270 query = _get_albums_for_artist_sort_year_query;
272 query = _get_albums_for_artist_sort_name_query;
274 query->bindValue(":uname", artist.toUpper());
276 while (query->next()) {
277 QString name = query->value(0).toString();
278 albums[name] = query->value(1).toInt();
283 QList<Track> DbStorage::getTracksForAlbum(QString album, QString artist) {
285 QSqlQuery *query = _get_tracks_for_album_query;
286 query->bindValue(":artist_uname", artist.toUpper());
287 query->bindValue(":album_uname", album.toUpper());
290 while (query->next()) {
291 QString title = query->value(1).toString();
292 QString source = query->value(2).toString();
293 int count = query->value(3).toInt();
294 int length = query->value(4).toInt();
295 TrackMetadata meta (title, artist, album, length);
296 Track track(meta, source);
297 track.setCount(count);
298 tracks.append(track);
304 Playlist DbStorage::getFavorites() {
306 QSqlQuery *query = _get_favorites_query;
308 while(query->next()) {
309 QString title = query->value(1).toString();
310 QString artist = query->value(2).toString();
311 QString album = query->value(3).toString();
312 QString source = query->value(4).toString();
313 int count = query->value(5).toInt();
314 int length = query->value(6).toInt();
315 TrackMetadata meta(title, artist, album, length);
316 Track track(meta, source);
317 track.setCount(count);
318 playlist.addTrack(track);
319 playlist.setName("Favorites");
324 Playlist DbStorage::getMostPlayed() {
326 QSqlQuery *query = _get_most_played_query;
327 query->bindValue(":max", _DYNAMIC_PLAYLIST_MAX_COUNT_);
329 while (query->next()) {
330 QString title = query->value(1).toString();
331 QString artist = query->value(2).toString();
332 QString album = query->value(3).toString();
333 QString source = query->value(4).toString();
334 int count = query->value(5).toInt();
335 int length = query->value(6).toInt();
336 TrackMetadata meta(title, artist, album, length);
337 Track track(meta, source);
338 track.setCount(count);
339 playlist.addTrack(track);
340 playlist.setName("Most popular");
345 Playlist DbStorage::getNeverPlayed() {
347 QSqlQuery *query = _get_never_played_query;
348 query->bindValue(":max", _DYNAMIC_PLAYLIST_MAX_COUNT_);
350 while (query->next()) {
351 QString title = query->value(1).toString();
352 QString artist = query->value(2).toString();
353 QString album = query->value(3).toString();
354 QString source = query->value(4).toString();
355 int count = query->value(5).toInt();
356 int length = query->value(6).toInt();
357 TrackMetadata meta(title, artist, album, length);
358 Track track(meta, source);
359 track.setCount(count);
360 playlist.addTrack(track);
361 playlist.setName("Never played");
366 Playlist DbStorage::getRecentlyAdded() {
368 QSqlQuery *query = _get_recently_added_query;
369 query->bindValue(":max", _DYNAMIC_PLAYLIST_MAX_COUNT_);
371 while (query->next()) {
372 QString title = query->value(1).toString();
373 QString artist = query->value(2).toString();
374 QString album = query->value(3).toString();
375 QString source = query->value(4).toString();
376 int count = query->value(5).toInt();
377 int length = query->value(6).toInt();
378 TrackMetadata meta(title, artist, album, length);
379 Track track(meta, source);
380 track.setCount(count);
381 playlist.addTrack(track);
382 playlist.setName("Recently added");
387 void DbStorage::removeTrack(Track track) {
388 QSqlQuery *query = _get_track_id_by_source_query;
389 query->bindValue(":source", track.source());
392 int id = query->value(0).toInt();
393 query = new QSqlQuery(db);
394 query->prepare("DELETE FROM tracks WHERE id = :id;");
395 query->bindValue(":id", id);
397 query->prepare("DELETE FROM favorites WHERE track_id = :id;");
398 query->bindValue(":id", id);
400 query->prepare("DELETE FROM adding_date WHERE id = :id;");
401 query->bindValue(":id", id);
406 void DbStorage::addTrack(Track track) {
407 QString title = track.metadata().title();
408 QString artist = track.metadata().artist();
409 QString album = track.metadata().album();
410 QString source = track.source();
411 QFileInfo info(source);
412 QString path = info.canonicalPath();
413 int year = track.metadata().year();
414 int artist_id = _check_add_artist(artist);
415 int album_id = _check_add_album(album, artist_id, year);
416 if (artist_id == -1 || album_id == -1) {
420 QSqlQuery *query = _get_track_id_by_source_query;
421 query->bindValue(":source", source);
424 // already in datebase, skip
427 query = _insert_track_query;
428 query->bindValue(":title", title);
429 query->bindValue(":utitle", title.toUpper());
430 query->bindValue(":artist_id", artist_id);
431 query->bindValue(":album_id", album_id);
432 query->bindValue(":source", source);
433 query->bindValue(":directory", _check_add_directory(path));
434 query->bindValue(":length", track.metadata().length());
437 query = _get_track_id_by_source_query;
438 query->bindValue(":source", source);
441 int id = query->value(0).toInt();
442 query = _insert_date_query;
443 query->bindValue(":track_id", id);
457 void DbStorage::addToFavorites(Track track) {
458 QSqlQuery *query = _get_track_id_by_source_query;
459 query->bindValue(":source", track.source());
462 int id = query->value(0).toInt();
463 query = _insert_favorites_query;
464 query->bindValue(":track_id", id);
469 void DbStorage::updateTrackCount(Track track) {
470 QSqlQuery *query = _get_track_id_by_source_query;
471 query->bindValue(":source", track.source());
474 int id = query->value(0).toInt();
475 query = _get_track_count_query;
476 query->bindValue(":id", id);
479 int count = query->value(0).toInt();
480 query = _update_track_count_query;
481 query->bindValue(":count", count+1);
482 query->bindValue(":id", id);
488 Track DbStorage::updateTrack(Track track) {
489 QSqlQuery *query = _get_track_id_by_source_query;
490 query->bindValue(":source", track.source());
492 if (query->next()) { // found track in library
493 int id = query->value(0).toInt();
494 query = _remove_track_query;
495 query->bindValue(":id", id);
503 int DbStorage::_check_add_artist(QString artist) {
504 QSqlQuery *query = _check_artist_query;
505 query->bindValue(":uname", artist.toUpper());
508 int id = query->value(0).toInt();
511 query = _insert_artist_query;
512 query->bindValue(":name", artist);
513 query->bindValue(":uname", artist.toUpper());
515 return _check_add_artist(artist);
523 int DbStorage::_check_add_album(QString album, int artist_id, int year) {
524 QSqlQuery *query = _check_album_query;
525 query->bindValue(":uname", album.toUpper());
526 query->bindValue(":artist_id", artist_id);
529 int id = query->value(0).toInt();
532 query = _insert_album_query;
533 query->bindValue(":name", album);
534 query->bindValue(":uname", album.toUpper());
535 query->bindValue(":artist_id", artist_id);
536 query->bindValue(":year", year);
538 return _check_add_album(album, artist_id, year);
546 QList<Track> DbStorage::searchTracks(QString pattern) {
548 QSqlQuery *query = _get_tracks_by_pattern_query;
549 query->bindValue(":ptitle", pattern.toUpper()); // with :pattern only doesn't work
551 // id, title, artist, album, source, count, length, year
552 while (query->next()) {
553 QString title = query->value(1).toString();
554 QString artist = query->value(2).toString();
555 QString album = query->value(3).toString();
556 QString source = query->value(4).toString();
557 int count = query->value(5).toInt();
558 int length = query->value(6).toInt();
559 int year = query->value(7).toInt();
560 TrackMetadata meta(title, artist, album, length);
562 Track track(meta, source);
563 track.setCount(count);
569 void DbStorage::_cleanup() {
570 _remove_empty_albums_query->exec();
571 _remove_empty_artists_query->exec();
574 int DbStorage::_check_add_directory(QString path) {
575 QSqlQuery *query = _check_directory_query;
576 query->bindValue(":path", path);
579 return query->value(0).toInt();
581 query = _insert_directory_query;
582 query->bindValue(":path", path);
584 return _check_add_directory(path);
588 QList<QString> DbStorage::getDirectories() {
589 QSqlQuery *query = _get_directories_query;
591 QList<QString> directories;
592 while (query->next()) {
593 directories.append(query->value(1).toString());
598 int DbStorage::getArtistsCount() {
599 QSqlQuery *query = _get_artists_count_query;
602 return query->value(0).toInt();
607 int DbStorage::getAlbumsCount() {
608 QSqlQuery *query = _get_albums_count_query;
611 return query->value(0).toInt();
616 int DbStorage::getTracksCount() {
617 QSqlQuery *query = _get_tracks_count_query;
620 return query->value(0).toInt();
625 void DbStorage::deleteTracksFrom(QString path) {
626 QSqlQuery *query = _check_directory_query;
627 query->bindValue(":path", path);
630 int id = query->value(0).toInt();
631 query = _remove_tracks_from_query;
632 query->bindValue(":directory_id", id);
634 query = _remove_directory_query;
635 query->bindValue(":path", path);
641 void DbStorage::checkTracksFrom(QString path) {
642 QSqlQuery *query = _check_directory_query;
643 query->bindValue(":path", path);
646 int id = query->value(0).toInt();
647 query = _get_tracks_source_from_query;
648 query->bindValue(":directory_id", id);
650 while (query->next()) {
651 QString source = query->value(0).toString();
652 if (!QFile::exists(source)) {
653 Track track(TrackMetadata(), source); // removeTrack uses only source field, so
654 removeTrack(track); // we can use this method
661 void DbStorage::removeFromFavorites(Track track) {
662 QSqlQuery *query = _remove_track_from_favorites_query;
663 query->bindValue(":source", track.source());