From b73c8b20a034a4e8ac9ab8740453e35160c20833 Mon Sep 17 00:00:00 2001 From: Jon Bergli Heier Date: Tue, 4 Jan 2011 02:10:27 +0100 Subject: Implemented music::update(), needs some work to make queries work on different engines. --- clear.sql | 3 +++ init.postgresql.sql | 31 +++++++++++++++++++++++++++++++ music.cpp | 44 ++++++++++++++++++++++++++++++++++++++++---- tag.cpp | 9 +++++++++ tag.h | 11 +++++++++++ 5 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 clear.sql create mode 100644 init.postgresql.sql 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()); + 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 #include +// 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 + #include #include class Tag { public: + typedef boost::shared_ptr p; typedef std::map 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 { -- cgit v1.2.3