summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--clear.sql3
-rw-r--r--init.postgresql.sql31
-rw-r--r--music.cpp44
-rw-r--r--tag.cpp9
-rw-r--r--tag.h11
5 files changed, 94 insertions, 4 deletions
diff --git a/clear.sql b/clear.sql
new file mode 100644
index 0000000..b098e4c
--- /dev/null
+++ b/clear.sql
@@ -0,0 +1,3 @@
+DROP TABLE tracks;
+DROP TABLE albums;
+DROP TABLE artists;
diff --git a/init.postgresql.sql b/init.postgresql.sql
new file mode 100644
index 0000000..96ddca5
--- /dev/null
+++ b/init.postgresql.sql
@@ -0,0 +1,31 @@
+BEGIN;
+
+CREATE SEQUENCE artists_id_seq;
+CREATE TABLE artists (
+ id INTEGER PRIMARY KEY DEFAULT nextval('artists_id_seq'),
+ name VARCHAR UNIQUE);
+ALTER SEQUENCE artists_id_seq OWNED BY artists.id;
+
+CREATE SEQUENCE albums_id_seq;
+CREATE TABLE albums (
+ id INTEGER PRIMARY KEY DEFAULT nextval('albums_id_seq'),
+ artist_id INTEGER REFERENCES artists (id) NULL,
+ name VARCHAR,
+ tracks INTEGER,
+ UNIQUE(artist_id, name));
+ALTER SEQUENCE albums_id_seq OWNED BY albums.id;
+
+CREATE SEQUENCE tracks_id_seq;
+CREATE TABLE tracks (
+ id INTEGER PRIMARY KEY DEFAULT nextval('tracks_id_seq'),
+ artist_id INTEGER REFERENCES artists (id) NULL,
+ album_id INTEGER REFERENCES albums (id) NULL,
+ name VARCHAR,
+ length INTEGER,
+ num INTEGER,
+ file_name VARCHAR,
+ file_index INTEGER,
+ UNIQUE(artist_id, name));
+ALTER SEQUENCE tracks_id_seq OWNED BY tracks.id;
+
+COMMIT;
diff --git a/music.cpp b/music.cpp
index 15ddbba..f74de9d 100644
--- a/music.cpp
+++ b/music.cpp
@@ -87,9 +87,49 @@ void music::begin_update(const std::string path) {
}
void music::update(const MusicDirectory& dir) {
+ soci::session sql(config::vm["audist.database"].as<std::string>());
+
BOOST_FOREACH(fs::path t, dir.tracks) {
std::cout << "track " << t << std::endl;
+ Tag::p tag = Tag::load(t.string());
+ BOOST_FOREACH(Tag::Fields::value_type& f, tag->fields) {
+ std::cout << boost::format(" %s: %s") % f.first % f.second << std::endl;
+ }
+
+ int 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) RETURNING id", soci::use(tag->fields["artist"]), 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) RETURNING id",
+ soci::use(artist_id, ind), soci::use(tag->fields["album"]), 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(t.string());
+ }
+ }
}
+ sql.close();
+
std::for_each(dir.directories.begin(), dir.directories.end(), update);
}
@@ -128,10 +168,6 @@ MusicDirectory::MusicDirectory(const fs::path root) {
void MusicTrack::render(HTTP::Connection::p req) {
req->add_header("content-type", "application/octet-stream");
- // tag test
- Tag *t = new ID3Tag(path.string());
- delete t;
-
if(req->args.count("decoder") && req->args.count("encoder")) {
DecoderFilter::p d = decoder::get_decoder(req->args["decoder"]);
EncoderFilter::p e = encoder::get_encoder(req->args["encoder"]);
diff --git a/tag.cpp b/tag.cpp
index 833735a..f65470f 100644
--- a/tag.cpp
+++ b/tag.cpp
@@ -5,6 +5,15 @@
#include <cstdlib>
#include <stdexcept>
+// TODO: Make this a "smart" tag loader
+Tag::p Tag::load(const std::string filename) {
+ return Tag::p(new ID3Tag(filename));
+}
+
+bool Tag::has_field(const std::string name) {
+ return fields.find(name) != fields.end();
+}
+
void ID3Tag::tag_add_string(struct id3_tag *id3tag, const char *type, const char *id) {
struct id3_frame *frame = id3_tag_findframe(id3tag, id, 0);
diff --git a/tag.h b/tag.h
index 83a1f6d..e844500 100644
--- a/tag.h
+++ b/tag.h
@@ -1,13 +1,24 @@
#ifndef TAG_H
#define TAG_H
+#include <boost/shared_ptr.hpp>
+
#include <string>
#include <map>
class Tag {
public:
+ typedef boost::shared_ptr<Tag> p;
typedef std::map<std::string, std::string> Fields;
+
+ //! Tag fields.
Fields fields;
+
+ //! Generic tag loader.
+ static p load(const std::string filename);
+
+ //! Check for the existence of a specific field.
+ bool has_field(const std::string name);
};
class ID3Tag : public Tag {