From 846272b3af9b9bf30f55912e051430830f126914 Mon Sep 17 00:00:00 2001 From: dougma Date: Tue, 28 Apr 2009 14:00:29 +0100 Subject: [PATCH 1/3] change boffin.sql and queries from file_tags to track_tags --- resolvers/boffin/BoffinDb.cpp | 8 ++++---- resolvers/boffin/BoffinDb.h | 21 +++++++++++---------- resolvers/boffin/RqlOpProcessor.cpp | 3 ++- resolvers/boffin/boffin.sql | 8 ++++---- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/resolvers/boffin/BoffinDb.cpp b/resolvers/boffin/BoffinDb.cpp index 71e6360..694d36e 100755 --- a/resolvers/boffin/BoffinDb.cpp +++ b/resolvers/boffin/BoffinDb.cpp @@ -16,8 +16,8 @@ BoffinDb::get_tag_cloud(int limit) { sqlite3pp::query qry(m_db, "SELECT name, sum(weight), count(weight) " - "FROM file_tag " - "INNER JOIN tag ON file_tag.tag = tag.rowid " + "FROM track_tag " + "INNER JOIN tag ON track_tag.tag = tag.rowid " "GROUP BY tag.rowid"); boost::shared_ptr p( new TagCloudVec() ); @@ -41,8 +41,8 @@ BoffinDb::get_all_artist_tags(BoffinDb::ArtistTagMap& out) { sqlite3pp::query qry(m_db, "SELECT artist, tag, avg(weight) " - "FROM file_tag " - "INNER JOIN pd.file_join ON file_tag.file = pd.file_join.file " + "FROM track_tag " + "INNER JOIN pd.file_join ON track_tag.file = pd.file_join.track " "GROUP BY artist, tag " "ORDER BY artist, tag "); diff --git a/resolvers/boffin/BoffinDb.h b/resolvers/boffin/BoffinDb.h index 398c8eb..3a4e474 100755 --- a/resolvers/boffin/BoffinDb.h +++ b/resolvers/boffin/BoffinDb.h @@ -28,16 +28,16 @@ class BoffinDb int get_tag_id(const std::string& tag, CreateFlag create = Create ); int get_artist_id(const std::string& artist); - // for each file without tags, call f(fileid, artist, album, track) + // for each track without tags, call f(track_id, artist_sortname, album_sortname, track_sortname) template void map_files_without_tags(Functor f) { sqlite3pp::query qry(m_db, - "SELECT pd.file_join.file, pd.artist.sortname, pd.album.sortname, pd.track.sortname FROM pd.file_join " + "SELECT pd.file_join.track, pd.artist.sortname, pd.album.sortname, pd.track.sortname FROM pd.file_join " "INNER JOIN pd.artist ON pd.file_join.artist = pd.artist.id " "LEFT JOIN pd.album ON pd.file_join.album = pd.album.id " "INNER JOIN pd.track ON pd.file_join.track = pd.track.id " - "WHERE pd.file_join.file NOT IN( SELECT file_tag.file FROM file_tag )"); + "WHERE pd.file_join.track NOT IN( SELECT track_tag.track FROM track_tag )"); for(sqlite3pp::query::iterator i = qry.begin(); i != qry.end(); ++i) { if (! f( i->get(0), i->get(1), i->get(2), i->get(3) ) ) { break; @@ -45,7 +45,7 @@ class BoffinDb } } - // Functor is: bool getResultLine(int& fileId, std::vector& tags) + // Functor is: bool getResultLine(int& trackId, std::vector& tags) template void update_tags(Functor getResultLine) { @@ -57,7 +57,7 @@ class BoffinDb std::vector tags; while (getResultLine( fileId, tags )) { BOOST_FOREACH(Tag& tag, tags) { - sqlite3pp::command cmd( m_db, "INSERT INTO file_tag (rowid, file, tag, weight) VALUES (null, ?, ?, ?)" ); + sqlite3pp::command cmd( m_db, "INSERT INTO track_tag (rowid, track, tag, weight) VALUES (null, ?, ?, ?)" ); cmd.bind(1, fileId); cmd.bind(2, get_tag_id( tag.first )); cmd.bind(3, tag.second); @@ -69,18 +69,19 @@ class BoffinDb xct.commit(); } - // Functor is: void onFile(int track, int artist, float weight) + // Functor is: void onFile(int file_id, int artist_id, float weight) template - void files_with_tag(const std::string& tag, Functor onFile) + void files_with_tag(const std::string& tag, float minWeight, Functor onFile) { int tagId = get_tag_id(tag, NoCreate); if (tagId > 0) { sqlite3pp::query qry( m_db, - "SELECT file_tag.file, artist, file_tag.weight FROM pd.file_join " - "INNER JOIN file_tag ON pd.file_join.file = file_tag.file " - "WHERE tag = ?"); + "SELECT pd.file_join.file, artist, track_tag.weight FROM pd.file_join " + "INNER JOIN track_tag ON pd.file_join.track = track_tag.track " + "WHERE tag = ? AND weight > ?"); qry.bind(1, tagId); + qry.bind(2, minWeight); for(sqlite3pp::query::iterator i = qry.begin(); i != qry.end(); ++i) { onFile( i->get(0), i->get(1), i->get(2) ); } diff --git a/resolvers/boffin/RqlOpProcessor.cpp b/resolvers/boffin/RqlOpProcessor.cpp index 93a40b7..6c95761 100755 --- a/resolvers/boffin/RqlOpProcessor.cpp +++ b/resolvers/boffin/RqlOpProcessor.cpp @@ -126,7 +126,8 @@ RqlOpProcessor::globalTag() { ResultSetPtr rs( new ResultSet() ); m_library.files_with_tag( - m_it->name, + m_it->name, + 0.05, boost::bind( &ResultSet::insertTrackResult, rs.get(), _1, _2, _3 ) ); normalise(m_it->weight, rs); return rs; diff --git a/resolvers/boffin/boffin.sql b/resolvers/boffin/boffin.sql index c9c4d6e..ca874c4 100755 --- a/resolvers/boffin/boffin.sql +++ b/resolvers/boffin/boffin.sql @@ -7,11 +7,11 @@ CREATE TABLE IF NOT EXISTS tag ( CREATE UNIQUE INDEX tag_name_idx ON tag(name); -CREATE TABLE IF NOT EXISTS file_tag ( - file INTEGER NOT NULL, +CREATE TABLE IF NOT EXISTS track_tag ( + track INTEGER NOT NULL, tag INTEGER NOT NULL, weight FLOAT NOT NULL ); -CREATE INDEX file_tag_track_idx ON file_tag(file); -CREATE INDEX file_tag_tag_idx ON file_tag(tag); +CREATE INDEX track_tag_track_idx ON track_tag(track); +CREATE INDEX track_tag_tag_idx ON track_tag(tag); From 7db5606ebc6c62a7e3c4418deb02ff1459a7a67d Mon Sep 17 00:00:00 2001 From: dougma Date: Tue, 28 Apr 2009 14:06:39 +0100 Subject: [PATCH 2/3] new lan/roster url returns json array of known hosts --- resolvers/lan/lan.cpp | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/resolvers/lan/lan.cpp b/resolvers/lan/lan.cpp index 5e0c714..af401bc 100644 --- a/resolvers/lan/lan.cpp +++ b/resolvers/lan/lan.cpp @@ -260,11 +260,10 @@ lan::handle_receive_from(const boost::system::error_code& error, rbs << "http://" << sender_endpoint_.address() << ":" - << sender_endpoint_.port(); - string url = rbs.str(); - url += "/sid/"; - url += rip->id(); - pip->set_url( url ); + << sender_endpoint_.port() + << "/sid/" + << rip->id(); + pip->set_url( rbs.str() ); } final_results.push_back( rip->get_json() ); m_pap->report_results( qid, final_results ); @@ -457,12 +456,37 @@ lan::receive_pang(map & om, m_lannodes.erase(from_name); } + +bool endsWith(const std::string& s, const std::string& tail) +{ + const std::string::size_type slen = s.length(), tlen = tail.length(); + return slen >= tlen ? (s.substr(slen - tlen) == tail) : false; +} + playdar_response lan::authed_http_handler(const playdar_request* req, playdar::auth* pauth) { cout << "request handler on lan for url: " << req->url() << endl; + time_t now; time(&now); + typedef std::pair LanPair; + + if (endsWith(req->url(), "roster")) { + Array a; + BOOST_FOREACH(const LanPair& p, m_lannodes) + { + Object o; + o.push_back( Pair("name", p.first) ); + o.push_back( Pair("address", p.second.http_base) ); + o.push_back( Pair("age", now - p.second.lastdate) ); + a.push_back(o); + } + ostringstream os; + write_formatted(a, os); + return playdar_response(os.str(), false); + } + ostringstream os; os << "

LAN

" << "

Detected nodes:" @@ -471,8 +495,7 @@ lan::authed_http_handler(const playdar_request* req, playdar::auth* pauth) << "Name Address Seconds since last ping" << "" << endl; - typedef std::pair pair_t; - BOOST_FOREACH( pair_t p, m_lannodes ) + BOOST_FOREACH( const LanPair& p, m_lannodes ) { os << "" << p.first << "" << ""<< p.second.http_base <<"" From ade4b46dd45b7c5e99bc4928532d7a733d60987a Mon Sep 17 00:00:00 2001 From: Jono Cole Date: Tue, 28 Apr 2009 14:07:44 +0100 Subject: [PATCH 3/3] consolidate specialized ResolvedItem types into a generic json backed ResolvedItem class. --- includes/playdar/application.h | 2 - includes/playdar/playable_item.hpp | 240 ------------------ includes/playdar/playdar_resolver_process.hpp | 1 - includes/playdar/pluginadaptor.h | 2 - includes/playdar/pluginadaptor_impl.hpp | 5 - includes/playdar/resolved_item.h | 91 ++++--- includes/playdar/resolved_item_builder.hpp | 47 ++++ includes/playdar/resolver.h | 16 +- includes/playdar/resolver_query.hpp | 4 +- includes/playdar/rs_http_playdar.hpp | 6 +- includes/playdar/track_rq_builder.hpp | 8 +- includes/playdar/types.h | 2 - resolvers/boffin/boffin.cpp | 74 ++---- resolvers/lan/lan.cpp | 4 +- src/library.cpp | 1 - src/playdar/playdar_request_handler.cpp | 31 ++- src/resolver.cpp | 38 +-- src/rs_local_library.cpp | 9 +- src/rs_script.cpp | 2 +- 19 files changed, 170 insertions(+), 413 deletions(-) delete mode 100644 includes/playdar/playable_item.hpp create mode 100644 includes/playdar/resolved_item_builder.hpp diff --git a/includes/playdar/application.h b/includes/playdar/application.h index 2cf2453..5641e8e 100644 --- a/includes/playdar/application.h +++ b/includes/playdar/application.h @@ -8,10 +8,8 @@ #include #include -#include "playdar/playable_item.hpp" #include "playdar/config.hpp" #include "playdar/types.h" -#include "playdar/playable_item.hpp" #define VERSION "0.1.0" //::: diff --git a/includes/playdar/playable_item.hpp b/includes/playdar/playable_item.hpp deleted file mode 100644 index 9903c1b..0000000 --- a/includes/playdar/playable_item.hpp +++ /dev/null @@ -1,240 +0,0 @@ -#ifndef __PLAYABLE_ITEM_H__ -#define __PLAYABLE_ITEM_H__ -#include "playdar/resolved_item.h" -#include "playdar/config.hpp" -#include "playdar/library.h" -#include "playdar/ss_curl.hpp" -#include "json_spirit/json_spirit.h" - -#include -#include -#include - -namespace playdar { - -/* - Represents something (a song) that can be played. - ResolverService plugins provide these as their results for matching content. - - The attached StreamingStrategy embodies how to access the song - this PlayableItem - could refer to a local file, or a remote file. Clients use the "sid" to access it. -*/ - -class PlayableItem : public ResolvedItem -{ -public: - PlayableItem() - : m_score( -1.0f ) - { - set_duration(0); - set_tracknum(0); - set_size(0); - set_bitrate(0); - set_mimetype("text/plain"); - set_source("unspecified"); - set_url(""); - } - - PlayableItem(std::string art, std::string alb, std::string trk) - : m_score( -1.0f ) - { - set_artist(art); - set_album(alb); - set_track(trk); - - set_duration(0); - set_tracknum(0); - set_size(0); - set_bitrate(0); - set_mimetype("text/plain"); - set_source("unspecified"); - set_url(""); - } - - static pi_ptr create(Library& lib, int fid) - { - return create( lib.db(), fid ); - } - - static pi_ptr create( sqlite3pp::database* db, int fid ) - { - LibraryFile_ptr file( Library::file_from_fid( db, fid) ); - - pi_ptr pip( new PlayableItem() ); - pip->set_mimetype( file->mimetype ); - pip->set_size( file->size ); - pip->set_duration( file->duration ); - pip->set_bitrate( file->bitrate ); - // int m_tracknum; - // float m_score; - // string m_source; - artist_ptr artobj = Library::load_artist( db, file->piartid); - track_ptr trkobj = Library::load_track( db, file->pitrkid); - pip->set_artist(artobj->name()); - pip->set_track(trkobj->name()); - // album metadata kinda optional for now - if (file->pialbid) { - album_ptr albobj = Library::load_album(db, file->pialbid); - pip->set_album(albobj->name()); - } - pip->set_url( file->url ); - return pip; - } - - ~PlayableItem() - { - //std::cout << "dtor, playableitem: " << id() << std::endl; - } - - static bool is_valid_json( const json_spirit::Object& obj ) - { - std::map objMap; - obj_to_map( obj, objMap ); - if( objMap.find( "artist" ) == objMap.end()) - return false; - if( objMap.find( "track" ) == objMap.end()) - return false; - - return true; - } - - static boost::shared_ptr from_json( const json_spirit::Object& resobj ) - { - std::string artist, album, track, sid, source, mimetype, url; - int size = 0, bitrate = 0, duration = 0; - float score = -1; - - using namespace json_spirit; - std::map resobj_map; - obj_to_map(resobj, resobj_map); - - if(in_map_and_is_type( resobj_map, "artist")) - artist = resobj_map["artist"].get_str(); - - if(in_map_and_is_type( resobj_map, "album")) - album = resobj_map["album"].get_str(); - - if(in_map_and_is_type( resobj_map, "track")) - track = resobj_map["track"].get_str(); - - if(in_map_and_is_type( resobj_map, "sid")) - sid = resobj_map["sid"].get_str(); - - if(in_map_and_is_type( resobj_map, "source")) - source = resobj_map["source"].get_str(); - - if(in_map_and_is_type( resobj_map, "mimetype")) - mimetype= resobj_map["mimetype"].get_str(); - - if(in_map_and_is_type( resobj_map, "url")) - url = resobj_map["url"].get_str(); - - if(in_map_and_is_type( resobj_map, "size", int_type)) - size = resobj_map["size"].get_int(); - - if(in_map_and_is_type( resobj_map, "bitrate", int_type)) - bitrate = resobj_map["bitrate"].get_int(); - - if(in_map_and_is_type( resobj_map, "duration", int_type)) - duration= resobj_map["duration"].get_int(); - - if(resobj_map.find("score")!=resobj_map.end()) - { - if(resobj_map["score"].type() == int_type) - score = (float) (resobj_map["score"].get_int()); - else - if (resobj_map["score"].type() == real_type) - score = (float) (resobj_map["score"].get_real()); - else - score = (float) -1; - } - - if(!artist.length() && !track.length()) throw; - - boost::shared_ptr - pip( new PlayableItem(artist, album, track) ); - - if(sid.length()) pip->set_id(sid); - if(source.length()) pip->set_source(source); - if(mimetype.length()) pip->set_mimetype(mimetype); - if(url.length()) pip->set_url(url); - if(size) pip->set_size(size); - if(bitrate) pip->set_bitrate(bitrate); - if(duration) pip->set_duration(duration); - if(score >= 0) pip->set_score(score); - - return pip; - } - - void create_json( json_spirit::Object& j ) const - { - using namespace json_spirit; - j.push_back( Pair("sid", id()) ); - j.push_back( Pair("artist", artist()) ); - j.push_back( Pair("album", album()) ); - j.push_back( Pair("track", track()) ); - j.push_back( Pair("source", source()) ); - j.push_back( Pair("size", size()) ); - j.push_back( Pair("mimetype", mimetype()) ); - j.push_back( Pair("bitrate", bitrate()) ); - j.push_back( Pair("duration", duration()) ); - j.push_back( Pair("url", url()) ); - } - - void set_artist(std::string s) { m_artist = s; } - void set_album(std::string s) { m_album = s; } - void set_track(std::string s) { m_track = s; } - void set_mimetype(std::string s) { m_mimetype = s; } - void set_duration(int s) { m_duration = s; } - void set_tracknum(int s) { m_tracknum = s; } - void set_size(int s) { m_size = s; } - void set_bitrate(int s) { m_bitrate = s; } - void set_streaming_strategy(boost::shared_ptr s) { m_ss = s; } - - const std::string & artist() const { return m_artist; } - const std::string & album() const { return m_album; } - const std::string & track() const { return m_track; } - const std::string & mimetype() const { return m_mimetype; } - const int duration() const { return m_duration; } - const int bitrate() const { return m_bitrate; } - const int tracknum() const { return m_tracknum; } - const int size() const { return m_size; } - /// This returns what ss->get_instance() returns, which in some cases is a - /// shared_ptr to the same SS as is attached, if the SS is threadsafe. - /// in the case of curlSS, it's a copy, because curlSS is not threadsafe. - boost::shared_ptr streaming_strategy() const - { - if(m_ss) return m_ss->get_instance(); - // this returns a null shared_ptr: - return m_ss; - } - -private: - - inline static bool in_map_and_is_type( std::map& map, - const std::string& key, - json_spirit::Value_type t = json_spirit::str_type) - { - return map.find( key ) != map.end() && - map[key].type() == t; - } - - std::string m_artist; - std::string m_album; - std::string m_track; - std::string m_mimetype; - std::string m_url; - - int m_size; - int m_duration; - int m_bitrate; - int m_tracknum; - float m_score; - std::string m_source; - - boost::shared_ptr m_ss; -}; - -} // ns - -#endif diff --git a/includes/playdar/playdar_resolver_process.hpp b/includes/playdar/playdar_resolver_process.hpp index 02a8df8..8e82340 100644 --- a/includes/playdar/playdar_resolver_process.hpp +++ b/includes/playdar/playdar_resolver_process.hpp @@ -14,7 +14,6 @@ #include "json_spirit/json_spirit.h" #include "playdar/resolver_query.hpp" -#include "playdar/playable_item.hpp" #define MAXBUF 4096 // max size of msg payload diff --git a/includes/playdar/pluginadaptor.h b/includes/playdar/pluginadaptor.h index 0b7e2e3..a47e6b1 100644 --- a/includes/playdar/pluginadaptor.h +++ b/includes/playdar/pluginadaptor.h @@ -45,8 +45,6 @@ class PluginAdaptor virtual ResolverService * rs() const { return m_rs; } virtual const std::string hostname() const = 0; - virtual void register_resolved_item( const ri_validator&, const ri_generator& ) = 0; - unsigned int targettime() const { return m_targettime; } unsigned short weight() const { return m_weight; } const bool script() const { return m_script; } diff --git a/includes/playdar/pluginadaptor_impl.hpp b/includes/playdar/pluginadaptor_impl.hpp index 7740237..2337fc5 100644 --- a/includes/playdar/pluginadaptor_impl.hpp +++ b/includes/playdar/pluginadaptor_impl.hpp @@ -94,11 +94,6 @@ class PluginAdaptorImpl : public PluginAdaptor { return m_resolver->ri_from_json(obj); } - - virtual void register_resolved_item( const ri_validator& v, const ri_generator& g ) - { - return m_resolver->register_resolved_item( v, g ); - } private: mutable playdar::utils::uuid_gen m_uuidgen; diff --git a/includes/playdar/resolved_item.h b/includes/playdar/resolved_item.h index ee0c0b6..5d66957 100644 --- a/includes/playdar/resolved_item.h +++ b/includes/playdar/resolved_item.h @@ -12,61 +12,92 @@ class ResolvedItem { public: - ResolvedItem():m_score( -1.0f ) + ResolvedItem() { } + ResolvedItem( const json_spirit::Object& jsonobj ) + { + json_spirit::obj_to_map( jsonobj, m_jsonmap ); + } + virtual ~ResolvedItem(){}; json_spirit::Object get_json() const { using namespace json_spirit; - Object j; - j.push_back( Pair("_msgtype", "ri") ); - j.push_back( Pair("score", (double) m_score) ); - j.push_back( Pair("source", m_source) ); - create_json( j ); - return j; + Object o; + map_to_obj( m_jsonmap, o); + return o; } - const source_uid& id() const { return m_uuid; } - void set_id(const source_uid& s) { m_uuid = s; } + const source_uid id() const { return json_value( "sid", ""); } + void set_id(const source_uid& s) { set_json_value( "sid", s ); } - const float score() const { return m_score; } - const std::string& source() const { return m_source; } + void set_score( const double s ) { set_json_value( "score", s ); } + const float score() const { return json_value( "score", -1.0); } + const std::string source() const { return json_value( "source", "" ); } - void set_score(float s) - { - assert(s <= 1.0); - assert(s >= 0); - m_score = s; + virtual void set_url(const std::string& s) { m_jsonmap["url"] = s; } + virtual const std::string url() const { return json_value( "url", "" ); } + + template< typename T > + bool has_json_value( const std::string& s ) const + { + std::map< std::string, json_spirit::Value >::const_iterator i = + m_jsonmap.find( s ); + + return i != m_jsonmap.end() && + i->second.type() == json_type(); } - void set_source(const std::string& s) { m_source = s; } - virtual void set_url(const std::string& s) { m_url = s; } - virtual const std::string & url() const { return m_url; } + std::string json_value( const std::string& s, const char* def ) const + { + return json_value( s, std::string( def )); + } - //TODO: move this into PlayableItem somehow - virtual void set_streaming_strategy(boost::shared_ptr s){} - virtual boost::shared_ptr streaming_strategy() const + template< typename T > + T json_value( const std::string& s, const T& def ) const { - return boost::shared_ptr(); + if( !has_json_value(s) ) + return def; + + //Hmm ideally we can make use of the iterator from the has_json_value method... + std::map< std::string, json_spirit::Value >::const_iterator i = m_jsonmap.find( s ); + return i->second.get_value(); } -protected: - virtual void create_json( json_spirit::Object& ) const = 0; + template< typename T > + void set_json_value( const std::string& k, const T& v ) + { + m_jsonmap[k] = v; + } + + void set_source(const std::string& s) { m_jsonmap["source"] = s; } private: - float m_score; - std::string m_source; - std::string m_url; - - source_uid m_uuid; + std::map< std::string, json_spirit::Value > m_jsonmap; + + template< typename T > + static json_spirit::Value_type json_type(); + }; + +template<> +inline json_spirit::Value_type ResolvedItem::json_type() { return json_spirit::int_type; } + +template<> +inline json_spirit::Value_type ResolvedItem::json_type() { return json_spirit::str_type; } +template<> +inline json_spirit::Value_type ResolvedItem::json_type() { return json_spirit::real_type; } + +template<> +inline json_spirit::Value_type ResolvedItem::json_type() { return json_spirit::bool_type; } + } #endif //__RESOLVED_ITEM_H__ diff --git a/includes/playdar/resolved_item_builder.hpp b/includes/playdar/resolved_item_builder.hpp new file mode 100644 index 0000000..d605de3 --- /dev/null +++ b/includes/playdar/resolved_item_builder.hpp @@ -0,0 +1,47 @@ +#ifndef __RESOLVED_ITEM_BUILDER_H__ +#define __RESOLVED_ITEM_BUILDER_H__ +#include "playdar/resolved_item.h" +#include "playdar/library.h" + +namespace playdar { + +/* + Builds a ResolvedItem describing something (a song) that can be played. +*/ + +class ResolvedItemBuilder +{ +public: + + static ri_ptr createFromFid(Library& lib, int fid) + { + return createFromFid( lib.db(), fid ); + } + + static ri_ptr createFromFid( sqlite3pp::database* db, int fid ) + { + LibraryFile_ptr file( Library::file_from_fid( db, fid) ); + + ri_ptr rip( new ResolvedItem() ); + rip->set_json_value( "mimetype", file->mimetype ); + rip->set_json_value( "size", file->size ); + rip->set_json_value( "duration", file->duration ); + rip->set_json_value( "bitrate", file->bitrate ); + artist_ptr artobj = Library::load_artist( db, file->piartid); + track_ptr trkobj = Library::load_track( db, file->pitrkid); + rip->set_json_value( "artist", artobj->name()); + rip->set_json_value( "track", trkobj->name()); + // album metadata kinda optional for now + if (file->pialbid) { + album_ptr albobj = Library::load_album(db, file->pialbid); + rip->set_json_value( "album", albobj->name()); + } + rip->set_json_value( "url", file->url ); + return rip; + } + +}; + +} // ns + +#endif //__RESOLVED_ITEM_BUILDER_H__ diff --git a/includes/playdar/resolver.h b/includes/playdar/resolver.h index ebfd23a..7e78b22 100644 --- a/includes/playdar/resolver.h +++ b/includes/playdar/resolver.h @@ -9,7 +9,6 @@ #include "playdar/types.h" #include "playdar/resolver_query.hpp" #include "playdar/resolver_service.h" -#include "playdar/playable_item.hpp" #include @@ -32,12 +31,6 @@ class ResolverService; */ class Resolver { -private: - // validator and generator functions to pass to the resolver in - // order to generate the correct derived ResolvedItem type from json_spirit - typedef boost::function ri_validator; - typedef boost::function ri_generator; - public: Resolver(MyApplication * app); ~Resolver(); @@ -58,8 +51,7 @@ class Resolver bool add_new_query(boost::shared_ptr rq); void cancel_query(const query_uid & qid); void cancel_query_timeout(query_uid qid); - - void register_resolved_item( const ri_validator&, const ri_generator& ); + ri_ptr ri_from_json( const json_spirit::Object& ) const; rq_ptr rq(const query_uid & qid); @@ -102,8 +94,8 @@ class Resolver protected: float calculate_score( const rq_ptr & rq, // query - const pi_ptr & pi, // candidate - std::string & reason ); // fail reason + const ri_ptr & ri, // candidate + std::string & reason ); // fail reason private: void load_library_resolver(); @@ -141,8 +133,6 @@ class Resolver std::deque< std::pair > m_pending; boost::mutex m_mutex; boost::condition m_cond; - - std::vector > m_riList; // StreamingStrategy factories std::map< std::string, boost::function > m_ss_factories; diff --git a/includes/playdar/resolver_query.hpp b/includes/playdar/resolver_query.hpp index 9bcad6b..fde3f80 100644 --- a/includes/playdar/resolver_query.hpp +++ b/includes/playdar/resolver_query.hpp @@ -4,7 +4,7 @@ //#include "playdar/application.h" #include "playdar/types.h" #include "playdar/config.hpp" -#include "playdar/playable_item.hpp" +#include "playdar/resolved_item.h" #include "json_spirit/json_spirit.h" #include @@ -45,7 +45,7 @@ class ResolverQuery /// between playable item and the original request. /// @return score between 0-1 or == 0 if there's an error /// @param reason will be set to the fail reason. - virtual float calculate_score( const pi_ptr & pi, + virtual float calculate_score( const ri_ptr & pi, std::string & reason ) { return 1.0f; diff --git a/includes/playdar/rs_http_playdar.hpp b/includes/playdar/rs_http_playdar.hpp index d11c0f5..2b3aa47 100644 --- a/includes/playdar/rs_http_playdar.hpp +++ b/includes/playdar/rs_http_playdar.hpp @@ -94,14 +94,14 @@ class RS_http_playdar : public ResolverService //cout << "Proceeding.." << endl; } Array resultsA = rr["results"].get_array(); - vector< boost::shared_ptr > v; + vector< boost::shared_ptr > v; BOOST_FOREACH(Value & result, resultsA) { Object reso = result.get_obj(); - boost::shared_ptr pip; + boost::shared_ptr rip; try { - pip = PlayableItem::from_json(reso); + rip = new ResolvedItem( reso ); // make sure to use whichever ip:port we are actually talking to them via: string url = remote_httpbase(); url += "/sid/"; diff --git a/includes/playdar/track_rq_builder.hpp b/includes/playdar/track_rq_builder.hpp index e48b2c6..6501836 100644 --- a/includes/playdar/track_rq_builder.hpp +++ b/includes/playdar/track_rq_builder.hpp @@ -5,8 +5,7 @@ namespace playdar { -class TrackRQBuilder { -public: +namespace TrackRQBuilder { static rq_ptr build( const std::string& artist, const std::string& album, const std::string& track ) { rq_ptr rq = rq_ptr( new ResolverQuery ); @@ -27,9 +26,8 @@ class TrackRQBuilder { rq->param_type( "track" ) == json_spirit::str_type && rq->param( "track" ).get_str().length(); } - -}; +} //namespace TrackRQBuilder -} +} //namespace playdar #endif //__TRACK_RESOLVER_QUERY__ diff --git a/includes/playdar/types.h b/includes/playdar/types.h index bd2be40..e012bcd 100644 --- a/includes/playdar/types.h +++ b/includes/playdar/types.h @@ -22,11 +22,9 @@ typedef boost::shared_ptr album_ptr; typedef boost::shared_ptr LibraryFile_ptr; class ResolvedItem; -class PlayableItem; class ResolverQuery; class PluginAdaptor; class StreamingStrategy; -typedef boost::shared_ptr pi_ptr; typedef boost::shared_ptr ri_ptr; typedef boost::shared_ptr rq_ptr; typedef boost::shared_ptr pa_ptr; diff --git a/resolvers/boffin/boffin.cpp b/resolvers/boffin/boffin.cpp index df83afc..d5f8f60 100755 --- a/resolvers/boffin/boffin.cpp +++ b/resolvers/boffin/boffin.cpp @@ -11,6 +11,7 @@ #include "playdar/resolved_item.h" #include "playdar/library.h" #include "playdar/playdar_request.h" +#include "playdar/resolved_item_builder.hpp" #include "BoffinRQUtil.h" using namespace fm::last::query_parser; @@ -52,61 +53,20 @@ static RqlOp leaf2op( const querynode_data& node ) using namespace playdar; -class TagCloudItem : public ResolvedItem +namespace TagCloudItem { -public: - TagCloudItem(const std::string& name, float weight, int trackCount, const std::string& source) - :m_name(name) - ,m_weight(weight) - ,m_trackCount(trackCount) - { - set_score(m_weight); - set_source(source); - } - - void create_json(json_spirit::Object &o) const - { - using namespace json_spirit; - o.push_back( Pair("name", m_name) ); - o.push_back( Pair("count", m_trackCount) ); - } - static bool validator(const json_spirit::Object& o) + static ri_ptr createTagCloudItem(const std::string& name, float weight, int trackCount, const std::string& source) { - map m; - obj_to_map(o, m); - - if( m.find("name") != m.end() && m.find("name")->second.type() == json_spirit::str_type && - m.find("score") != m.end() && m.find("score")->second.type() == json_spirit::real_type && - m.find("count") != m.end() && m.find("count")->second.type() == json_spirit::int_type ) - { - return true; - } - - return false; - } + ri_ptr rip( new ResolvedItem ); + rip->set_score( weight ); + rip->set_json_value( "name", name ); + rip->set_json_value( "source", source ); + rip->set_json_value( "count", trackCount ); - static ri_ptr generator(const json_spirit::Object& o) - { - map m; - obj_to_map(o, m); - - // source is optional (?) - map::const_iterator it( m.find("source") ); - std::string source( it == m.end() ? "" : it->second.get_str() ); - - return ri_ptr(new TagCloudItem( - m.find("name")->second.get_str(), - m.find("score")->second.get_real(), - m.find("count")->second.get_int(), - source ) ); // source not always essential/present. + return rip; } - -private: - std::string m_name; - float m_weight; - int m_trackCount; }; @@ -149,7 +109,6 @@ boffin::init( pa_ptr pap ) m_db = boost::shared_ptr( new BoffinDb(boffinDb, playdarDb) ); m_sa = boost::shared_ptr( new SimilarArtists() ); - m_pap->register_resolved_item(&TagCloudItem::validator, &TagCloudItem::generator); return true; } @@ -225,11 +184,10 @@ boffin::start_resolving(boost::shared_ptr rq) static -boost::shared_ptr +ri_ptr makeTagCloudItem(const boost::tuple& in, const std::string& source) { - return boost::shared_ptr( - new TagCloudItem(in.get<0>(), in.get<1>(), in.get<2>(), source)); + return TagCloudItem::createTagCloudItem(in.get<0>(), in.get<1>(), in.get<2>(), source); } @@ -259,11 +217,11 @@ boffin::resolve(boost::shared_ptr rq) // look up results, turn them into a vector of json objects std::vector< json_spirit::Object > results; BOOST_FOREACH(const TrackResult& t, sa.get_results()) { - pi_ptr pip = PlayableItem::create( m_db->db(), t.trackId ); - pip->set_score( t.weight ); - pip->set_source( m_pap->hostname() ); - pip->set_id( m_pap->gen_uuid() ); - results.push_back( pip->get_json() ); + ri_ptr rip = playdar::ResolvedItemBuilder::createFromFid( m_db->db(), t.trackId ); + rip->set_score( t.weight ); + rip->set_source( m_pap->hostname() ); + rip->set_id( m_pap->gen_uuid() ); + results.push_back( rip->get_json() ); } m_pap->report_results(rq->id(), results); diff --git a/resolvers/lan/lan.cpp b/resolvers/lan/lan.cpp index 5e0c714..934d5c5 100644 --- a/resolvers/lan/lan.cpp +++ b/resolvers/lan/lan.cpp @@ -254,7 +254,7 @@ lan::handle_receive_from(const boost::system::error_code& error, if(!rip) break; //FIXME this could be moved into the PlayableItem class perhaps // you'd need to be able to pass endpoint information to resolver()->ri_from_json though. - if( pi_ptr pip = boost::dynamic_pointer_cast(rip)) + if( rip->has_json_value("url")) { ostringstream rbs; rbs << "http://" @@ -264,7 +264,7 @@ lan::handle_receive_from(const boost::system::error_code& error, string url = rbs.str(); url += "/sid/"; url += rip->id(); - pip->set_url( url ); + rip->set_url( url ); } final_results.push_back( rip->get_json() ); m_pap->report_results( qid, final_results ); diff --git a/src/library.cpp b/src/library.cpp index cb8e624..1c2e054 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -10,7 +10,6 @@ #include #include "playdar/application.h" -#include "playdar/playable_item.hpp" using namespace std; diff --git a/src/playdar/playdar_request_handler.cpp b/src/playdar/playdar_request_handler.cpp index c56441b..2dec3e3 100644 --- a/src/playdar/playdar_request_handler.cpp +++ b/src/playdar/playdar_request_handler.cpp @@ -488,22 +488,23 @@ playdar_request_handler::handle_queries( const playdar_request& req, int i = 0; BOOST_FOREACH(ri_ptr ri, results) { - pi_ptr pi = boost::dynamic_pointer_cast(ri); - if( !pi ) continue; + if( !ri->has_json_value( "artist" ) || + !ri->has_json_value( "track" )) + continue; bgc = ++i%2 ? "lightgrey" : ""; os << "" << "" - << "id() << "\">" - << pi->id() << "" - << "" << pi->artist() << "" - << "" << pi->album() << "" - << "" << pi->track() << "" - << "" << pi->duration() << "" - << "" << pi->bitrate() << "" - << "" << pi->size() << "" - << "" << pi->source() << "" - << "" << pi->score() << "" + << "id() << "\">" + << ri->id() << "" + << "" << ri->json_value("artist", "" ) << "" + << "" << ri->json_value("album", "" ) << "" + << "" << ri->json_value("track", "" ) << "" + << "" << ri->json_value("duration", "" )<< "" + << "" << ri->json_value("bitrate", "" ) << "" + << "" << ri->json_value("size", "" ) << "" + << "" << ri->json_value("source", "" ) << "" + << "" << ri->json_value("score", "" ) << "" << "" ; } @@ -562,16 +563,14 @@ playdar_request_handler::handle_quickplay( const playdar_request& req, if( !results.size() ) return; - pi_ptr result = boost::dynamic_pointer_cast(results[0]); - - if( !result) return; + if( results[0]->json_value( "url", "" ).empty()) return; json_spirit::Object ro = results[0]->get_json(); cout << "Top result:" <id(); + url += results[0]->id(); rep.headers.resize(3); rep.status = moost::http::reply::moved_temporarily; moost::http::header h; diff --git a/src/resolver.cpp b/src/resolver.cpp index 43b5f27..662ce44 100644 --- a/src/resolver.cpp +++ b/src/resolver.cpp @@ -122,9 +122,6 @@ Resolver::load_library_resolver() ResolverService * rs = new RS_local_library(); - register_resolved_item( boost::bind( &PlayableItem::is_valid_json, _1 ), - boost::bind( &PlayableItem::from_json, _1 )); - // local library resolver is special, it gets a handle to app: ((RS_local_library *)rs)->set_app(app()); pap->set_rs( rs ); @@ -432,14 +429,15 @@ Resolver::add_results(query_uid qid, const vector< ri_ptr >& results, string via rq_ptr rq = m_queries[qid]; // resolver fixes the score using a standard algorithm // unless a non-zero score was specified by resolver. - pi_ptr pip = boost::dynamic_pointer_cast(rip); - if(pip && rip->score() < 0 && - TrackRQBuilder::valid( rq ) + if(rip->score() < 0 && + TrackRQBuilder::valid( rq ) && + rip->has_json_value( "artist" ) && + rip->has_json_value( "track" ) ) { - float score = calculate_score( rq, pip, reason ); + float score = calculate_score( rq, rip, reason ); if( score == 0.0) continue; - pip->set_score( score ); + rip->set_score( score ); } m_queries[qid]->add_result(rip); @@ -460,7 +458,7 @@ Resolver::add_results(query_uid qid, const vector< ri_ptr >& results, string via /// TODO albums are ignored atm. float Resolver::calculate_score( const rq_ptr & rq, // query - const pi_ptr & pi, // candidate + const ri_ptr & ri, // candidate string & reason ) // fail reason { using namespace boost; @@ -470,9 +468,9 @@ Resolver::calculate_score( const rq_ptr & rq, // query string o_alb = trim_copy(to_lower_copy(rq->param( "album" ).get_str())); // names from candidate result: - string art = trim_copy(to_lower_copy(pi->artist())); - string trk = trim_copy(to_lower_copy(pi->track())); - string alb = trim_copy(to_lower_copy(pi->album())); + string art = trim_copy(to_lower_copy(ri->json_value("artist", "" ))); + string trk = trim_copy(to_lower_copy(ri->json_value("track", ""))); + string alb = trim_copy(to_lower_copy(ri->json_value("album",""))); // short-circuit for exact match if(o_art == art && o_trk == trk) return 1.0; // the real deal, with edit distances: @@ -678,23 +676,11 @@ Resolver::get_ss(const source_uid & sid) } -void -Resolver::register_resolved_item( const ri_validator& val, const ri_generator& gen) -{ - m_riList.push_back( std::pair( val, gen )); -} - +//TODO: probably a redundant function now. ri_ptr Resolver::ri_from_json( const json_spirit::Object& j ) const { - std::pair< ri_validator , ri_generator> pair; - - BOOST_FOREACH( pair, m_riList ) - { - if( pair.first( j ) ) - return pair.second( j ); - } - return ri_ptr(); + return ri_ptr( new ResolvedItem( j )); } template diff --git a/src/rs_local_library.cpp b/src/rs_local_library.cpp index 48a68c7..6641e13 100644 --- a/src/rs_local_library.cpp +++ b/src/rs_local_library.cpp @@ -7,6 +7,7 @@ #include "playdar/utils/uuid.h" #include "playdar/utils/levenshtein.h" #include "playdar/resolver.h" +#include "playdar/resolved_item_builder.hpp" using namespace std; @@ -93,10 +94,10 @@ RS_local_library::process( rq_ptr rq ) vector fids = app()->library()->get_fids_for_tid(sp.id); BOOST_FOREACH(int fid, fids) { - pi_ptr pip = PlayableItem::create(*app()->library(), fid); - pip->set_id( m_pap->gen_uuid() ); - pip->set_source( m_pap->hostname() ); - final_results.push_back( pip->get_json() ); + ri_ptr rip = ResolvedItemBuilder::createFromFid(*app()->library(), fid); + rip->set_id( m_pap->gen_uuid() ); + rip->set_source( m_pap->hostname() ); + final_results.push_back( rip->get_json() ); } } if(final_results.size()) diff --git a/src/rs_script.cpp b/src/rs_script.cpp index 4426bc1..86c7b47 100644 --- a/src/rs_script.cpp +++ b/src/rs_script.cpp @@ -262,7 +262,7 @@ rs_script::process_output() BOOST_FOREACH(Value & result, resultsA) { Object po = result.get_obj(); - boost::shared_ptr pip( PlayableItem::from_json(po) ); + boost::shared_ptr pip( new ResolvedItem( po ) ); //cout << "Parserd pip from script: " << endl; //write_formatted( pip->get_json(), cout );