lastFmAlbumArtFetcher.js
Go to the documentation of this file.
1 /*
2  *=BEGIN SONGBIRD GPL
3  *
4  * This file is part of the Songbird web player.
5  *
6  * Copyright(c) 2005-2010 POTI, Inc.
7  * http://www.songbirdnest.com
8  *
9  * This file may be licensed under the terms of of the
10  * GNU General Public License Version 2 (the ``GPL'').
11  *
12  * Software distributed under the License is distributed
13  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
14  * express or implied. See the GPL for the specific language
15  * governing rights and limitations.
16  *
17  * You should have received a copy of the GPL along with this
18  * program. If not, go to http://www.gnu.org/licenses/gpl.html
19  * or write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  *=END SONGBIRD GPL
23  */
24 
25 /* This XPCOM service knows how to ask Last.fm where to find art for
26  * music albums.
27  */
28 
29 const Cc = Components.classes;
30 const CC = Components.Constructor;
31 const Ci = Components.interfaces;
32 const Cr = Components.results;
33 const Cu = Components.utils;
34 
35 // The root of our preferences branch
36 const PREF_BRANCH = "extensions.albumart.lastfm.";
37 
38 // Importing helper modules
39 Cu.import('resource://app/jsmodules/sbProperties.jsm');
40 Cu.import("resource://app/jsmodules/StringUtils.jsm");
41 Cu.import("resource://app/jsmodules/ArrayConverter.jsm");
42 Cu.import("resource://app/jsmodules/sbCoverHelper.jsm");
43 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
44 
49 __defineGetter__("Application", function() {
50  delete this.Application;
51  return this.Application = Cc["@mozilla.org/fuel/application;1"]
52  .getService(Ci.fuelIApplication);
53 });
54 
55 // This is the fetcher module, you will need to change the name
56 // sbLastFMAlbumArtFetcher to you own in all instances.
57 function sbLastFMAlbumArtFetcher() {
58  // Use the last FM web api to make things easier.
59  this._lastFMWebApi = Cc['@songbirdnest.com/Songbird/webservices/last-fm;1']
60  .getService(Ci.sbILastFmWebServices);
61  this._strings = Cc["@mozilla.org/intl/stringbundle;1"]
62  .getService(Ci.nsIStringBundleService)
63  .createBundle("chrome://albumartlastfm/locale/albumartlastfm.properties");
64 };
65 sbLastFMAlbumArtFetcher.prototype = {
66  // XPCOM Magic
67  className: 'lastFMAlbumArtFetcher',
68  classDescription: 'LastFM Album Cover Fetcher',
69  classID: Components.ID('{8569316f-13a0-44d8-9d08-800999ed1f1c}'),
70  contractID: '@songbirdnest.com/album-art/lastfm-fetcher;1',
72  category: "songbird-album-art-fetcher"
73  }],
74 
75  // Variables
76  _shutdown: false,
77  _albumArtSourceList: null,
78  _isFetching: false,
79 
80 
81  _findImageForItem: function(aMediaItem, aCallback) {
82 
83  var albumName = aMediaItem.getProperty(SBProperties.albumName);
84  var artistName = aMediaItem.getProperty(SBProperties.artistName);
85  var albumArtistName = aMediaItem.getProperty(SBProperties.albumArtistName);
86  if (albumArtistName) {
87  artistName = albumArtistName;
88  }
89 
90  var arguments = Cc["@mozilla.org/hash-property-bag;1"]
91  .createInstance(Ci.nsIWritablePropertyBag2);
92  arguments.setPropertyAsAString("album", albumName);
93  arguments.setPropertyAsAString("artist", artistName);
94 
95  var self = this;
96  var apiResponse = function response(success, xml) {
97  // Indicate that fetcher is no longer fetching.
98  self._isFetching = false;
99 
100  // Abort if we are shutting down
101  if (self._shutdown) {
102  return;
103  }
104 
105  // Failed to get a good response back from the server :(
106  if (!success) {
107  aCallback(null);
108  return;
109  }
110 
111  var foundCover = null;
112  var imageSizes = ['large', 'medium', 'small'];
113  // Use XPath to parse out the image, we want to find the first image
114  // in the order of the imageSizes array above.
115  var nsResolver = xml.createNSResolver(xml.ownerDocument == null ?
116  xml.documentElement :
117  xml.ownerDocument.documentElement);
118  for (var iSize = 0; iSize < imageSizes.length; iSize++) {
119  var result = xml.evaluate("//image[@size='" + imageSizes[iSize] + "']",
120  xml,
121  nsResolver,
122  7, //XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
123  null);
124  if (result.snapshotLength > 0) {
125  foundCover = result.snapshotItem(0).textContent;
126  break;
127  }
128  }
129 
130  aCallback(foundCover);
131  };
132 
133  // Indicate that fetcher is fetching.
134  this._isFetching = true;
135 
136  this._lastFMWebApi.apiCall("album.getInfo", // method
137  arguments, // Property bag of strings
138  apiResponse); // Callback
139  },
140 
141  /*********************************
142  * sbIAlbumArtFetcher
143  ********************************/
144  // These are a bunch of getters for attributes in the sbICoverFetcher.idl
145  get shortName() {
146  return "lastfm"; // Change this to something that represents your fetcher
147  },
148 
149  // These next few use the .properties file to get the information
150  get name() {
151  return SBString(PREF_BRANCH + "name", null, this._strings);
152  },
153 
154  get description() {
155  return SBString(PREF_BRANCH + "description", null, this._strings);
156  },
157 
158  get isLocal() {
159  return false;
160  },
161 
162  // These are preference settings
163  get priority() {
164  return parseInt(Application.prefs.getValue(PREF_BRANCH + "priority", 10), 10);
165  },
166  set priority(aNewVal) {
167  return Application.prefs.setValue(PREF_BRANCH + "priority", aNewVal);
168  },
169 
170  get isEnabled() {
171  return Application.prefs.getValue(PREF_BRANCH + "enabled", false);
172  },
173  set isEnabled(aNewVal) {
174  return Application.prefs.setValue(PREF_BRANCH + "enabled", aNewVal);
175  },
176 
177  get albumArtSourceList() {
178  return this._albumArtSourceList;
179  },
180  set albumArtSourceList(aNewVal) {
181  this._albumArtSourceList = aNewVal;
182  },
183 
184  get isFetching() {
185  return this._isFetching;
186  },
187 
188  fetchAlbumArtForAlbum: function (aMediaItems, aListener) {
189  if (aMediaItems.length <= 0) {
190  // No Items so abort
191  Cu.reportError("No media items passed to fetchAlbumArtForAlbum.");
192  if (aListener) {
193  aListener.onAlbumResult(null, aMediaItems);
194  aListener.onSearchComplete(aMediaItems, null);
195  }
196  return;
197  }
198 
199  // Extract the first item and use that to get album information
200  var firstMediaItem = null;
201  try {
202  firstMediaItem = aMediaItems.queryElementAt(0, Ci.sbIMediaItem);
203  } catch (err) {
204  Cu.reportError(err);
205  aListener.onAlbumResult(null, aMediaItems);
206  aListener.onAlbumComplete(aMediaItems);
207  return;
208  }
209 
210  var returnResult = function (aImageLocation) {
211  if (aImageLocation) {
212  // Convert to an nsIURI
213  var ioService = Cc["@mozilla.org/network/io-service;1"]
214  .getService(Ci.nsIIOService);
215  var uri = null;
216  try {
217  uri = ioService.newURI(aImageLocation, null, null);
218  } catch (err) {
219  Cu.reportError("lastFM: Unable to convert to URI: [" + aImageLocation +
220  "] " + err);
221  uri = null;
222  }
223  aImageLocation = uri;
224  }
225  aListener.onAlbumResult(aImageLocation, aMediaItems);
226  aListener.onSearchComplete(aMediaItems);
227  };
228  var downloadCover = function (aFoundCover) {
229  if (aFoundCover) {
230  sbCoverHelper.downloadFile(aFoundCover, returnResult);
231  } else {
232  returnResult(null);
233  }
234  };
235  this._findImageForItem(firstMediaItem, downloadCover);
236  },
237 
238  fetchAlbumArtForTrack: function (aMediaItem, aListener) {
239  var returnResult = function (aImageLocation) {
240  if (aImageLocation) {
241  // Convert to an nsIURI
242  var ioService = Cc["@mozilla.org/network/io-service;1"]
243  .getService(Ci.nsIIOService);
244  var uri = null;
245  try {
246  uri = ioService.newURI(aImageLocation, null, null);
247  } catch (err) {
248  Cu.reportError("lastFM: Unable to convert to URI: [" + aImageLocation +
249  "] " + err);
250  uri = null;
251  }
252  aImageLocation = uri;
253  }
254  aListener.onTrackResult(aImageLocation, aMediaItem);
255  // We need to wrap the item in an nsIArray
256  var items = Cc["@songbirdnest.com/moz/xpcom/threadsafe-array;1"]
257  .createInstance(Ci.nsIMutableArray);
258  items.appendElement(aMediaItem, false);
259  aListener.onSearchComplete(items);
260  };
261  var downloadCover = function (aFoundCover) {
262  if (aFoundCover) {
263  sbCoverHelper.downloadFile(aFoundCover, returnResult);
264  } else {
265  returnResult(null);
266  }
267  };
268  this._findImageForItem(aMediaItem, downloadCover);
269  },
270 
271  shutdown: function () {
272  // Don't shutdown if still fetching.
273  if (this._isFetching)
274  return;
275 
276  this._shutdown = true;
277  },
278 
279  /*********************************
280  * nsISupports
281  ********************************/
282  QueryInterface: XPCOMUtils.generateQI([Ci.sbIAlbumArtFetcher])
283 }
284 
285 // This is for XPCOM to register this Fetcher as a module
286 function NSGetModule(compMgr, fileSpec) {
287  return XPCOMUtils.generateModule([sbLastFMAlbumArtFetcher]);
288 }
const Cu
var Application
Definition: sbAboutDRM.js:37
sbOSDControlService prototype className
sbDeviceFirmwareAutoCheckForUpdate prototype contractID
sbOSDControlService prototype QueryInterface
sbDeviceFirmwareAutoCheckForUpdate prototype classDescription
var ioService
sbDownloadDeviceServicePaneModule prototype shutdown
function SBString(aKey, aDefault, aStringBundle)
Definition: StringUtils.jsm:93
const CC
const PREF_BRANCH
const Cr
const Ci
return null
Definition: FeedWriter.js:1143
return!aWindow arguments!aWindow arguments[0]
var uri
Definition: FeedWriter.js:1135
sbDeviceFirmwareAutoCheckForUpdate prototype classID
sbWindowsAutoPlayServiceCfg _xpcom_categories
__defineGetter__("Application", function(){delete this.Application;return this.Application=Cc["@mozilla.org/fuel/application;1"].getService(Ci.fuelIApplication);})
const Cc