Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 795 lines (720 sloc) 26.872 kb
2c294b7 @RJ license preamble spam
authored
1 /*
2 Playdar - music content resolver
3 Copyright (C) 2009 Richard Jones
4 Copyright (C) 2009 Last.fm Ltd.
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
b4c5641 @RJ mega-refactor into separate includes dir, plugins should build withou…
authored
19 #include "playdar/application.h"
20
7760c19 @RJ initial checkin
authored
21 #include <sstream>
e588be7 @RJ add timeout to auto-cancel stale queries
authored
22 #include <boost/asio.hpp>
7760c19 @RJ initial checkin
authored
23 #include <boost/foreach.hpp>
24 #include <boost/thread/mutex.hpp>
b4c5641 @RJ mega-refactor into separate includes dir, plugins should build withou…
authored
25 #include <boost/filesystem.hpp>
9a8aa7d @RJ change LAN plugin protocol (backwards incompatible\!) and wire up bas…
authored
26 #include <boost/algorithm/string.hpp>
e588be7 @RJ add timeout to auto-cancel stale queries
authored
27 #include <boost/date_time/posix_time/posix_time.hpp>
9091da2 @nova77 * Added win32 version with libraries project
nova77 authored
28 #include <boost/version.hpp>
f17044a @RJ lazy creation of SS, re-enable script resolver
authored
29 #include <boost/bind.hpp>
30 #include <boost/function.hpp>
7760c19 @RJ initial checkin
authored
31
b4c5641 @RJ mega-refactor into separate includes dir, plugins should build withou…
authored
32 #include "playdar/resolver.h"
c0d3aa8 @dougma reduce include deps in resolver.h
dougma authored
33 #include "playdar/ss_curl.hpp"
3b6ca0e @RJ start to treat external processes/scripts as first-class resolvers
authored
34 #include "playdar/rs_script.h"
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
35 #include "playdar/logger.h"
7760c19 @RJ initial checkin
authored
36
b1f4658 @jonocole make TrackRQBuilder and others json::Value_type safe.
jonocole authored
37 // Generic track calculation stuff:
38 #include "playdar/track_rq_builder.hpp"
323959f @RJ move levenshtein, base64 into utils. Add support for HTTP basic auth …
authored
39 #include "playdar/utils/levenshtein.h"
e1b7e40 @RJ first wave of api refactoring - builds and runs without any plugins o…
authored
40 #include "playdar/pluginadaptor_impl.hpp"
323959f @RJ move levenshtein, base64 into utils. Add support for HTTP basic auth …
authored
41
b4c5641 @RJ mega-refactor into separate includes dir, plugins should build withou…
authored
42 // PDL stuff:
e0a49b8 @RJ use PDL for non-core resolver plugins
authored
43 #include <DynamicLoader.hpp>
44 #include <DynamicClass.hpp>
45 #include <DynamicLibrary.hpp>
46 #include <DynamicLibraryManager.hpp>
47 #include <LoaderException.hpp>
7e81018 @mokele pdl needs to include "./platform.h" for cases when you have another p…
mokele authored
48 #include "./platform.h"
b4c5641 @RJ mega-refactor into separate includes dir, plugins should build withou…
authored
49 // end PDL stuff
7760c19 @RJ initial checkin
authored
50
56985db @nova77 updated namespaces
nova77 authored
51 namespace playdar {
52
53 using namespace resolvers;
61b1e90 @jonocole less using ns std in headers. more checking json_spirit::Value types
jonocole authored
54 using namespace std;
f7463d8 @RJ loading darknet and udp as plugins works
authored
55
7760c19 @RJ initial checkin
authored
56 Resolver::Resolver(MyApplication * app)
e226616 @RJ proper shutdown and cleanup, and a signal handler that might not comp…
authored
57 :m_app(app), m_exiting(false)
7760c19 @RJ initial checkin
authored
58 {
59 m_id_counter = 0;
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
60 log::info() << "Resolver starting..." << endl;
f571f00 @RJ first version of resolver pipeline code
authored
61
e226616 @RJ proper shutdown and cleanup, and a signal handler that might not comp…
authored
62 m_t = new boost::thread(boost::bind(&Resolver::dispatch_runner, this));
001de9d @RJ pipeline update, better threading model in main resolver dispatch loop
authored
63
f571f00 @RJ first version of resolver pipeline code
authored
64 // set up io_service with work so it never ends:
e226616 @RJ proper shutdown and cleanup, and a signal handler that might not comp…
authored
65 m_io_service = new boost::asio::io_service();
66 m_work = new boost::asio::io_service::work(*m_io_service);
67 m_iothr = new boost::thread(boost::bind(
68 &boost::asio::io_service::run,
69 m_io_service));
f17044a @RJ lazy creation of SS, re-enable script resolver
authored
70
71 // Initialize built-in curl SS facts:
72 detect_curl_capabilities();
f571f00 @RJ first version of resolver pipeline code
authored
73
7e37ba4 @RJ config object now holds config rather than app()
authored
74 // Load all non built-in resolvers:
1077159 @RJ ability for plugins to handle specified URLs for configuration/info
authored
75 try
76 {
cdd15be @RJ config option to not load plugins andor scripts
authored
77 // Scripts resolvers:
78 if( m_app->conf()->get<bool>("load_scripts", true) )
79 load_resolver_scripts(); // external processes
80 else
81 cerr << "NOT loading scripts, due to load_scripts=no" << endl;
82
83 // PDL-loaded plugins:
84 if( m_app->conf()->get<bool>("load_plugins", true) )
85 load_resolver_plugins(); // DLL plugins
86 else
87 cerr << "NOT loading scripts, due to load_scripts=no" << endl;
1077159 @RJ ability for plugins to handle specified URLs for configuration/info
authored
88 }
89 catch(...)
90 {
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
91 log::error() << "Error loading resolver plugins." << endl;
1077159 @RJ ability for plugins to handle specified URLs for configuration/info
authored
92 }
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
93 //typedef std::pair<string,ResolverService*> pairx;
94 //BOOST_FOREACH( pairx px, m_pluginNameMap )
95 //{
96 // log::info() << px.first << ", " ;
97 //}
98 //log::info() << endl;
9a8aa7d @RJ change LAN plugin protocol (backwards incompatible\!) and wire up bas…
authored
99
f571f00 @RJ first version of resolver pipeline code
authored
100 // sort the list of resolvers by weight, descending:
e1b7e40 @RJ first wave of api refactoring - builds and runs without any plugins o…
authored
101 boost::function<bool (const pa_ptr &, const pa_ptr&)> sortfun =
102 boost::bind(&Resolver::pluginadaptor_sorter, this, _1, _2);
f571f00 @RJ first version of resolver pipeline code
authored
103 sort(m_resolvers.begin(), m_resolvers.end(), sortfun);
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
104 //cout << endl;
105 log::info() << "Loaded resolvers (" << m_resolvers.size() << ")" << endl;
e1b7e40 @RJ first wave of api refactoring - builds and runs without any plugins o…
authored
106 BOOST_FOREACH( pa_ptr & pa, m_resolvers )
bfd2850 @RJ scripts/external process are now first-class resolvers, like plugins
authored
107 {
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
108 log::info() << "RESOLVER w:" << pa->weight() << "\tp:" << pa->preference()
a2e1a6f @RJ add 'preference' which is a secondary sorting option, implying networ…
authored
109 << "\tt:" << pa->targettime()
e1b7e40 @RJ first wave of api refactoring - builds and runs without any plugins o…
authored
110 << "\t[" << (pa->script()?"script":"plugin") << "] "
111 << pa->rs()->name() << endl;
bfd2850 @RJ scripts/external process are now first-class resolvers, like plugins
authored
112 }
1077159 @RJ ability for plugins to handle specified URLs for configuration/info
authored
113 }
114
f17044a @RJ lazy creation of SS, re-enable script resolver
authored
115 /// set up SS factories for every protocol curl can handle
116 void
117 Resolver::detect_curl_capabilities()
118 {
119 curl_version_info_data * cv = curl_version_info(CURLVERSION_NOW);
120 assert( cv->age >= 0 ); // should never get this far without curl.
121 boost::function<boost::shared_ptr<CurlStreamingStrategy>(std::string)>
122 ssf = boost::bind( &Resolver::ss_ptr_generator<CurlStreamingStrategy>, this, _1 );
123 const char * proto;
124 for(int i = 0; (proto = cv->protocols[i]) ; i++ )
125 {
126 string p(proto);
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
127 log::info() << "SS factory registered for: " << p << endl ;
f17044a @RJ lazy creation of SS, re-enable script resolver
authored
128 m_ss_factories[ p ] = ssf; // add an SS factory for this protocol
129 }
130 }
131
e226616 @RJ proper shutdown and cleanup, and a signal handler that might not comp…
authored
132
133 Resolver::~Resolver()
134 {
135 m_exiting = true;
136 m_cond.notify_one();
137 m_t->join();
e1b7e40 @RJ first wave of api refactoring - builds and runs without any plugins o…
authored
138 delete m_work;
e226616 @RJ proper shutdown and cleanup, and a signal handler that might not comp…
authored
139 m_io_service->stop();
140 m_iothr->join();
141 }
142
f571f00 @RJ first version of resolver pipeline code
authored
143 bool
e1b7e40 @RJ first wave of api refactoring - builds and runs without any plugins o…
authored
144 Resolver::pluginadaptor_sorter(const pa_ptr& lhs, const pa_ptr& rhs)
1077159 @RJ ability for plugins to handle specified URLs for configuration/info
authored
145 {
79be739 @RJ jonos local library pluginization
authored
146 // this first check is really just cosmetic
147 // the sorting looks nicer this way:
148 if( lhs->weight() == rhs->weight() )
149 return lhs->targettime() < rhs->targettime();
150 // this is the important bit that orders the pipeline:
e1b7e40 @RJ first wave of api refactoring - builds and runs without any plugins o…
authored
151 return lhs->weight() > rhs->weight();
7760c19 @RJ initial checkin
authored
152 }
153
3b6ca0e @RJ start to treat external processes/scripts as first-class resolvers
authored
154 /// spawn resolver scripts:
155 void
156 Resolver::load_resolver_scripts()
157 {
cb3c918 @mxcl Load all scripts found in etc directory
mxcl authored
158 using namespace boost::filesystem;
159
ed268f1 @RJ resolver scripts now live in scripts/
authored
160 path const etc = "scripts"; //FIXME don't depend on working directory
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
161 log::info() << "Loading resolver scripts from: " << etc << endl;
765825c @dougma don't throw on non-existent script directory
dougma authored
162
163 if (!exists(etc) || !is_directory(etc)) return; // avoid the throw
164
cb3c918 @mxcl Load all scripts found in etc directory
mxcl authored
165 directory_iterator const end;
166 string name;
167 for(directory_iterator i(etc); i != end; ++i) {
168 try {
9091da2 @nova77 * Added win32 version with libraries project
nova77 authored
169 #if BOOST_VERSION >= 103600
cb3c918 @mxcl Load all scripts found in etc directory
mxcl authored
170 string name = i->path().filename();
b35d211 @RJ add a localonly option for resolvers - they will only be asked to res…
authored
171 string conf = "plugins." + i->path().stem();
9091da2 @nova77 * Added win32 version with libraries project
nova77 authored
172 #else
173 string name = i->path().leaf();
b35d211 @RJ add a localonly option for resolvers - they will only be asked to res…
authored
174 string conf = "plugins." + basename(i->path());
9091da2 @nova77 * Added win32 version with libraries project
nova77 authored
175 #endif
cb3c918 @mxcl Load all scripts found in etc directory
mxcl authored
176 if (is_directory(i->status()) || is_other(i->status()))
177 continue;
178 //FIXME more sensible place to put resolving scripts
ed268f1 @RJ resolver scripts now live in scripts/
authored
179 if (name=="README.txt")
cb3c918 @mxcl Load all scripts found in etc directory
mxcl authored
180 continue;
b35d211 @RJ add a localonly option for resolvers - they will only be asked to res…
authored
181 if (app()->conf()->get<bool>(conf+".enabled", true) == false){
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
182 log::info() << "-> Skipping '"+name+"' - disabled in config file";
13d8fcb @RJ dynamically loaded plugins must now extend ResolverServicePlugin, whi…
authored
183 continue;
184 }
cb3c918 @mxcl Load all scripts found in etc directory
mxcl authored
185
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
186 log::info() << "-> Loading: " << name << endl;
13d8fcb @RJ dynamically loaded plugins must now extend ResolverServicePlugin, whi…
authored
187
c351a1a @jonocole store classname(/scriptname) in pluginadaptor to allow use eg in urlh…
jonocole authored
188 pa_ptr pap( new PluginAdaptorImpl( app()->conf(), this, name ) );
f17044a @RJ lazy creation of SS, re-enable script resolver
authored
189 pap->set_script( true );
190 pap->set_scriptpath( i->path().string() );
191 ResolverService * rs = new playdar::resolvers::rs_script();
192 bool init = rs->init( pap );
cb3c918 @mxcl Load all scripts found in etc directory
mxcl authored
193 if(!init) throw 1;
f17044a @RJ lazy creation of SS, re-enable script resolver
authored
194 pap->set_rs( rs );
b35d211 @RJ add a localonly option for resolvers - they will only be asked to res…
authored
195 pap->set_weight( app()->conf()->get<int>
196 (conf+".weight", rs->weight()) );
197 pap->set_preference( app()->conf()->get<int>
198 (conf+".preference", rs->preference()) );
199 pap->set_targettime( app()->conf()->get<int>
200 (conf+".targettime", rs->target_time()) );
201 pap->set_localonly( app()->conf()->get<bool>
202 (conf+".localonly", rs->localonly()) );
f17044a @RJ lazy creation of SS, re-enable script resolver
authored
203 // if weight == 0, it doesnt resolve, but may handle HTTP calls etc.
204 if(pap->weight() > 0)
205 {
206 m_resolvers.push_back( pap );
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
207 log::info() << "-> OK [w:" << pap->weight()
a2e1a6f @RJ add 'preference' which is a secondary sorting option, implying networ…
authored
208 << " p:" << pap->preference()
209 << " t:" << pap->targettime()
210 << "] "
211 << pap->rs()->name() << endl;
f17044a @RJ lazy creation of SS, re-enable script resolver
authored
212 }
3b6ca0e @RJ start to treat external processes/scripts as first-class resolvers
authored
213 }
214 catch(...)
215 {
cb3c918 @mxcl Load all scripts found in etc directory
mxcl authored
216 cerr << "-> ERROR initializing script at: " << *i << endl;
3b6ca0e @RJ start to treat external processes/scripts as first-class resolvers
authored
217 }
218 }
219 }
220
f571f00 @RJ first version of resolver pipeline code
authored
221 /// dynamically load resolver plugins:
e0a49b8 @RJ use PDL for non-core resolver plugins
authored
222 void
3b6ca0e @RJ start to treat external processes/scripts as first-class resolvers
authored
223 Resolver::load_resolver_plugins()
e0a49b8 @RJ use PDL for non-core resolver plugins
authored
224 {
f7463d8 @RJ loading darknet and udp as plugins works
authored
225 namespace bfs = boost::filesystem;
226 bfs::directory_iterator end_itr;
0377eef add www_root and plugin_path configurables, remove conf::get/1 (fails…
doug.mansell@gmail.com authored
227 bfs::path p( m_app->conf()->get(string("plugin_path"), string("plugins")) );
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
228 log::info() << "Loading resolver plugins from: "
7e37ba4 @RJ config object now holds config rather than app()
authored
229 << p.string() << endl;
d1267a0 @jonocole creating a new uuid generator for every auth request and every plugin…
jonocole authored
230
f7463d8 @RJ loading darknet and udp as plugins works
authored
231 for(bfs::directory_iterator itr( p ); itr != end_itr; ++itr)
e0a49b8 @RJ use PDL for non-core resolver plugins
authored
232 {
f7463d8 @RJ loading darknet and udp as plugins works
authored
233 if ( bfs::is_directory(itr->status()) ) continue;
234 string pluginfile = itr->string();
7e37ba4 @RJ config object now holds config rather than app()
authored
235 if(bfs::extension(pluginfile)!=".resolver")
236 {
1077159 @RJ ability for plugins to handle specified URLs for configuration/info
authored
237 //cerr << "Skipping '" << pluginfile
238 // << "' from plugins directory" << endl;
7e37ba4 @RJ config object now holds config rather than app()
authored
239 continue;
240 }
f7463d8 @RJ loading darknet and udp as plugins works
authored
241 string classname = bfs::basename(pluginfile);
9a8aa7d @RJ change LAN plugin protocol (backwards incompatible\!) and wire up bas…
authored
242 if(m_pluginNameMap.find(boost::to_lower_copy(classname)) != m_pluginNameMap.end())
243 {
244 cerr << "ERROR: Plugin class '"<< classname << "' already in use. "
245 << "Case-insensitivity applies." << endl;
246 continue;
247 }
e600e8e @jonocole update default conf file and make sure consistant naming of plugins a…
jonocole authored
248 string confopt = "plugins.";
7e37ba4 @RJ config object now holds config rather than app()
authored
249 confopt += classname;
250 confopt += ".enabled";
cb3c918 @mxcl Load all scripts found in etc directory
mxcl authored
251 if(app()->conf()->get<bool>(confopt, true) == false)
7e37ba4 @RJ config object now holds config rather than app()
authored
252 {
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
253 log::info() << "Skipping '" << classname
cb3c918 @mxcl Load all scripts found in etc directory
mxcl authored
254 <<"' - disabled in config file" << endl;
7e37ba4 @RJ config object now holds config rather than app()
authored
255 continue;
256 }
f7463d8 @RJ loading darknet and udp as plugins works
authored
257 try
258 {
7e37ba4 @RJ config object now holds config rather than app()
authored
259 PDL::DynamicLoader & dynamicLoader =
260 PDL::DynamicLoader::Instance();
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
261 log::info() << "Loading resolver: " << pluginfile << endl;
5b93bba @nova77 using ResolverServicePlugin when loading plugins!
nova77 authored
262 ResolverServicePlugin * instance =
13d8fcb @RJ dynamically loaded plugins must now extend ResolverServicePlugin, whi…
authored
263 dynamicLoader.GetClassInstance< ResolverServicePlugin >
7e37ba4 @RJ config object now holds config rather than app()
authored
264 ( pluginfile.c_str(), classname.c_str() );
c351a1a @jonocole store classname(/scriptname) in pluginadaptor to allow use eg in urlh…
jonocole authored
265 pa_ptr pap( new PluginAdaptorImpl( app()->conf(), this, classname ) );
e1b7e40 @RJ first wave of api refactoring - builds and runs without any plugins o…
authored
266 if( ! instance->init(pap) )
13d8fcb @RJ dynamically loaded plugins must now extend ResolverServicePlugin, whi…
authored
267 {
268 cerr << "-> ERROR couldn't initialize." << endl;
269 instance->Destroy();
270 continue;
271 }
9a8aa7d @RJ change LAN plugin protocol (backwards incompatible\!) and wire up bas…
authored
272 m_pluginNameMap[ boost::to_lower_copy(classname) ] = instance;
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
273 log::info() << "Added pluginName " << boost::to_lower_copy(classname) << endl;
e1b7e40 @RJ first wave of api refactoring - builds and runs without any plugins o…
authored
274 pap->set_script( false );
275 pap->set_rs( instance );
a2e1a6f @RJ add 'preference' which is a secondary sorting option, implying networ…
authored
276 string rsopt = "plugins."+classname;
b35d211 @RJ add a localonly option for resolvers - they will only be asked to res…
authored
277
278 pap->set_weight( app()->conf()->get<int>
279 (rsopt + ".weight", instance->weight()) );
280 pap->set_preference( app()->conf()->get<int>
281 (rsopt + ".preference", instance->preference()) );
282 pap->set_targettime( app()->conf()->get<int>
283 (rsopt + ".targettime", instance->target_time()) );
284 pap->set_localonly( app()->conf()->get<bool>
285 (rsopt + ".localonly", instance->localonly()) );
286
e1b7e40 @RJ first wave of api refactoring - builds and runs without any plugins o…
authored
287 m_resolvers.push_back( pap );
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
288 log::info() << "-> OK [w:" << pap->weight()
a2e1a6f @RJ add 'preference' which is a secondary sorting option, implying networ…
authored
289 << " p:" << pap->preference()
290 << " t:" << pap->targettime()
291 << "] "
e1b7e40 @RJ first wave of api refactoring - builds and runs without any plugins o…
authored
292 << pap->rs()->name() << endl;
2d92222 @RJ xmpp ft working for first block with IBB
authored
293 // add any custom SS handlers for this plugin
294 map< std::string, boost::function<ss_ptr(std::string)> > ssfacts = instance->get_ss_factories();
295 typedef std::pair< std::string, boost::function<ss_ptr(std::string)> > sspair_t;
296 BOOST_FOREACH( sspair_t sp, ssfacts )
297 {
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
298 log::info() << "-> Added SS factory for protocol '"<<sp.first<<"'" << endl;
2d92222 @RJ xmpp ft working for first block with IBB
authored
299 m_ss_factories[ sp.first ] = sp.second;
300 }
f7463d8 @RJ loading darknet and udp as plugins works
authored
301 }
302 catch( PDL::LoaderException & ex )
303 {
304 cerr << "-> Error: " << ex.what() << endl;
305 }
e0a49b8 @RJ use PDL for non-core resolver plugins
authored
306 }
307 }
308
1077159 @RJ ability for plugins to handle specified URLs for configuration/info
authored
309
f571f00 @RJ first version of resolver pipeline code
authored
310 /// start resolving! (non-blocking)
311 /// returns a query_uid so you can check status of this query later
312 query_uid
b35d211 @RJ add a localonly option for resolvers - they will only be asked to res…
authored
313 Resolver::dispatch(rq_ptr rq)
f571f00 @RJ first version of resolver pipeline code
authored
314 {
35d4ad6 @RJ implement cancel_query so queries can be manually cancelled from web …
authored
315 return dispatch(rq, 0);
f571f00 @RJ first version of resolver pipeline code
authored
316 }
317
7760c19 @RJ initial checkin
authored
318 query_uid
b35d211 @RJ add a localonly option for resolvers - they will only be asked to res…
authored
319 Resolver::dispatch(rq_ptr rq, rq_callback_t cb)
7760c19 @RJ initial checkin
authored
320 {
001de9d @RJ pipeline update, better threading model in main resolver dispatch loop
authored
321 boost::mutex::scoped_lock lk(m_mutex);
7760c19 @RJ initial checkin
authored
322 if(!add_new_query(rq))
323 {
f571f00 @RJ first version of resolver pipeline code
authored
324 // already running
2e6b81a @RJ support AAC if taglib does, mimetype lookup, fix empty name crashbug …
authored
325 return rq->id();
7760c19 @RJ initial checkin
authored
326 }
f571f00 @RJ first version of resolver pipeline code
authored
327 if(cb) rq->register_callback(cb);
88b502c @dougma initial comet code (compiles, untested, more todo)
dougma authored
328
329 // setup comet callback if the request has a valid comet session id
330 const string& cometId(rq->comet_session_id());
331 if (cometId.length()) {
332 boost::mutex::scoped_lock cometlock(m_comets_mutex);
333 std::map< std::string, rq_callback_t >::const_iterator it = m_comets.find(cometId);
334 if (it != m_comets.end()) {
335 rq->register_callback(it->second);
336 }
337 }
338
001de9d @RJ pipeline update, better threading model in main resolver dispatch loop
authored
339 m_pending.push_front( pair<rq_ptr, unsigned short>(rq, 999) );
340 m_cond.notify_one();
e588be7 @RJ add timeout to auto-cancel stale queries
authored
341 // set up timer to auto-cancel this query after a while:
342 boost::asio::deadline_timer * t = new boost::asio::deadline_timer(*m_io_service);
343 // give 5 mins additional time to allow setup/results, otherwise it would never be stale
344 // at max_query_lifetime, because the first result updates the atime:
345 t->expires_from_now(boost::posix_time::seconds(max_query_lifetime()+300));
346 t->async_wait(boost::bind(&Resolver::cancel_query_timeout, this, rq->id()));
347 m_qidtimers[rq->id()] = t;
f571f00 @RJ first version of resolver pipeline code
authored
348 return rq->id();
349 }
7760c19 @RJ initial checkin
authored
350
001de9d @RJ pipeline update, better threading model in main resolver dispatch loop
authored
351 /// thread that loops forever dispatching stuff in the queue
352 void
353 Resolver::dispatch_runner()
354 {
e226616 @RJ proper shutdown and cleanup, and a signal handler that might not comp…
authored
355 try
001de9d @RJ pipeline update, better threading model in main resolver dispatch loop
authored
356 {
e226616 @RJ proper shutdown and cleanup, and a signal handler that might not comp…
authored
357 while(true)
001de9d @RJ pipeline update, better threading model in main resolver dispatch loop
authored
358 {
35d4ad6 @RJ implement cancel_query so queries can be manually cancelled from web …
authored
359 pair<rq_ptr, unsigned short> p;
e226616 @RJ proper shutdown and cleanup, and a signal handler that might not comp…
authored
360 {
361 boost::mutex::scoped_lock lk(m_mutex);
362 if(m_pending.size() == 0) m_cond.wait(lk);
363 if(m_exiting) break;
364 p = m_pending.back();
365 m_pending.pop_back();
366 }
367 run_pipeline( p.first, p.second );
001de9d @RJ pipeline update, better threading model in main resolver dispatch loop
authored
368 }
369 }
e226616 @RJ proper shutdown and cleanup, and a signal handler that might not comp…
authored
370 catch(...)
371 {
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
372 log::error() << "Error exiting Resolver::dispatch_runner" << endl;
e226616 @RJ proper shutdown and cleanup, and a signal handler that might not comp…
authored
373 }
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
374 log::info() << "Resolver dispatch_runner terminating" << endl;
001de9d @RJ pipeline update, better threading model in main resolver dispatch loop
authored
375 }
376
f571f00 @RJ first version of resolver pipeline code
authored
377 /// go thru list of resolversservices and dispatch in order
378 /// lastweight is the weight of the last resolver we dispatched to.
379 void
380 Resolver::run_pipeline( rq_ptr rq, unsigned short lastweight )
381 {
810e382 @RJ a lot less cout
authored
382 unsigned short atweight = 0;
383 unsigned int mintime = 0;
f571f00 @RJ first version of resolver pipeline code
authored
384 bool started = false;
e1b7e40 @RJ first wave of api refactoring - builds and runs without any plugins o…
authored
385 BOOST_FOREACH( pa_ptr pap, m_resolvers )
7760c19 @RJ initial checkin
authored
386 {
e1b7e40 @RJ first wave of api refactoring - builds and runs without any plugins o…
authored
387 if(pap->weight() >= lastweight) continue;
f571f00 @RJ first version of resolver pipeline code
authored
388 if(!started)
389 {
e1b7e40 @RJ first wave of api refactoring - builds and runs without any plugins o…
authored
390 atweight = pap->weight();
391 mintime = pap->targettime();
f571f00 @RJ first version of resolver pipeline code
authored
392 started = true;
810e382 @RJ a lot less cout
authored
393 //cout << "Pipeline at weight: " << atweight << endl;
f571f00 @RJ first version of resolver pipeline code
authored
394 }
e1b7e40 @RJ first wave of api refactoring - builds and runs without any plugins o…
authored
395 if(pap->weight() != atweight)
f7463d8 @RJ loading darknet and udp as plugins works
authored
396 {
f571f00 @RJ first version of resolver pipeline code
authored
397 // we've dispatched to everything of weight "atweight"
398 // and the shortest targettime at that weight is "mintime"
399 // so schedule a callaback after mintime to carry on down the
400 // chain and dispatch to the next lowest weighted resolver services.
810e382 @RJ a lot less cout
authored
401 //cout << "Will continue pipeline after " << mintime << "ms." << endl;
f571f00 @RJ first version of resolver pipeline code
authored
402 boost::shared_ptr<boost::asio::deadline_timer>
403 t(new boost::asio::deadline_timer( m_work->get_io_service() ));
404 t->expires_from_now(boost::posix_time::milliseconds(mintime));
405 // pass the timer pointer to the handler so it doesnt autodestruct:
406 t->async_wait(boost::bind(&Resolver::run_pipeline_cont, this,
407 rq, atweight, t));
408 break;
f7463d8 @RJ loading darknet and udp as plugins works
authored
409 }
e1b7e40 @RJ first wave of api refactoring - builds and runs without any plugins o…
authored
410 if(pap->targettime() < mintime) mintime = pap->targettime();
b35d211 @RJ add a localonly option for resolvers - they will only be asked to res…
authored
411
412 if( pap->localonly() && !rq->origin_local() )
413 {
414 // Not dispatching (remote query, to local-only plugin)
415 }
416 else
417 {
418 // dispatch to this resolver:
419 //cout << "Pipeline dispatching to " << pap->rs()->name()
420 // << " (lastweight: " << lastweight << ")" << endl;
421 pap->rs()->start_resolving(rq);
422 }
7760c19 @RJ initial checkin
authored
423 }
424 }
425
f571f00 @RJ first version of resolver pipeline code
authored
426 void
427 Resolver::run_pipeline_cont( rq_ptr rq,
428 unsigned short lastweight,
429 boost::shared_ptr<boost::asio::deadline_timer> oldtimer)
8102dac @RJ callbacks/observability for RQs
authored
430 {
810e382 @RJ a lot less cout
authored
431 //cout << "Pipeline continues.." << endl;
f571f00 @RJ first version of resolver pipeline code
authored
432 if(rq->solved())
8102dac @RJ callbacks/observability for RQs
authored
433 {
810e382 @RJ a lot less cout
authored
434 //cout << "Bailing from pipeline: SOLVED @ lastweight: " << lastweight
435 // << endl;
8102dac @RJ callbacks/observability for RQs
authored
436 }
f571f00 @RJ first version of resolver pipeline code
authored
437 else
8102dac @RJ callbacks/observability for RQs
authored
438 {
001de9d @RJ pipeline update, better threading model in main resolver dispatch loop
authored
439 boost::mutex::scoped_lock lk(m_mutex);
440 m_pending.push_front( pair<rq_ptr, unsigned short>(rq, lastweight) );
441 m_cond.notify_one();
8102dac @RJ callbacks/observability for RQs
authored
442 }
443 }
444
e588be7 @RJ add timeout to auto-cancel stale queries
authored
445 /// a resolver will report results here
f25562e @dougma change resolver_query::add_result to add_results in quest for speed
dougma authored
446 /// false return means give up on this query, it's over
447 /// true return means carry on as normal
7760c19 @RJ initial checkin
authored
448 bool
f17044a @RJ lazy creation of SS, re-enable script resolver
authored
449 Resolver::add_results(query_uid qid, const vector< ri_ptr >& results, string via)
7760c19 @RJ initial checkin
authored
450 {
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
451 log::info() << "add_results(" << results.size() << ", '"<< via << "')" << endl;
2c624c1 @RJ integrate (currently useless) darknet code, add shortcut when no quer…
authored
452 if(results.size()==0)
453 {
454 return true;
455 }
b373a0b @dougma only insert meaningful sids into Resolver::m_sid2ri
dougma authored
456
f25562e @dougma change resolver_query::add_result to add_results in quest for speed
dougma authored
457 rq_ptr rq;
57d9ac6 @dougma hoist rq_ptr out of the loop in add_results
dougma authored
458
7760c19 @RJ initial checkin
authored
459 {
f25562e @dougma change resolver_query::add_result to add_results in quest for speed
dougma authored
460 // scope lock to protect m_queries and m_sid2ri
461 boost::mutex::scoped_lock lock(m_mut_results);
462
463 if(!query_exists(qid))
464 return false; // query was deleted
465 rq = m_queries[qid];
466
467 // setup sid mappings
468 string sid;
469 BOOST_FOREACH(const ri_ptr& rip, results)
33b82ec @jonocole move default track score calculation back into resolver.
jonocole authored
470 {
f25562e @dougma change resolver_query::add_result to add_results in quest for speed
dougma authored
471 // update map of source id -> playable item
472 sid = rip->id();
473 if (sid.length()) {
474 m_sid2ri[sid] = rip;
475 }
33b82ec @jonocole move default track score calculation back into resolver.
jonocole authored
476 }
f25562e @dougma change resolver_query::add_result to add_results in quest for speed
dougma authored
477 }
b373a0b @dougma only insert meaningful sids into Resolver::m_sid2ri
dougma authored
478
f25562e @dougma change resolver_query::add_result to add_results in quest for speed
dougma authored
479 if (rq->isValidTrack()) {
ba9ac26 @dougma ref count CometSession instances via shared_ptr
dougma authored
480 // these results are for a track query, score the unscored results
f25562e @dougma change resolver_query::add_result to add_results in quest for speed
dougma authored
481 string reason;
482 BOOST_FOREACH(const ri_ptr& rip, results) {
483 // resolver fixes the score using a standard algorithm
484 // unless a non-zero score was specified by resolver.
485 if (rip->score() < 0 &&
486 rip->has_json_value<string>( "artist" ) &&
487 rip->has_json_value<string>( "track" ) )
488 {
489 float score = calculate_score( rq, rip, reason );
2cfa2cb @dougma fix unscored results problem without breaking boffin/others
dougma authored
490 if (score > 0) {
f25562e @dougma change resolver_query::add_result to add_results in quest for speed
dougma authored
491 rip->set_score( score );
2cfa2cb @dougma fix unscored results problem without breaking boffin/others
dougma authored
492 rq->add_result( rip );
493 }
b97cc84 @dougma resolver logic was discarding results with a score :(
dougma authored
494 } else if (rip->score() > 0) {
495 rq->add_result( rip );
f25562e @dougma change resolver_query::add_result to add_results in quest for speed
dougma authored
496 }
b373a0b @dougma only insert meaningful sids into Resolver::m_sid2ri
dougma authored
497 }
2cfa2cb @dougma fix unscored results problem without breaking boffin/others
dougma authored
498 } else {
499 // some other type of query, doesn't need scoring.
500 rq->add_results( results );
f17044a @RJ lazy creation of SS, re-enable script resolver
authored
501 }
f25562e @dougma change resolver_query::add_result to add_results in quest for speed
dougma authored
502
7760c19 @RJ initial checkin
authored
503 return true;
504 }
33b82ec @jonocole move default track score calculation back into resolver.
jonocole authored
505
3596a8f @dougma don't add zero scores to the results, make a couple of resolver metho…
dougma authored
506 // static
6bf527b @jonocole start of local library pluginization.
jonocole authored
507 string
508 Resolver::sortname(const string& name)
509 {
510 string data(name);
511 std::transform(data.begin(), data.end(), data.begin(), ::tolower);
512 boost::trim(data);
513 return data;
514 }
515
516
33b82ec @jonocole move default track score calculation back into resolver.
jonocole authored
517 /// caluclate score 0-1 based on how similar the names are.
518 /// string similarity algo that combines art,alb,trk from the original
519 /// query (rq) against a potential match (pi).
520 /// this is mostly just edit-distance, with some extra checks.
521 /// TODO albums are ignored atm.
3596a8f @dougma don't add zero scores to the results, make a couple of resolver metho…
dougma authored
522 // static
33b82ec @jonocole move default track score calculation back into resolver.
jonocole authored
523 float
524 Resolver::calculate_score( const rq_ptr & rq, // query
ade4b46 @jonocole consolidate specialized ResolvedItem types into a generic json backed…
jonocole authored
525 const ri_ptr & ri, // candidate
33b82ec @jonocole move default track score calculation back into resolver.
jonocole authored
526 string & reason ) // fail reason
527 {
528 using namespace boost;
529 // original names from the query:
b1f4658 @jonocole make TrackRQBuilder and others json::Value_type safe.
jonocole authored
530 string o_art = trim_copy(to_lower_copy(rq->param( "artist" ).get_str()));
531 string o_trk = trim_copy(to_lower_copy(rq->param( "track" ).get_str()));
532 string o_alb = trim_copy(to_lower_copy(rq->param( "album" ).get_str()));
35bc6da @jonocole fix generic score calculations for scripts / plugins that don't provi…
jonocole authored
533
33b82ec @jonocole move default track score calculation back into resolver.
jonocole authored
534 // names from candidate result:
ade4b46 @jonocole consolidate specialized ResolvedItem types into a generic json backed…
jonocole authored
535 string art = trim_copy(to_lower_copy(ri->json_value("artist", "" )));
536 string trk = trim_copy(to_lower_copy(ri->json_value("track", "")));
537 string alb = trim_copy(to_lower_copy(ri->json_value("album","")));
33b82ec @jonocole move default track score calculation back into resolver.
jonocole authored
538 // short-circuit for exact match
539 if(o_art == art && o_trk == trk) return 1.0;
540 // the real deal, with edit distances:
541 unsigned int trked = playdar::utils::levenshtein(
6bf527b @jonocole start of local library pluginization.
jonocole authored
542 sortname(trk),
543 sortname(o_trk));
33b82ec @jonocole move default track score calculation back into resolver.
jonocole authored
544 unsigned int arted = playdar::utils::levenshtein(
6bf527b @jonocole start of local library pluginization.
jonocole authored
545 sortname(art),
546 sortname(o_art));
33b82ec @jonocole move default track score calculation back into resolver.
jonocole authored
547 // tolerances:
548 float tol_art = 1.5;
549 float tol_trk = 1.5;
550 //float tol_alb = 1.5; // album rating unsed atm.
551
552 // names less than this many chars aren't dismissed based on % edit-dist:
553 unsigned int grace_len = 6;
554
555 // if % edit distance is greater than tolerance, fail them outright:
556 if( o_art.length() > grace_len &&
557 arted > o_art.length()/tol_art )
558 {
559 reason = "artist name tolerance";
560 return 0.0;
561 }
562 if( o_trk.length() > grace_len &&
563 trked > o_trk.length()/tol_trk )
564 {
565 reason = "track name tolerance";
566 return 0.0;
567 }
568 // if edit distance longer than original name, fail them outright:
569 if( arted >= o_art.length() )
570 {
571 reason = "artist name editdist >= length";
572 return 0.0;
573 }
574 if( trked >= o_trk.length() )
575 {
576 reason = "track name editdist >= length";
577 return 0.0;
578 }
579
580 // combine the edit distance of artist & track into a final score:
581 float artdist_pc = (o_art.length()-arted) / (float) o_art.length();
582 float trkdist_pc = (o_trk.length()-trked) / (float) o_trk.length();
583 return artdist_pc * trkdist_pc;
584 }
585
586
35d4ad6 @RJ implement cancel_query so queries can be manually cancelled from web …
authored
587 /// Cancel a query, delete any results and free the memory. QID will no longer exist.
588 void
589 Resolver::cancel_query(const query_uid & qid)
590 {
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
591 log::info() << "Cancelling query: " << qid << endl;
35d4ad6 @RJ implement cancel_query so queries can be manually cancelled from web …
authored
592 // send cancel to all resolvers, in case they have cleanup to do:
e1b7e40 @RJ first wave of api refactoring - builds and runs without any plugins o…
authored
593 BOOST_FOREACH( pa_ptr pap, m_resolvers )
35d4ad6 @RJ implement cancel_query so queries can be manually cancelled from web …
authored
594 {
e1b7e40 @RJ first wave of api refactoring - builds and runs without any plugins o…
authored
595 pap->rs()->cancel_query( qid );
35d4ad6 @RJ implement cancel_query so queries can be manually cancelled from web …
authored
596 }
597 rq_ptr cq;
598 {
599 // removing from m_queries map means no-one can find and get a new shared_ptr given a qid:
600 boost::mutex::scoped_lock lock(m_mut_results);
601 if(!query_exists(qid)) return;
602 cq = rq(qid);
603 if(!cq || cq->cancelled()) return;
604 // this disables callbacks and marks it as cancelled:
605 cq->cancel();
606 m_queries.erase(qid);
e588be7 @RJ add timeout to auto-cancel stale queries
authored
607 // stop and cleanup timer:
608 boost::asio::deadline_timer * t = m_qidtimers[qid];
609 if(t)
610 {
611 t->cancel();
612 delete(t);
613 m_qidtimers.erase(qid);
614 }
615 // cleanup registered source ids -> playable items:
5d5a86a @jonocole Generalise playable_type class into a resolved_type class.
jonocole authored
616 vector< ri_ptr > results = cq->results();
617 BOOST_FOREACH( ri_ptr rip, results )
35d4ad6 @RJ implement cancel_query so queries can be manually cancelled from web …
authored
618 {
f17044a @RJ lazy creation of SS, re-enable script resolver
authored
619 m_sid2ri.erase( rip->id() );
35d4ad6 @RJ implement cancel_query so queries can be manually cancelled from web …
authored
620 }
621 }
622 // the RQ should not be referenced anywhere and will destruct now.
623 // a resolverservice may still be processing it, in which case it will destruct once done.
624 }
7760c19 @RJ initial checkin
authored
625
e588be7 @RJ add timeout to auto-cancel stale queries
authored
626 /// called when timer expires, so we can delete stale queries
627 void
628 Resolver::cancel_query_timeout(query_uid qid)
629 {
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
630 log::info() << "Stale timeout reached for QID: " << qid << endl;
e588be7 @RJ add timeout to auto-cancel stale queries
authored
631 rq_ptr rq;
632 {
633 boost::mutex::scoped_lock lock(m_mut_results);
634 if(!query_exists(qid)) return;
635 rq = this->rq(qid);
636 if(rq->cancelled()) return;
637 }
638 // check if it's stale enough to warrant cleaning up
639 time_t now;
640 time(&now);
641 time_t diff = now - rq->atime();
642 if( diff >= max_query_lifetime() ) // stale, clean it up
643 {
644 cancel_query( qid );
645 }
646 else if( m_qidtimers.find(qid) != m_qidtimers.end() ) // not stale, reset timer
647 {
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
648 log::info() << "Not stale, resetting timer." << endl;
e588be7 @RJ add timeout to auto-cancel stale queries
authored
649 m_qidtimers[qid]->expires_from_now(boost::posix_time::seconds(max_query_lifetime()-diff));
650 m_qidtimers[qid]->async_wait(boost::bind(&Resolver::cancel_query_timeout, this, qid));
651 }
652 }
653
654 /// gets all the current results for a query
655 /// but leaves query active. (ie, results may change later)
5d5a86a @jonocole Generalise playable_type class into a resolved_type class.
jonocole authored
656 vector< ri_ptr >
7760c19 @RJ initial checkin
authored
657 Resolver::get_results(query_uid qid)
658 {
001de9d @RJ pipeline update, better threading model in main resolver dispatch loop
authored
659 boost::mutex::scoped_lock lock(m_mut_results);
e588be7 @RJ add timeout to auto-cancel stale queries
authored
660 if(!query_exists(qid)) throw; // query was deleted
d9583cd @RJ parallel transfer fix, multihop transfer works
authored
661 return m_queries[qid]->results();
7760c19 @RJ initial checkin
authored
662 }
663
e588be7 @RJ add timeout to auto-cancel stale queries
authored
664 /// check how many results we found for this query id
7760c19 @RJ initial checkin
authored
665 int
666 Resolver::num_results(query_uid qid)
667 {
668 {
001de9d @RJ pipeline update, better threading model in main resolver dispatch loop
authored
669 boost::mutex::scoped_lock lock(m_mut_results);
7760c19 @RJ initial checkin
authored
670 if(query_exists(qid))
671 {
672 return m_queries[qid]->results().size();
673 }
674 }
675 cerr << "Query id '"<< qid <<"' does not exist" << endl;
676 return 0;
677 }
678
e588be7 @RJ add timeout to auto-cancel stale queries
authored
679 /// does this qid still exist?
7760c19 @RJ initial checkin
authored
680 bool
681 Resolver::query_exists(const query_uid & qid)
682 {
683 return (m_queries.find(qid) != m_queries.end());
684 }
685
e588be7 @RJ add timeout to auto-cancel stale queries
authored
686 /// true on success, false if it already exists.
7760c19 @RJ initial checkin
authored
687 bool
688 Resolver::add_new_query(boost::shared_ptr<ResolverQuery> rq)
689 {
8855279 @dougma remove playdar::utils::gen_uuid link requirement from plugin interfac…
dougma authored
690 if (rq->id().length() == 0) {
691 // create and assign an id to the request
d1267a0 @jonocole creating a new uuid generator for every auth request and every plugin…
jonocole authored
692 rq->set_id( gen_uuid() );
8855279 @dougma remove playdar::utils::gen_uuid link requirement from plugin interfac…
dougma authored
693 } else if (query_exists(rq->id())) {
694 return false;
695 }
696
7760c19 @RJ initial checkin
authored
697 m_queries[rq->id()] = rq;
7166a3b @dougma added mutex to protect Resolver::m_qidlist
dougma authored
698 {
699 boost::mutex::scoped_lock lock(m_mut_qidlist);
700 m_qidlist.push_front(rq->id());
701 }
7760c19 @RJ initial checkin
authored
702 return true;
703 }
704
705 boost::shared_ptr<ResolverQuery>
35d4ad6 @RJ implement cancel_query so queries can be manually cancelled from web …
authored
706 Resolver::rq(const query_uid & qid)
7760c19 @RJ initial checkin
authored
707 {
7166a3b @dougma added mutex to protect Resolver::m_qidlist
dougma authored
708 map< query_uid, rq_ptr >::iterator it = m_queries.find(qid);
709 return it == m_queries.end() ? boost::shared_ptr<ResolverQuery>() : it->second;
7760c19 @RJ initial checkin
authored
710 }
711
1494cb7 @RJ stats update, debug removal
authored
712 size_t
713 Resolver::num_seen_queries()
714 {
715 return m_queries.size();
716 }
717
40184ff @RJ dry out ss_curl, add support for 'extra_headers' to be sent when requ…
authored
718 /// this creates a SS from the ResolvedItem.
719 /// Typically it looks for the url field, and extra_headers etc.
f17044a @RJ lazy creation of SS, re-enable script resolver
authored
720 /// it checks our map of protocol -> SS factory where protocol is the bit before the : in urls.
0d9e797 @RJ SS attached and work now, but needs rethink anyway so SS generated on…
authored
721 ss_ptr
722 Resolver::get_ss(const source_uid & sid)
7760c19 @RJ initial checkin
authored
723 {
7dc09f1 @dougma make Resolver::get_ss work again
dougma authored
724 map< source_uid, ri_ptr >::iterator it = m_sid2ri.find(sid);
725 if (it != m_sid2ri.end()) {
726 ri_ptr rip( it->second );
727 if( rip->url().empty() ) return ss_ptr();
728
729 size_t offset = rip->url().find(':');
730 if( offset == string::npos ) return ss_ptr();
731
732 string p = rip->url().substr(0, offset);
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
733 log::info() << "get a SS("<<p<<") for url: " << rip->url() << endl;
7dc09f1 @dougma make Resolver::get_ss work again
dougma authored
734
40184ff @RJ dry out ss_curl, add support for 'extra_headers' to be sent when requ…
authored
735 map< std::string, boost::function<ss_ptr(std::string)> >::iterator itFac =
7dc09f1 @dougma make Resolver::get_ss work again
dougma authored
736 m_ss_factories.find(p);
737 if (itFac != m_ss_factories.end())
40184ff @RJ dry out ss_curl, add support for 'extra_headers' to be sent when requ…
authored
738 {
739 ss_ptr ss = itFac->second(rip->url());
740 // Any extra headers to add to the request for this URL?
741 // this is typically only used for http urls, but could be used
742 // for any protocol really, if the SS supports the concept.
743 vector<string> xh = rip->get_extra_headers();
744 BOOST_FOREACH( string h, xh )
745 {
44132c5 @RJ first stab at changing couts to log::info() etc.
authored
746 log::info() << "Extra header: " << h<< endl;
40184ff @RJ dry out ss_curl, add support for 'extra_headers' to be sent when requ…
authored
747 ss->set_extra_header( h );
748 }
749 return ss;
750 }
f17044a @RJ lazy creation of SS, re-enable script resolver
authored
751 }
752 return ss_ptr();
7760c19 @RJ initial checkin
authored
753 }
fdabb5f @RJ refactor local library resolver, move scoring algo into resolver
authored
754
b57a9b5 @lfranchi yay for more uncommenting and hoping
lfranchi authored
755 ri_ptr
756 Resolver::sid2ri( const source_uid& sid )
757 {
5f5e63a @lfranchi check if item is in map
lfranchi authored
758 map< source_uid, ri_ptr >::iterator it = m_sid2ri.find(sid);
759 if (it != m_sid2ri.end())
760 return it->second;
761 else
762 return ri_ptr();
763
b57a9b5 @lfranchi yay for more uncommenting and hoping
lfranchi authored
764 }
765
f17044a @RJ lazy creation of SS, re-enable script resolver
authored
766 template <class T>
767 boost::shared_ptr<T>
768 Resolver::ss_ptr_generator(string url)
769 {
770 return boost::shared_ptr<T>(new T(url));
771 }
772
88b502c @dougma initial comet code (compiles, untested, more todo)
dougma authored
773 bool
774 Resolver::create_comet_session(const std::string& sessionId, rq_callback_t cb)
775 {
ba9ac26 @dougma ref count CometSession instances via shared_ptr
dougma authored
776 if (!sessionId.length() || !cb)
777 return false;
778
88b502c @dougma initial comet code (compiles, untested, more todo)
dougma authored
779 boost::mutex::scoped_lock cometlock(m_comets_mutex);
780 // a new callback replaces the old callback
781 // todo: can we terminate the old comet session via the callback?
782 // todo: need a general mechanism to terminate (like when shutting down)
783 m_comets[sessionId] = cb;
784 return true;
785 }
786
ba9ac26 @dougma ref count CometSession instances via shared_ptr
dougma authored
787 void
788 Resolver::remove_comet_session(const std::string& sessionId)
789 {
790 boost::mutex::scoped_lock cometlock(m_comets_mutex);
791 m_comets.erase(sessionId);
792 }
793
11b9d5e @nova77 using playlist namespace everywhere
nova77 authored
794 }
Something went wrong with that request. Please try again.