summaryrefslogtreecommitdiff
path: root/database.cpp
blob: ea87e170b247d6963a2d453f2d1895822a23188f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include "database.h"
#include "config.h"

#include <boost/algorithm/string/join.hpp>
#include <boost/format.hpp>

Database::Database() {
	sql.open(config::vm["audist.database"].as<std::string>());
}

Database::~Database() {
	sql.close();
}

// NOTE: LIKE is case-insensitive by default on sqlite
std::vector<MusicListing::p> Database::find(std::map<std::string, std::string> search) {
	soci::session sql(config::vm["audist.database"].as<std::string>());
	soci::statement st(sql);
	std::string filename, artist, album, title;
	st.exchange(soci::into(filename));
	std::string query = "SELECT file_name FROM tracks WHERE ";
	std::vector<std::string> where_conditions;

	if(search.find("artist") != search.end()) {
		where_conditions.push_back("artist_id IN (SELECT id FROM artists WHERE name LIKE :artist)");
		artist = "%"+search["artist"]+"%";
		st.exchange(soci::use(artist, "artist"));
	}

	if(search.find("album") != search.end()) {
		where_conditions.push_back("album_id IN (SELECT id FROM albums WHERE name LIKE :album)");
		album = "%"+search["album"]+"%";
		st.exchange(soci::use(album, "album"));
	}

	if(search.find("title") != search.end()) {
		where_conditions.push_back("name LIKE :title");
		artist = "%"+search["title"]+"%";
		st.exchange(soci::use(title, "title"));
	}

	query += boost::algorithm::join(where_conditions, " AND ");
	st.alloc();
	st.prepare(query);
	st.define_and_bind();
	st.execute(true);

	std::vector<MusicListing::p> results;
	while(st.fetch()) {
		MusicListing::p ml(new MusicTrack(filename));
		results.push_back(ml);
	}
	sql.close();

	return results;
}

std::vector<MusicListing::p> Database::find(std::string search) {
	soci::session sql(config::vm["audist.database"].as<std::string>());

	search = "%"+search+"%";
	soci::rowset<std::string> rs = (sql.prepare << "SELECT DISTINCT file_name FROM tracks WHERE name LIKE :search OR file_name LIKE :search OR "
		"artist_id IN (SELECT id FROM artists WHERE name LIKE :search) OR album_id IN (SELECT id FROM albums WHERE name LIKE :search)",
		soci::use(search, "search"));

	std::vector<MusicListing::p> results;
	for(soci::rowset<std::string>::const_iterator it = rs.begin(); it != rs.end(); it++) {
		MusicListing::p ml(new MusicTrack(*it));
		results.push_back(ml);
	}

	return results;
}

// NOTE: last_insert_rowid() is sqlite-specific
void Database::update(fs::path track, Tag::p tag) {
	int64_t artist_id = 0, album_id = 0, track_id = 0;

	if(tag->has_field("artist")) {
		sql << "SELECT id FROM artists WHERE name = :name", soci::use(tag->fields["artist"]), soci::into(artist_id);
		if(!sql.got_data()) {
			sql << "INSERT INTO artists (name) VALUES (:name)", soci::use(tag->fields["artist"]);
			sql << "SELECT last_insert_rowid()", soci::into(artist_id);
		}
	}

	if(tag->has_field("album")) {
		std::string query(boost::str(boost::format("SELECT id FROM albums WHERE %s AND name = :name") %
					(artist_id ? boost::str(boost::format("artist_id = %d") % artist_id) : "artist_id IS NULL")));
		sql << query, soci::use(tag->fields["album"]), soci::into(album_id);
		if(!sql.got_data()) {
			soci::indicator ind = (artist_id ? soci::i_ok : soci::i_null);
			sql << "INSERT INTO albums (artist_id, name) VALUES (:artist_id, :name)", soci::use(artist_id, ind), soci::use(tag->fields["album"]);
			sql << "SELECT last_insert_rowid()", soci::into(album_id);
		}
	}

	if(tag->has_field("title")) {
		std::string query(boost::str(boost::format("SELECT id FROM tracks WHERE %s AND %s AND name = :name") %
					(artist_id ? boost::str(boost::format("artist_id = %d") % artist_id) : "artist_id IS NULL") %
					(album_id ? boost::str(boost::format("album_id = %d") % album_id) : "album_id IS NULL")));
		sql << query, soci::use(tag->fields["title"]), soci::into(track_id);
		if(!sql.got_data()) {
			soci::indicator artist_ind = (artist_id ? soci::i_ok : soci::i_null),
				album_ind = (album_id ? soci::i_ok : soci::i_null);
			sql << "INSERT INTO tracks (artist_id, album_id, name, file_name) VALUES (:artist_id, :album_id, :name, :file_name)",
				soci::use(artist_id, artist_ind), soci::use(album_id, album_ind), soci::use(tag->fields["title"]), soci::use(track.string());
		}
	}
}