sbCDRipServicePaneService.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 if (typeof(Cc) == "undefined")
26  var Cc = Components.classes;
27 if (typeof(Ci) == "undefined")
28  var Ci = Components.interfaces;
29 if (typeof(Cr) == "undefined")
30  var Cr = Components.results;
31 if (typeof(Cu) == "undefined")
32  var Cu = Components.utils;
33 
34 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
35 Cu.import("resource://app/jsmodules/ArrayConverter.jsm");
36 Cu.import("resource://app/jsmodules/DOMUtils.jsm");
37 Cu.import("resource://app/jsmodules/PlatformUtils.jsm");
38 Cu.import("resource://app/jsmodules/sbProperties.jsm");
39 
40 const CDRIPNS = 'http://songbirdnest.com/rdf/servicepane/cdrip#';
41 const SPNS = 'http://songbirdnest.com/rdf/servicepane#';
42 
43 // For some device events we need to check if they are from the CD Device
44 // marshall and not others.
45 const CDDEVICEMARSHALLNAME = "sbCDDeviceMarshall";
46 
48  className: "Songbird CD Rip Device Support Module",
49  cid: Components.ID("{9925b565-5c19-4feb-87a8-413d86570cd9}"),
50  contractID: "@songbirdnest.com/servicepane/cdDevice;1",
51 
52  ifList: [ Ci.sbIServicePaneModule,
53  Ci.nsIObserver ],
54 
56  [
57  {
58  category: "service-pane",
59  entry: "cdrip-device"
60  }
61  ],
62 
63  devCatName: "CD Rip Device",
64 
65  appQuitTopic: "quit-application",
66 
67  devMgrURL: "chrome://songbird/content/mediapages/cdripMediaView.xul"
68 };
69 if ("sbIWindowsAutoPlayActionHandler" in Ci) {
70  sbCDRipServicePaneServiceConfig.ifList.push(Ci.sbIWindowsAutoPlayActionHandler);
71 }
72 
74 
75 }
76 
78 
80  classDescription: sbCDRipServicePaneServiceConfig.className,
81  classID: sbCDRipServicePaneServiceConfig.cid,
82  contractID: sbCDRipServicePaneServiceConfig.contractID,
83 
85  _xpcom_categories: sbCDRipServicePaneServiceConfig.categoryList,
86 
87  // Services to use.
88  _deviceManagerSvc: null,
89  _deviceServicePaneSvc: null,
90  _observerSvc: null,
91  _servicePaneSvc: null,
92 
93  _deviceInfoList: [],
94 
95  _deviceScanInProgress: false,
96 
97  // ************************************
98  // sbIServicePaneService implementation
99  // ************************************
100  servicePaneInit: function sbCDRipServicePaneService_servicePaneInit(aServicePaneService) {
101  this._servicePaneSvc = aServicePaneService;
102  this._initialize();
103  },
104 
105  fillContextMenu: function sbCDRipServicePaneService_fillContextMenu(aNode,
106  aContextMenu,
107  aParentWindow) {
108  // Get the node device ID. Do nothing if not a device node.
109  var deviceID = aNode.getAttributeNS(CDRIPNS, "DeviceId");
110  if (!deviceID)
111  return;
112 
113  // Get the device node type.
114  var deviceNodeType = aNode.getAttributeNS(CDRIPNS, "deviceNodeType");
115 
116  // Import device context menu items into the context menu.
117  if (deviceNodeType == "cd-device") {
118  DOMUtils.importChildElements(aContextMenu,
119  this._deviceContextMenuDoc,
120  "cddevice_context_menu_items",
121  { "device-id": deviceID,
122  "service_pane_node_id": aNode.id });
123  }
124  },
125 
126  fillNewItemMenu: function sbCDRipServicePaneService_fillNewItemMenu(aNode,
127  aContextMenu,
128  aParentWindow) {
129  },
130 
131  onSelectionChanged: function sbCDRipServicePaneService_onSelectionChanged(aNode,
132  aContainer,
133  aParentWindow) {
134  },
135 
136  canDrop: function sbCDRipServicePaneService_canDrop(aNode,
137  aDragSession,
138  aOrientation,
139  aWindow) {
140  // Currently no drag and drop allowed
141  return false;
142  },
143 
144  onDrop: function sbCDRipServicePaneService_onDrop(aNode,
145  aDragSession,
146  aOrientation,
147  aWindow) {
148  // Currently no drag and drop allowed
149  },
150 
151  onDragGesture: function sbCDRipServicePaneService_onDragGesture(aNode,
152  aDataTransfer) {
153  // Currently no drag and drop allowed
154  },
155 
156  onBeforeRename: function sbCDRipServicePaneService_onBeforeRename(aNode) {
157  // Rename is not allowed for CD Devices
158  },
159 
160  onRename: function sbCDRipServicePaneService_onRename(aNode,
161  aNewName) {
162  // Rename is not allowed for CD Devices
163  },
164 
165  shutdown: function sbCDRipServicePaneService_shutdown() {
166  // Do nothing, since we shut down on quit-application
167  },
168 
169  // ************************************
170  // nsIObserver implementation
171  // ************************************
172  observe: function sbCDRipServicePaneService_observe(aSubject,
173  aTopic,
174  aData) {
175  switch (aTopic) {
176  case this._cfg.appQuitTopic :
177  this._shutdown();
178  break;
179  }
180 
181  },
182 
183  // ************************************
184  // nsISupports implementation
185  // ************************************
186  QueryInterface: XPCOMUtils.generateQI(sbCDRipServicePaneServiceConfig.ifList),
187 
188  // ************************************
189  // Internal methods
190  // ************************************
191 
195  _initialize: function sbCDRipServicePaneService_initialize() {
196  this._observerSvc = Cc["@mozilla.org/observer-service;1"]
197  .getService(Ci.nsIObserverService);
198 
199  this._observerSvc.addObserver(this, this._cfg.appQuitTopic, false);
200 
201  this._deviceServicePaneSvc = Cc["@songbirdnest.com/servicepane/device;1"]
202  .getService(Ci.sbIDeviceServicePaneService);
203 
204  this._deviceManagerSvc = Cc["@songbirdnest.com/Songbird/DeviceManager;2"]
205  .getService(Ci.sbIDeviceManager2);
206 
207  // Add a listener for CDDevice Events
208  var deviceEventListener = {
209  cdDeviceServicePaneSvc: this,
210 
211  onDeviceEvent: function deviceEventListener_onDeviceEvent(aDeviceEvent) {
212  this.cdDeviceServicePaneSvc._processDeviceManagerEvent(aDeviceEvent);
213  }
214  };
215 
216  this._deviceEventListener = deviceEventListener;
217  this._deviceManagerSvc.addEventListener(deviceEventListener);
218 
219  // load the cd-device context menu document
220  this._deviceContextMenuDoc =
221  DOMUtils.loadDocument
222  ("chrome://songbird/content/xul/device/deviceContextMenu.xul");
223 
224  if (PlatformUtils.platformString == "Windows_NT") {
225  // Register autoplay handler
226  var autoPlayActionHandler = {
227  cdDeviceServicePaneSvc: this,
228 
229  handleAction: function autoPlayActionHandler_handleAction(aAction, aActionArg) {
230  return this.cdDeviceServicePaneSvc._processAutoPlayAction(aAction);
231  },
232 
233  QueryInterface: XPCOMUtils.generateQI([Ci.sbIWindowsAutoPlayActionHandler])
234  }
235 
236  this._autoPlayActionHandler = autoPlayActionHandler;
237  var windowsAutoPlayService =
238  Cc["@songbirdnest.com/Songbird/WindowsAutoPlayService;1"]
239  .getService(Ci.sbIWindowsAutoPlayService);
240  windowsAutoPlayService.addActionHandler
241  (autoPlayActionHandler,
242  Ci.sbIWindowsAutoPlayService.ACTION_CD_RIP);
243  }
244  },
245 
249  _shutdown: function sbCDRipServicePaneService_shutdown() {
250  this._observerSvc.removeObserver(this, this._cfg.appQuitTopic);
251 
252  this._deviceManagerSvc.removeEventListener(this._deviceEventListener);
253  this._deviceEventListener = null;
254 
255  if (PlatformUtils.platformString == "Windows_NT") {
256  // Unregister autoplay handler
257  var windowsAutoPlayService =
258  Cc["@songbirdnest.com/Songbird/WindowsAutoPlayService;1"]
259  .getService(Ci.sbIWindowsAutoPlayService);
260  windowsAutoPlayService.removeActionHandler
261  (this._autoPlayActionHandler,
262  Ci.sbIWindowsAutoPlayService.ACTION_CD_RIP);
263  this._autoPlayActionHandler = null;
264  }
265 
266  // Remove all references to nodes
267  this._deviceInfoList = [];
268 
269  this._deviceManagerSvc = null;
270  this._deviceServicePaneSvc = null;
271  this._servicePaneSvc = null;
272  this._observerSvc = null;
273  },
274 
279  _processDeviceManagerEvent:
280  function sbCDRipServicePaneService_processDeviceManagerEvent(aDeviceEvent) {
281 
282  switch(aDeviceEvent.type) {
283  case Ci.sbIDeviceEvent.EVENT_DEVICE_SCAN_START:
284  var marshall = aDeviceEvent.origin.QueryInterface(Ci.sbIDeviceMarshall);
285  if (marshall.name == CDDEVICEMARSHALLNAME)
286  this._deviceScanInProgress = true;
287  break;
288 
289  case Ci.sbIDeviceEvent.EVENT_DEVICE_SCAN_END:
290  var marshall = aDeviceEvent.origin.QueryInterface(Ci.sbIDeviceMarshall);
291  if (marshall.name == CDDEVICEMARSHALLNAME)
292  this._deviceScanInProgress = false;
293  break;
294 
295  case Ci.sbIDeviceEvent.EVENT_DEVICE_ADDED:
296  var result = this._addDeviceFromEvent(aDeviceEvent);
297 
298  // if we successfully added the device, switch the media tab
299  // to the CD rip view as long as this is not during the initial scan
300  // of CD Devices.
301  if (result)
302  this._loadCDViewFromEvent(aDeviceEvent);
303  break;
304 
305  case Ci.sbIDeviceEvent.EVENT_DEVICE_REMOVED:
306  this._removeDeviceFromEvent(aDeviceEvent);
307  break;
308 
309  case Ci.sbICDDeviceEvent.EVENT_CDLOOKUP_INITIATED:
310  this._updateState(aDeviceEvent, true);
311  break;
312 
313  case Ci.sbICDDeviceEvent.EVENT_CDLOOKUP_COMPLETED:
314  this._updateState(aDeviceEvent, false);
315  break;
316 
317  case Ci.sbICDDeviceEvent.EVENT_CDLOOKUP_METADATA_COMPLETE:
318  this._updateState(aDeviceEvent, false);
319  break;
320 
321  case Ci.sbIDeviceEvent.EVENT_DEVICE_STATE_CHANGED:
322  this._updateState(aDeviceEvent, false);
323  break;
324 
325  default:
326  break;
327  }
328  },
329 
334  _processAutoPlayAction:
335  function sbCDRipServicePaneService_processAutoPlayAction(aAction) {
336 
337  switch (aAction) {
338  case Ci.sbIWindowsAutoPlayService.ACTION_CD_RIP:
339  // No way to tell which CD the user meant to rip, let's take one randomly
340  // (hopefully the last one enumerated is also the last one added)
341  var deviceNode = null;
342  for each (let deviceInfo in this._deviceInfoList) {
343  deviceNode = deviceInfo.svcPaneNode;
344  }
345 
346  if (deviceNode) {
347  Cc['@mozilla.org/appshell/window-mediator;1']
348  .getService(Ci.nsIWindowMediator)
349  .getMostRecentWindow('Songbird:Main').gBrowser
350  .loadURI(deviceNode.url, null, null, null, "_media");
351  }
352  else {
353  // CD is probably not recognized yet, don't do anything - the view
354  // will switch to it anyway once it is.
355  }
356 
357  return true;
358 
359  default:
360  return false;
361  }
362  },
363 
368  _loadCDViewFromEvent:
369  function sbCDRipServicePaneService_loadCDViewFromEvent(aDeviceEvent) {
370 
371  // We only want to do this if it is not during a device scan (usually at
372  // startup).
373  if (this._deviceScanInProgress)
374  return;
375 
376  var device = aDeviceEvent.data.QueryInterface(Ci.sbIDevice);
377  var url = this._cfg.devMgrURL + "?device-id=" + device.id;
378  Cc['@mozilla.org/appshell/window-mediator;1']
379  .getService(Ci.nsIWindowMediator)
380  .getMostRecentWindow('Songbird:Main').gBrowser
381  .loadURI(url, null, null, null, "_media");
382  },
383 
388  _updateState: function sbCDRipServicePaneService__updateState(aDeviceEvent,
389  aForceBusy) {
390  // Get the device and its node.
391  var device = aDeviceEvent.origin.QueryInterface(Ci.sbIDevice);
392  var deviceId = device.id;
393  var deviceType = device.parameters.getProperty("DeviceType");
394 
395  // We only care about CD devices
396  if (deviceType != "CD")
397  return;
398 
399  if (device.state == Ci.sbIDevice.STATE_TRANSCODE) {
400  this._toggleReadOnly(true, device);
401  }
402  else if (device.state == Ci.sbIDevice.STATE_IDLE) {
403  this._toggleReadOnly(false, device);
404  }
405 
406  if (typeof(this._deviceInfoList[deviceId]) != 'undefined') {
407  var devNode = this._deviceInfoList[deviceId].svcPaneNode;
408 
409  // The friendly name might have changed, keep it in sync.
410  if (devNode.name != device.properties.friendlyName) {
411  devNode.name = device.properties.friendlyName;
412  }
413 
414  // Get the device properties and clear the busy property.
415  devProperties = devNode.className.split(" ");
416  devProperties = devProperties.filter(function(aProperty) {
417  return aProperty != "busy";
418  });
419 
420  // Set the busy property if the device is busy.
421  if ((device.state != Ci.sbIDevice.STATE_CANCEL) &&
422  (aForceBusy || device.state != Ci.sbIDevice.STATE_IDLE)) {
423  // Clear success state from previous rip
424  devProperties = devProperties.filter(function(aProperty) {
425  return aProperty != "successful" &&
426  aProperty != "unsuccessful";
427  });
428  devProperties.push("busy");
429  } else {
430  if (devNode.hasAttributeNS(CDRIPNS, "LastState")) {
431  var lastState = devNode.getAttributeNS(CDRIPNS, "LastState");
432  if (lastState == Ci.sbIDevice.STATE_TRANSCODE) {
433  if (this._checkErrors(device)) {
434  devProperties.push("unsuccessful");
435  } else if (this._checkSuccess(device)){
436  devProperties.push("successful");
437  }
438  //if neither _checkErrors nor _checkSuccess is true, then the user
439  //cancelled before any successes or fails. Go back to idle image.
440  }
441  }
442  }
443  devNode.setAttributeNS(CDRIPNS, "LastState", device.state);
444 
445  // Write back the device node properties.
446  devNode.className = devProperties.join(" ");
447  }
448  },
449 
454  _getDeviceLibrary: function sbCDRipServicePaneService__getDeviceLibrary(aDevice) {
455  // Get the libraries for device
456  var libraries = aDevice.content.libraries;
457  if (libraries.length < 1) {
458  // Oh no, we have no libraries
459  Cu.reportError("Device " + aDevice.id + " has no libraries!");
460  return null;
461  }
462 
463  // Get the requested library
464  var deviceLibrary = libraries.queryElementAt(0, Ci.sbIMediaList);
465  if (!deviceLibrary) {
466  Cu.reportError("Unable to get library for device: " + aDevice.id);
467  return null;
468  }
469 
470  return deviceLibrary;
471  },
472 
479  _toggleReadOnly: function sbCDRipServicePaneService__toggleReadOnly(aReadOnly,
480  aDevice) {
481  var deviceLibrary = this._getDeviceLibrary(aDevice);
482  if (deviceLibrary)
483  deviceLibrary.setProperty(SBProperties.isReadOnly,
484  (aReadOnly ? "1" : "0"));
485  },
486 
492  _checkErrors: function sbCDRipServicePaneService__checkErrors(aDevice) {
493  // Check for any tracks that have a failed status
494  var deviceLibrary = this._getDeviceLibrary(aDevice);
495  var errorCount = 0;
496 
497  try {
498  // Get all the did not successfully ripped tracks
499  var propArray =
500  Cc["@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1"]
501  .createInstance(Ci.sbIMutablePropertyArray);
502  propArray.appendProperty(SBProperties.cdRipStatus, "3|100");
503  propArray.appendProperty(SBProperties.shouldRip, "1");
504 
505  var rippedItems = deviceLibrary.getItemsByProperties(propArray);
506  errorCount = rippedItems.length;
507  }
508  catch (err if err.result == Cr.NS_ERROR_NOT_AVAILABLE) {
509  // deviceLibrary.getItemsByProperties() will throw NS_ERROR_NOT_AVAILABLE
510  // if there are no failed rips in the list, thus no errors.
511  return false;
512  }
513  catch (err) {
514  Cu.reportError("ERROR GETTING TRANSCODE ERROR COUNT " + err);
515  }
516 
517  return (errorCount > 0);
518  },
519 
525  _checkSuccess: function sbCDRipServicePaneService__checkErrors(aDevice) {
526  // Check if any tracks were successfully ripped
527  var deviceLibrary = this._getDeviceLibrary(aDevice);
528  var successCount = 0;
529 
530  try {
531  var propArray = Cc["@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1"]
532  .createInstance(Ci.sbIMutablePropertyArray);
533  propArray.appendProperty(SBProperties.cdRipStatus, "2|100");
534  propArray.appendProperty(SBProperties.shouldRip, "1");
535 
536  var rippedItems = deviceLibrary.getItemsByProperties(propArray);
537  successCount = rippedItems.length;
538  }
539  catch (err if err.result == Cr.NS_ERROR_NOT_AVAILABLE) {
540  // deviceLibrary.getItemsByProperties() will throw NS_ERROR_NOT_AVAILABLE
541  // if there are no successful rips in the list.
542  return false;
543  }
544  catch (err) {
545  Cu.reportError("ERROR GETTING TRANSCODE SUCCESS COUNT " + err);
546  }
547 
548  return (successCount > 0);
549  },
550 
555  _addDeviceFromEvent:
556  function sbCDRipServicePaneService_addDeviceFromEvent(aDeviceEvent) {
557 
558  var device = aDeviceEvent.data.QueryInterface(Ci.sbIDevice);
559  var deviceType = device.parameters.getProperty("DeviceType");
560 
561  // We only care about CD devices
562  if (deviceType != "CD")
563  return false;
564 
565  try {
566  this._addDevice(device);
567  }
568  catch(e) {
569  Cu.reportError(e);
570  return false;
571  }
572  return true;
573  },
574 
579  _removeDeviceFromEvent:
580  function sbCDRipServicePaneService_removeDeviceFromEvent(aDeviceEvent) {
581 
582  var device = aDeviceEvent.data.QueryInterface(Ci.sbIDevice);
583  var deviceType = device.parameters.getProperty("DeviceType");
584 
585  // We only care about CD devices
586  if (deviceType != "CD")
587  return;
588 
589  try {
590  this._removeDevice(device);
591  }
592  catch(e) {
593  Cu.reportError(e);
594  }
595  },
596 
602  _addDevice: function sbCDRipServicePaneService_addDevice(aDevice) {
603  var device = aDevice.QueryInterface(Ci.sbIDevice);
604  var devId = device.id;
605 
606  // Do nothing if device is not an CD device.
607  var deviceType = device.parameters.getProperty("DeviceType");
608  if (deviceType != "CD") {
609  return;
610  }
611 
612  // Add a cd rip node in the service pane.
613  var devNode = this._deviceServicePaneSvc.createNodeForDevice2(device, true);
614  devNode.setAttributeNS(CDRIPNS, "DeviceId", devId);
615  devNode.setAttributeNS(CDRIPNS, "deviceNodeType", "cd-device");
616  devNode.className = "cd-device";
617  devNode.contractid = this._cfg.contractID;
618  devNode.url = this._cfg.devMgrURL + "?device-id=" + devId;
619  devNode.editable = false;
620  devNode.name = device.properties.friendlyName;
621 
622  this._deviceInfoList[devId] = {svcPaneNode: devNode};
623  },
624 
629  _removeDevice: function sbCDRipServicePaneService_removeDevice(aDevice) {
630  var device = aDevice.QueryInterface(Ci.sbIDevice);
631  var devId = device.id;
632 
633  var devInfo = this._deviceInfoList[devId];
634  if (!devInfo) {
635  return;
636  }
637 
638  // Remove the device node.
639  devInfo.svcPaneNode.parentNode.removeChild(devInfo.svcPaneNode);
640 
641  // Remove device info list entry.
642  delete this._deviceInfoList[devId];
643  }
644 };
645 
646 // Instantiate an XPCOM module.
647 function NSGetModule(compMgr, fileSpec) {
648  return XPCOMUtils.generateModule([sbCDRipServicePaneService]);
649 }
const Cu
classDescription entry
Definition: FeedWriter.js:1427
const Cc
sbLibraryImporterManagerCfg categoryList
sbOSDControlService prototype className
sbDeviceFirmwareAutoCheckForUpdate prototype contractID
function sbCDRipServicePaneService()
sbOSDControlService prototype QueryInterface
sbDeviceFirmwareAutoCheckForUpdate prototype classDescription
var sbCDRipServicePaneServiceConfig
sbDownloadDeviceServicePaneModule prototype shutdown
sbDeviceServicePane prototype servicePaneInit
return null
Definition: FeedWriter.js:1143
function url(spec)
const Cr
const Ci
sbDeviceFirmwareAutoCheckForUpdate prototype classID
sbWindowsAutoPlayServiceCfg _xpcom_categories
_updateTextAndScrollDataForFrame aData
sbDeviceFirmwareAutoCheckForUpdate prototype observe
function NSGetModule(compMgr, fileSpec)