sbDynamicPlaylist.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-2009 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 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
28 Components.utils.import("resource://app/jsmodules/ArrayConverter.jsm");
29 Components.utils.import("resource://app/jsmodules/sbProperties.jsm");
30 Components.utils.import("resource://app/jsmodules/sbLibraryUtils.jsm");
31 
32 const Cc = Components.classes;
33 const Ci = Components.interfaces;
34 const Cr = Components.results;
35 
36 const SB_NS = "http://songbirdnest.com/data/1.0#";
37 const SB_PROP_ISSUBSCRIPTION = SB_NS + "isSubscription";
38 const SB_PROP_SUBSCRIPTIONURL = SB_NS + "subscriptionURL";
39 const SB_PROP_SUBSCRIPTIONINTERVAL = SB_NS + "subscriptionInterval";
40 const SB_PROP_SUBSCRIPTIONNEXTRUN = SB_NS + "subscriptionNextRun";
41 const SB_PROP_DESTINATION = SB_NS + "destination"
42 
43 const SB_FINAL_UI_STARTUP_TOPIC = "final-ui-startup";
44 const SB_DEVICE_MANAGER_READY_TOPIC = "songbird-device-manager-ready";
45 const SB_LIBRARY_MANAGER_READY_TOPIC = "songbird-library-manager-ready";
46 const SB_LIBRARY_MANAGER_BEFORE_SHUTDOWN_TOPIC = "songbird-library-manager-before-shutdown";
47 
48 const UPDATE_INTERVAL = 60 * 1000;
49 
50 function d(s) {
51  //dump("------------------> sbDynamicPlaylistService " + s + "\n");
52 }
53 
54 function TRACE(s) {
55  //dump("------------------> " + s + "\n");
56 }
57 
58 // XXXsteve Any time we get a media item's guid and want to use it as an
59 // object property, run it through this function first. This will force the
60 // string to be a javascript land string rather than a string dependent on the
61 // buffer held by the media item. This it to prevent crashes when the atom
62 // created from that string gets deleted at shutdown. See bmo 391590
63 function FIX(s) {
64  var g = "x" + s;
65  g = g.substr(1);
66  return g;
67 }
68 
70 {
71  this._started = false;
72  this._scheduledLists = {};
73  this._ignoreLibraryNotifications = {};
74  this._libraryBatch = new LibraryUtils.MultiBatchHelper();
75  this._libraryRefreshPending = {};
76 
77  var obs = Cc["@mozilla.org/observer-service;1"]
78  .getService(Ci.nsIObserverService);
79  //obs.addObserver(this, SB_LIBRARY_MANAGER_READY_TOPIC, false);
80  obs.addObserver(this, SB_DEVICE_MANAGER_READY_TOPIC, false);
81  obs.addObserver(this, SB_LIBRARY_MANAGER_BEFORE_SHUTDOWN_TOPIC, false);
82 }
83 
85  classDescription: "Dynamic Playlist Service",
86  classID: Components.ID("{10a07ef5-8ab6-4728-9172-4e609f65b4a2}"),
87  contractID: "@songbirdnest.com/Songbird/Library/DynamicPlaylistService;1"
88 }
89 
90 sbDynamicPlaylistService.prototype.QueryInterface =
91 XPCOMUtils.generateQI([
92  Ci.sbIDynamicPlaylistService,
93  Ci.nsIObserver,
94  Ci.nsITimerCallback,
95  Ci.sbILibraryManagerListener,
96  Ci.sbIMediaListListener
97 ]);
98 
99 /*
100  * Startup method for the dynamic playlist service
101  */
102 sbDynamicPlaylistService.prototype._startup =
103 function sbDynamicPlaylistService__startup()
104 {
105  TRACE("sbDynamicPlaylistService::_startup\n");
106 
107  if (this._started) {
108  return;
109  }
110 
111  // Register properties
112  var propMan = Cc["@songbirdnest.com/Songbird/Properties/PropertyManager;1"]
113  .getService(Ci.sbIPropertyManager);
114 
115  var prop = Cc["@songbirdnest.com/Songbird/Properties/Info/Boolean;1"]
116  .createInstance(Ci.sbIBooleanPropertyInfo);
117  prop.id = SB_PROP_ISSUBSCRIPTION;
118  prop.userViewable = false;
119  prop.userEditable = false;
120  prop.remoteReadable = true;
121  prop.remoteWritable = true;
122  propMan.addPropertyInfo(prop);
123 
124  prop = Cc["@songbirdnest.com/Songbird/Properties/Info/URI;1"]
125  .createInstance(Ci.sbIURIPropertyInfo);
126  prop.id = SB_PROP_SUBSCRIPTIONURL;
127  prop.userViewable = false;
128  prop.userEditable = false;
129  prop.remoteReadable = true;
130  prop.remoteWritable = true;
131  propMan.addPropertyInfo(prop);
132 
133  var prop = Cc["@songbirdnest.com/Songbird/Properties/Info/Number;1"]
134  .createInstance(Ci.sbINumberPropertyInfo);
136  prop.userViewable = false;
137  prop.userEditable = false;
138  prop.remoteReadable = true;
139  prop.remoteWritable = true;
140  prop.minValue = 0;
141  propMan.addPropertyInfo(prop);
142 
143  var prop = Cc["@songbirdnest.com/Songbird/Properties/Info/Number;1"]
144  .createInstance(Ci.sbINumberPropertyInfo);
145  prop.id = SB_PROP_SUBSCRIPTIONNEXTRUN;
146  prop.userViewable = false;
147  prop.userEditable = false;
148  prop.remoteReadable = true;
149  prop.remoteWritable = true;
150  prop.minValue = 0;
151  propMan.addPropertyInfo(prop);
152 
153  // Set up our timer
154  this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
155  this._timer.initWithCallback(this,
157  Ci.nsITimer.TYPE_REPEATING_SLACK);
158 
159  // Attach an observer so we can track library additions / removals
160  var libraryManager = Cc["@songbirdnest.com/Songbird/library/Manager;1"]
161  .getService(Ci.sbILibraryManager);
162  libraryManager.addListener(this);
163 
164  // Schedule all of the dynamic media lists in all libraries
165  var libraries = libraryManager.getLibraries();
166  while (libraries.hasMoreElements()) {
167  var library = libraries.getNext();
168  this.onLibraryRegistered(library);
169  }
170 
171  // Do a queue run just in care some things should have been refreshed while
172  // we were off
173  this._updateSubscriptions(false);
174 
175  this._started = true;
176 }
177 
178 sbDynamicPlaylistService.prototype._shutdown =
179 function sbDynamicPlaylistService__shutdown()
180 {
181  var obs = Cc["@mozilla.org/observer-service;1"]
182  .getService(Ci.nsIObserverService);
183  //obs.removeObserver(this, SB_LIBRARY_MANAGER_READY_TOPIC);
184  obs.removeObserver(this, SB_DEVICE_MANAGER_READY_TOPIC);
185  obs.removeObserver(this, SB_LIBRARY_MANAGER_BEFORE_SHUTDOWN_TOPIC);
186 
187  if (this._started) {
188  this._timer.cancel();
189  this._timer = null;
190 
191  // Stop listening to the library manager
192  var libraryManager = Cc["@songbirdnest.com/Songbird/library/Manager;1"]
193  .getService(Ci.sbILibraryManager);
194  libraryManager.removeListener(this);
195 
196  // Stop listening to each library
197  var libraries = libraryManager.getLibraries();
198  while (libraries.hasMoreElements()) {
199  this.onLibraryUnregistered(libraries.getNext());
200  }
201  }
202 
203  this._started = false;
204 }
205 
206 sbDynamicPlaylistService.prototype._scheduleLibrary =
207 function sbDynamicPlaylistService__scheduleLibrary(aLibrary)
208 {
209  // Add all the media lists that are subscriptions to the _scheduledList
210  // array
211  var self = this;
212  var listener = {
213  onEnumerationBegin: function() {
214  },
215  onEnumeratedItem: function(list, item) {
216  self._scheduledLists[FIX(item.guid)] = item;
217  },
218  onEnumerationEnd: function() {
219  }
220  };
221 
222  var pa = Cc["@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1"]
223  .createInstance(Ci.sbIMutablePropertyArray);
224  pa.appendProperty(SB_NS + "isList", "1");
225  pa.appendProperty(SB_PROP_ISSUBSCRIPTION, "1");
226 
227  aLibrary.enumerateItemsByProperties(pa, listener);
228 
229 }
230 
231 sbDynamicPlaylistService.prototype._isDynamicPlaylist =
232 function sbDynamicPlaylistService__isDynamicPlaylist(aMediaList)
233 {
234  return aMediaList.getProperty(SB_PROP_ISSUBSCRIPTION) == "1";
235 }
236 
237 sbDynamicPlaylistService.prototype._removeListsFromLibrary =
238 function sbDynamicPlaylistService__removeListsFromLibrary(aLibrary)
239 {
240  for each (var list in this._scheduledLists) {
241  if (list.library.equals(aLibrary)) {
242  delete this._scheduledLists[FIX(list.guid)];
243  }
244  }
245 }
246 
247 sbDynamicPlaylistService.prototype._updateSubscriptions =
248 function sbDynamicPlaylistService__updateSubscriptions(aForce)
249 {
250  var now = (new Date()).getTime() * 1000;
251  for each (var list in this._scheduledLists) {
252 
253  var nextRun = list.getProperty(SB_PROP_SUBSCRIPTIONNEXTRUN);
254  var interval = list.getProperty(SB_PROP_SUBSCRIPTIONINTERVAL);
255 
256  // If there is no next run set, set it
257  if (!nextRun) {
258  nextRun = this._setNextRun(list);
259  }
260 
261  // Update this list if we are forced or if we have an interval and the
262  // current time is after the next run
263  if (aForce || (interval && now > nextRun)) {
264  try {
265  this._updateList(list);
266  }
267  catch (e) {
268  // Log and continue
269  Components.utils.reportError(e);
270  }
271  }
272  }
273 }
274 
275 sbDynamicPlaylistService.prototype._updateList =
276 function sbDynamicPlaylistService__updateList(aList)
277 {
278  // Get the playlist reader and load the tracks from this url
279  var manager = Cc["@songbirdnest.com/Songbird/PlaylistReaderManager;1"]
280  .getService(Ci.sbIPlaylistReaderManager);
281  var listener = Cc["@songbirdnest.com/Songbird/PlaylistReaderListener;1"]
282  .createInstance(Ci.sbIPlaylistReaderListener);
283 
284  var ioService = Cc["@mozilla.org/network/io-service;1"]
285  .getService(Ci.nsIIOService);
286  var uri = ioService.newURI(aList.getProperty(SB_PROP_SUBSCRIPTIONURL), null, null);
287 
288  var observer = new sbPlaylistReaderListenerObserver(this, aList);
289  listener.observer = observer;
290 
291  manager.loadPlaylist(uri, aList, null, true, listener);
292 }
293 
294 sbDynamicPlaylistService.prototype._setNextRun =
295 function sbDynamicPlaylistService__setNextRun(aList)
296 {
297  var now = (new Date()).getTime() * 1000;
298  var interval = aList.getProperty(SB_PROP_SUBSCRIPTIONINTERVAL);
299 
300  // Interval is in seconds, next run is in micro seconds
301  var nextRun = now + (interval * 1000 * 1000);
302  aList.setProperty(SB_PROP_SUBSCRIPTIONNEXTRUN, nextRun);
303  return nextRun;
304 }
305 
306 sbDynamicPlaylistService.prototype._beginIgnore =
307 function sbDynamicPlaylistService__beginIgnore(aLibrary)
308 {
309  var count = this._ignoreLibraryNotifications[FIX(aLibrary.guid)];
310  if (count)
311  this._ignoreLibraryNotifications[FIX(aLibrary.guid)] = count + 1;
312  else
313  this._ignoreLibraryNotifications[FIX(aLibrary.guid)] = 1;
314 }
315 
316 sbDynamicPlaylistService.prototype._endIgnore =
317 function sbDynamicPlaylistService__endIgnore(aLibrary)
318 {
319  var count = this._ignoreLibraryNotifications[FIX(aLibrary.guid)];
320  if (count)
321  this._ignoreLibraryNotifications[FIX(aLibrary.guid)] = count - 1;
322  else
323  throw Cr.NS_ERROR_FAILURE;
324 }
325 
326 sbDynamicPlaylistService.prototype._ignore =
327 function sbDynamicPlaylistService__ignore(aLibrary)
328 {
329  var count = this._ignoreLibraryNotifications[FIX(aLibrary.guid)];
330  return count && count > 0;
331 }
332 
333 // sbIDynamicPlaylistService
334 sbDynamicPlaylistService.prototype.createList =
335 function sbDynamicPlaylistService_createList(aLibrary,
336  aUri,
337  aIntervalSeconds,
338  aDestinationDirectory)
339 {
340  if (!aLibrary ||
341  !aUri ||
342  aIntervalSeconds == 0 ||
343  (aDestinationDirectory && !aDestinationDirectory.isDirectory()))
344  throw Cr.NS_ERROR_INVALID_ARG;
345 
346  try {
347  this._beginIgnore(aLibrary);
348  var list = aLibrary.createMediaList("dynamic");
349  this._scheduledLists[FIX(list.guid)] = list;
350  this.updateList(list, aUri, aIntervalSeconds, aDestinationDirectory);
351  }
352  finally {
353  this._endIgnore(aLibrary);
354  }
355  return list;
356 }
357 
358 sbDynamicPlaylistService.prototype.createPodcast =
359 function sbDynamicPlaylistService_createPodcast(aLibrary,
360  aUri,
361  aProperties)
362 {
363  if (!aLibrary || !aUri)
364  throw Cr.NS_ERROR_INVALID_ARG;
365 
366  try {
367  // Ignore library changes.
368  this._beginIgnore(aLibrary);
369 
370  // Make a deep mutable copy of any properties.
371  var properties =
372  Cc["@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1"]
373  .createInstance(Ci.sbIMutablePropertyArray);
374  if (aProperties) {
375  var propertyCount = aProperties.length;
376  for (var i = 0; i < propertyCount; i++) {
377  var property = aProperties.getPropertyAt(i);
378  properties.appendProperty(property.id, property.value);
379  }
380  }
381 
382  // Create the podcast media list.
383  properties.appendProperty(SBProperties.customType, "podcast");
384  var list = aLibrary.createMediaList("dynamic", properties);
385 
386  // Use the URI host for the podcast name for now.
387  //XXXeps need to read the podcast name from the feed.
388  list.name = aUri.host;
389 
390  //XXXeps set default podcast settings unless set in aProperties and add to
391  //XXXepsscheduled list
392  }
393  finally {
394  // Stop ignoring library changes.
395  this._endIgnore(aLibrary);
396  }
397  return list;
398 }
399 
400 sbDynamicPlaylistService.prototype.updateList =
401 function sbDynamicPlaylistService_updateList(aMediaList,
402  aUri,
403  aIntervalSeconds,
404  aDestinationDirectory)
405 {
406  if (!aMediaList ||
407  !this._isDynamicPlaylist(aMediaList) ||
408  !aUri ||
409  aIntervalSeconds == 0 ||
410  (aDestinationDirectory && !aDestinationDirectory.isDirectory()))
411  throw Cr.NS_ERROR_INVALID_ARG;
412 
413  try {
414  this._beginIgnore(aMediaList.library);
415  aMediaList.setProperty(SB_PROP_SUBSCRIPTIONURL, aUri.spec);
416  aMediaList.setProperty(SB_PROP_SUBSCRIPTIONINTERVAL, aIntervalSeconds);
417 
418  if (aDestinationDirectory) {
419  var ioService = Cc["@mozilla.org/network/io-service;1"]
420  .getService(Ci.nsIIOService);
421  var destinationUri = ioService.newFileURI(aDestinationDirectory);
422  aMediaList.setProperty(SB_PROP_DESTINATION, destinationUri.spec);
423  }
424  }
425  finally {
426  this._endIgnore(aMediaList.library);
427  }
428 
429 }
430 
431 sbDynamicPlaylistService.prototype.updateAllNow =
432 function sbDynamicPlaylistService_updateAllNow()
433 {
434  this._updateSubscriptions(true);
435 }
436 
437 sbDynamicPlaylistService.prototype.updateNow =
438 function sbDynamicPlaylistService_updateNow(aMediaList)
439 {
440  var interval = aMediaList.getProperty(SB_PROP_SUBSCRIPTIONINTERVAL);
441  var url = aMediaList.getProperty(SB_PROP_SUBSCRIPTIONURL);
442  if (interval && url)
443  this._updateList(aMediaList);
444  else
445  throw Cr.NS_ERROR_INVALID_ARG;
446 }
447 
448 sbDynamicPlaylistService.prototype.__defineGetter__("scheduledLists",
449 function sbDynamicPlaylistService_scheduledLists()
450 {
451  var a = [];
452  for each (var list in this._scheduledLists) {
453  a.push(list);
454  }
455 
456  return ArrayConverter.enumerator(a);
457 });
458 
459 // sbILibraryManagerListener
460 sbDynamicPlaylistService.prototype.onLibraryRegistered =
461 function sbDynamicPlaylistService_onLibraryRegistered(aLibrary)
462 {
463  TRACE("sbDynamicPlaylistService::onLibraryRegistered");
464 
465  // If a library is registered, attach a listener so we can be notified of
466  // media item change notifications.
467  var filter = SBProperties.createArray([[SB_PROP_ISSUBSCRIPTION, null]], false);
468  aLibrary.addListener(this,
469  false,
470  Ci.sbIMediaList.LISTENER_FLAGS_ALL &
471  ~Ci.sbIMediaList.LISTENER_FLAGS_AFTERITEMREMOVED,
472  filter);
473 
474  // Schedule all of the dynamic media lists in this library
475  this._scheduleLibrary(aLibrary);
476 }
477 
478 sbDynamicPlaylistService.prototype.onLibraryUnregistered =
479 function sbDynamicPlaylistService_onLibraryUnregistered(aLibrary)
480 {
481  TRACE("sbDynamicPlaylistService::onLibraryUnregistered");
482  // Remove all the lists that are in this library
483  if (aLibrary instanceof Ci.sbILibrary) {
484  this._removeListsFromLibrary(aLibrary);
485 
486  // Remove our listener from this library
487  aLibrary.removeListener(this);
488  }
489 }
490 
491 // sbIMediaListListener
492 sbDynamicPlaylistService.prototype.onItemAdded =
493 function sbDynamicPlaylistService_onItemAdded(aMediaList,
494  aMediaItem,
495  aIndex)
496 {
497  if (this._ignore(aMediaList.library))
498  return;
499 
500  // If we are in a batch, we are going to refresh the list of dynamic
501  // playlists when the batch ends, so we don't need any more notifications
502  if (this._libraryBatch.isActive(aMediaList.library)) {
503  this._libraryRefreshPending[FIX(aMediaList.library.guid)] = true;
504  return true;
505  }
506 
507  // Is this new item a dynamic media list?
508  if (this._isDynamicPlaylist(aMediaItem)) {
509 
510  // If there is no next run time, set one
511  if (aMediaItem.getProperty(SB_PROP_SUBSCRIPTIONNEXTRUN)) {
512  this._setNextRun(aMediaItem);
513  }
514  this._scheduledLists[FIX(aMediaItem.guid)] = aMediaItem;
515  d("A new dynamic playlist was added");
516  }
517 
518 }
519 
520 sbDynamicPlaylistService.prototype.onBeforeItemRemoved =
521 function sbDynamicPlaylistService_onBeforeItemRemoved(aMediaList,
522  aMediaItem,
523  aIndex)
524 {
525  if (this._ignore(aMediaList.library))
526  return;
527 
528  // If we are in a batch, we are going to refresh the list of dynamic
529  // playlists when the batch ends, so we don't need any more notifications
530  if (this._libraryBatch.isActive(aMediaList.library)) {
531  this._libraryRefreshPending[FIX(aMediaList.library.guid)] = true;
532  return true;
533  }
534 
535  delete this._scheduledLists[FIX(aMediaItem.guid)];
536 }
537 
538 sbDynamicPlaylistService.prototype.onAfterItemRemoved =
539 function sbDynamicPlaylistService_onAfterItemRemoved(aMediaList,
540  aMediaItem,
541  aIndex)
542 {
543  // do nothing
544 }
545 
546 sbDynamicPlaylistService.prototype.onItemUpdated =
547 function sbDynamicPlaylistService_onItemUpdated(aMediaList,
548  aMediaItem,
549  aProperties)
550 {
551  if (this._ignore(aMediaList.library))
552  return;
553 
554  // If we are in a batch, we are going to refresh the list of dynamic
555  // playlists when the batch ends, so we don't need any more notifications
556  if (this._libraryBatch.isActive(aMediaList.library)) {
557  this._libraryRefreshPending[FIX(aMediaList.library.guid)] = true;
558  return true;
559  }
560 
561  // Make sure this list is scheduled
562  this._scheduledLists[FIX(aMediaItem.guid)] = aMediaItem;
563 }
564 
565 sbDynamicPlaylistService.prototype.onItemMoved =
566 function sbDynamicPlaylistService_onItemMoved(aMediaList,
567  aFromIndex,
568  aToIndex)
569 {
570 }
571 
572 
573 sbDynamicPlaylistService.prototype.onBeforeListCleared =
574 function sbDynamicPlaylistService_onBeforeListCleared(aMediaList,
575  aExcludeLists)
576 {
577  return true;
578 }
579 
580 sbDynamicPlaylistService.prototype.onListCleared =
581 function sbDynamicPlaylistService_onListCleared(aMediaList,
582  aExcludeLists)
583 {
584  if (this._ignore(aMediaList.library))
585  return;
586 
587  // If we are in a batch, we are going to refresh the list of dynamic
588  // playlists when the batch ends, so we don't need any more notifications
589  if (this._libraryBatch.isActive(aMediaList.library)) {
590  this._libraryRefreshPending[FIX(aMediaList.library.guid)] = true;
591  return true;
592  }
593 
594  // Clear all schedueld items from this library
595  this._removeListsFromLibrary(aMediaList.library);
596 }
597 
598 sbDynamicPlaylistService.prototype.onBatchBegin =
599 function sbDynamicPlaylistService_onBatchBegin(aMediaList)
600 {
601  this._libraryBatch.begin(aMediaList.library);
602 }
603 
604 sbDynamicPlaylistService.prototype.onBatchEnd =
605 function sbDynamicPlaylistService_onBatchEnd(aMediaList)
606 {
607  var library = aMediaList.library;
608  this._libraryBatch.end(library);
609 
610  if (!this._libraryBatch.isActive(library)) {
611 
612  // If there is a refresh pending for this library, do it
613  if (this._libraryRefreshPending[FIX(library.guid)]) {
614 
615  d("Refreshing dynamic playlists in library " + library);
616  this._removeListsFromLibrary(library);
617  this._scheduleLibrary(library);
618 
619  this._libraryRefreshPending[FIX(library.guid)] = false;
620  }
621  }
622 }
623 
624 // nsITimerCallback
625 sbDynamicPlaylistService.prototype.notify =
626 function sbDynamicPlaylistService_notify(aTimer)
627 {
628  this._updateSubscriptions(false);
629 }
630 
631 // nsIObserver
632 sbDynamicPlaylistService.prototype.observe =
633 function sbDynamicPlaylistService_observe(aSubject, aTopic, aData)
634 {
635  if (aTopic == SB_DEVICE_MANAGER_READY_TOPIC) {
636  this._startup();
637  }
638  else if(aTopic == SB_LIBRARY_MANAGER_BEFORE_SHUTDOWN_TOPIC) {
639  this._shutdown();
640  }
641 }
642 
643 function sbPlaylistReaderListenerObserver(aService, aList) {
644  this._service = aService;
645  this._list = aList;
646  this._oldLength = aList.length;
647 }
648 
649 sbPlaylistReaderListenerObserver.prototype.observe =
650 function sbPlaylistReaderListenerObserver_observe(aSubject, aTopic, aData)
651 {
652  var ioService = Cc["@mozilla.org/network/io-service;1"]
653  .getService(Ci.nsIIOService);
654  var uri = ioService.newURI(this._list.getProperty(SB_PROP_SUBSCRIPTIONURL), null, null);
655 
656  TRACE("Updated list with uri " + uri.spec);
657 
658  // Schedule the next run for this list
659  this._service._setNextRun(this._list);
660 
661  // Check to see if this list has a custom download destination, and that
662  // the destination is a directory
663  var destination = this._list.getProperty(SB_PROP_DESTINATION);
664  var destinationDir;
665  if (destination) {
666  try {
667  destinationDir = ioService.newURI(destination, null, null)
668  .QueryInterface(Ci.nsIFileURL).file;
669  if (!destinationDir.isDirectory())
670  destinationDir = null;
671  }
672  catch (e) {
673  // If we couldn't get a destination dir, use the default
674  destinationDir = null;
675  }
676  }
677 
678  // Start a metadata job for the newly added items. If there is a custom
679  // destination, update each new item with it
680  var array = Cc["@songbirdnest.com/moz/xpcom/threadsafe-array;1"].createInstance(Ci.nsIMutableArray);
681  for (var i = this._oldLength; i < this._list.length; i++) {
682  var item = this._list.getItemByIndex(i);
683  if (destinationDir) {
684  var itemUri = item.contentSrc;
685 
686  // If this is not a nsIURL, let the download manager figure it out
687  if (itemUri instanceof Ci.nsIURL) {
688  var dest = destinationDir.clone();
689  dest.append(itemUri.QueryInterface(Ci.nsIURL).fileName);
690  var destUri = ioService.newFileURI(dest);
691  item.setProperty(SB_PROP_DESTINATION, destUri.spec);
692  }
693  }
694  array.appendElement(item, false);
695  }
696 
697  var metadataService = Cc["@songbirdnest.com/Songbird/FileMetadataService;1"]
698  .getService(Ci.sbIFileMetadataService);
699  var metadataJob = metadataService.read(array);
700 
701  // Download the new items
702  var ddh =
703  Cc["@songbirdnest.com/Songbird/DownloadDeviceHelper;1"]
704  .getService(Ci.sbIDownloadDeviceHelper);
705  ddh.downloadSome(array.enumerate());
706 }
707 
708 function NSGetModule(compMgr, fileSpec) {
709 
710  return XPCOMUtils.generateModule([
712  ],
713  function(aCompMgr, aFileSpec, aLocation) {
714  XPCOMUtils.categoryManager.addCategoryEntry(
715  "app-startup",
716  sbDynamicPlaylistService.prototype.classDescription,
717  "service," + sbDynamicPlaylistService.prototype.contractID,
718  true,
719  true);
720  });
721 }
722 
const SB_LIBRARY_MANAGER_READY_TOPIC
const SB_PROP_ISSUBSCRIPTION
inArray array
const Ci
const SB_DEVICE_MANAGER_READY_TOPIC
const Cc
sbDeviceFirmwareAutoCheckForUpdate prototype contractID
sbDynamicPlaylistService prototype _startup
function FIX(s)
const SB_PROP_DESTINATION
sbDeviceFirmwareAutoCheckForUpdate prototype classDescription
var ioService
sbDeviceFirmwareAutoCheckForUpdate prototype _timer
function d(s)
var count
Definition: test_bug7406.js:32
function TRACE(s)
const SB_PROP_SUBSCRIPTIONINTERVAL
var libraryManager
const SB_NS
const UPDATE_INTERVAL
return null
Definition: FeedWriter.js:1143
const SB_PROP_SUBSCRIPTIONNEXTRUN
const SB_PROP_SUBSCRIPTIONURL
function url(spec)
var uri
Definition: FeedWriter.js:1135
function sbDynamicPlaylistService()
sbDeviceFirmwareAutoCheckForUpdate prototype classID
Javascript wrappers for common library tasks.
function now()
_getSelectedPageStyle s i
Array filter(tab.attributes, function(aAttr){return(_this.xulAttributes.indexOf(aAttr.name) >-1);}).forEach(tab.removeAttribute
const Cr
let observer
const SB_LIBRARY_MANAGER_BEFORE_SHUTDOWN_TOPIC
_updateTextAndScrollDataForFrame aData