sbLastFmWebServices.js
Go to the documentation of this file.
1 /*
2 //
3 // BEGIN SONGBIRD GPL
4 //
5 // This file is part of the Songbird web player.
6 //
7 // Copyright(c) 2005-2008 POTI, Inc.
8 // http://songbirdnest.com
9 //
10 // This file may be licensed under the terms of of the
11 // GNU General Public License Version 2 (the "GPL").
12 //
13 // Software distributed under the License is distributed
14 // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
15 // express or implied. See the GPL for the specific language
16 // governing rights and limitations.
17 //
18 // You should have received a copy of the GPL along with this
19 // program. If not, go to http://www.gnu.org/licenses/gpl.html
20 // or write to the Free Software Foundation, Inc.,
21 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 // END SONGBIRD GPL
24 //
25 */
26 
27 // these constants make everything better
28 const Cc = Components.classes;
29 const CC = Components.Constructor;
30 const Ci = Components.interfaces;
31 const Cr = Components.results;
32 const Cu = Components.utils;
33 
34 // Last.fm API key, secret and URL
35 const API_KEY = 'ad68d3b69dee88a912b193a35d235a5b';
36 const API_SECRET = '5cb0c1f1cceb3bff561a62b718702175'; // obviously not secret
37 const API_URL = 'http://ws.audioscrobbler.com/2.0/';
38 
39 // Import some helper scripts
40 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
41 Cu.import("resource://app/jsmodules/sbProperties.jsm");
42 
43 // XPCOM Constants
44 const CONTRACTID = "@songbirdnest.com/Songbird/webservices/last-fm;1";
45 const CLASSNAME = "Songbird Last.FM WebService Interface";
46 const CID = Components.ID("{6582d596-95dd-4449-be8b-7793a15bdfa2}");
47 const IID = Ci.sbILastFmWebServices;
48 
49 // helper for enumerating enumerators.
50 function enumerate(enumerator, func) {
51  while(enumerator.hasMoreElements()) {
52  try {
53  func(enumerator.getNext());
54  } catch(e) {
55  Cu.reportError(e);
56  }
57  }
58 }
59 
60 // calculate a hex md5 digest thing
61 function md5(str) {
62  var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
63  .createInstance(Ci.nsIScriptableUnicodeConverter);
64 
65  converter.charset = "UTF-8";
66  // result is an out parameter,
67  // result.value will contain the array length
68  var result = {};
69  // data is an array of bytes
70  var data = converter.convertToByteArray(str, result);
71  var ch = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
72  ch.init(ch.MD5);
73  ch.update(data, data.length);
74  var hash = ch.finish(false);
75 
76  // return the two-digit hexadecimal code for a byte
77  function toHexString(charCode) {
78  return ("0" + charCode.toString(16)).slice(-2);
79  }
80 
81  // convert the binary hash data to a hex string.
82  var s = [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
83  return s;
84 }
85 
86 // urlencode an object's keys & values
87 function urlencode(o) {
88  s = '';
89  for (var k in o) {
90  var v = o[k];
91  if (s.length) { s += '&'; }
92  s += encodeURIComponent(k) + '=' + encodeURIComponent(v);
93  }
94  return s;
95 }
96 
97 // make an HTTP POST request
98 function POST(url, params, onload, onerror) {
99  var xhr = null;
100  try {
101  // create the XMLHttpRequest object
102  xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
103  // don't tie it to a XUL window
104  xhr.mozBackgroundRequest = true;
105  // open the connection to the url
106  xhr.open('POST', url, true);
107  // set up event handlers
108  xhr.onload = function(event) { onload(xhr); }
109  xhr.onerror = function(event) { onerror(xhr); }
110  // we're always sending url encoded parameters
111  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
112  // send url encoded parameters
113  xhr.send(urlencode(params));
114  // pass the XHR back to the caller
115  } catch(e) {
116  Cu.reportError(e);
117  onerror(xhr);
118  }
119  return xhr;
120 }
121 
127  // our interface is really lightweight - make the service available as a JS
128  // object so we can avoid the IDL / XPConnect complexity.
129  this.wrappedJSObject = this;
130 
131  // report errors using the console service
132  this._console = Cc["@mozilla.org/consoleservice;1"]
133  .getService(Ci.nsIConsoleService);
134 
135 }
136 sbLastFmWebServices.prototype = {
137  constructor: sbLastFmWebServices, // Constructor to this object
139  classID: CID,
142  [{
143  category: "app-startup",
144  entry: "webservices-lastfm",
145  value: "service," + CONTRACTID
146  }],
147 
148  // Error reporting
149  log: function sbLastFmWebServices_log(message) {
150  this._console.logStringMessage('[last-fm] '+message);
151  },
152 
156  apiCall: function sbLastFmWebServices_apiCall(aMethod,
157  aArguments,
158  aCallback) {
159  // make a new Last.fm Web Service API call
160  // see: http://www.last.fm/api/rest
161  // note: this isn't really REST.
162 
163  function callback(success, response) {
164  if (typeof(aCallback) == 'function') {
165  aCallback(success, response);
166  } else {
167  aCallback.responseReceived(success, response);
168  }
169  }
170 
171  // create an object to hold the HTTP params
172  var post_params = new Object();
173 
174  // load the params from the nsIPropertyBag
175  if (aArguments instanceof Ci.nsIPropertyBag) {
176  enumerate(aArguments.enumerator, function(item) {
177  item.QueryInterface(Ci.nsIProperty);
178  post_params[item.name] = item.value;
179  })
180  } else {
181  // or from the object
182  for (var k in aArguments) { post_params[k] = aArguments[k]; }
183  }
184 
185  // set the method and API key
186  post_params.method = aMethod;
187  post_params.api_key = API_KEY;
188 
189  // calculate the signature...
190  // put the key/value pairs in an array
191  var sorted_params = new Array();
192  for (var k in post_params) {
193  sorted_params.push(k+post_params[k])
194  }
195  // sort them
196  sorted_params.sort();
197  // join them into a string
198  sorted_params = sorted_params.join('');
199  // hash them with the "secret" to get the API signature
200  post_params.api_sig = md5(sorted_params+API_SECRET);
201 
202  // post the request
203  var self = this;
204  POST(API_URL, post_params, function (xhr) {
205  if (!xhr.responseXML) {
206  // we expect all API responses to have XML
207  self.log('Last.fm WebServices Error: No valid XML response.');
208  callback(false, null);
209  return;
210  }
211 
212  // Check for an error status
213  var nsResolver = xhr.responseXML.createNSResolver(
214  xhr.responseXML.ownerDocument == null ?
215  xhr.responseXML.documentElement :
216  xhr.responseXML.ownerDocument.documentElement);
217  var result = xhr.responseXML.evaluate("/lfm/@status",
218  xhr.responseXML,
219  nsResolver,
220  2, //XPathResult.STRING_TYPE,
221  null);
222  if (result.stringValue && result.stringValue == 'failed') {
223  result = xhr.responseXML.evaluate("/lfm/error",
224  xhr.responseXML,
225  nsResolver,
226  2, //XPathResult.STRING_TYPE,
227  null);
228  var error = "Unknown Error";
229  if (result.stringValue) {
230  error = result.stringValue;
231  }
232  self.log('Last.fm WebServices Error: ' + error);
233  callback(false, xhr.responseXML);
234  return;
235  }
236 
237  // all should be good!
238  callback(true, xhr.responseXML);
239  }, function (xhr) {
240  self.log('Last.fm WebServices Error: Bad response from server.');
241  callback(false, null);
242  });
243  },
244 
248  QueryInterface: XPCOMUtils.generateQI([IID])
249 }
250 
257 function NSGetModule(comMgr, fileSpec) {
258  return XPCOMUtils.generateModule([sbLastFmWebServices]);
259 } // NSGetModule
classDescription entry
Definition: FeedWriter.js:1427
nsString encodeURIComponent(const nsString &c)
window onload
const CONTRACTID
const Cu
function sbLastFmWebServices()
function log(s)
sbDeviceFirmwareAutoCheckForUpdate prototype contractID
const API_KEY
const CLASSNAME
var event
var converter
function enumerate(enumerator, func)
const CC
sbOSDControlService prototype QueryInterface
sbDeviceFirmwareAutoCheckForUpdate prototype classDescription
const API_URL
function POST(url, params, onload, onerror)
const IID
Browser Features xhr
const Cc
DataRemote prototype constructor
function hash(str)
Definition: sbAboutDRM.js:40
grep callback
function md5(str)
GstMessage * message
return null
Definition: FeedWriter.js:1143
function urlencode(o)
function url(spec)
countRef value
Definition: FeedWriter.js:1423
const API_SECRET
const Cr
sbDeviceFirmwareAutoCheckForUpdate prototype classID
sbWindowsAutoPlayServiceCfg _xpcom_categories
const CID
const Ci
observe data
Definition: FeedWriter.js:1329
_getSelectedPageStyle s i