cdripMediaView.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 const SB_NS = 'http://songbirdnest.com/data/1.0#';
26 
27 Components.utils.import("resource://app/jsmodules/StringUtils.jsm");
28 Components.utils.import("resource://app/jsmodules/sbLibraryUtils.jsm");
29 Components.utils.import("resource://app/jsmodules/kPlaylistCommands.jsm");
30 Components.utils.import("resource://app/jsmodules/ArrayConverter.jsm");
31 Components.utils.import("resource://app/jsmodules/sbCDDeviceUtils.jsm");
32 Components.utils.import("resource://app/jsmodules/sbProperties.jsm");
33 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
34 
35 if (typeof(Cc) == "undefined")
36  var Cc = Components.classes;
37 if (typeof(Ci) == "undefined")
38  var Ci = Components.interfaces;
39 if (typeof(Cu) == "undefined")
40  var Cu = Components.utils;
41 if (typeof(Cr) == "undefined")
42  var Cr = Components.results;
43 
44 //------------------------------------------------------------------------------
45 // XUL ID Constants
46 
47 var CDRIP_BASE = "sb-cdrip-";
48 const CDRIP_HBOX_CLASS = CDRIP_BASE + "header-hbox";
49 const CDRIP_HBOX_PLAIN_CLASS = CDRIP_BASE + "header-plain-hbox";
50 
51 const RIP_SETTINGS_HBOX = CDRIP_BASE + "settings-hbox";
52 const RIP_SETTINGS_FORMAT_LABEL = CDRIP_BASE + "format-label";
53 const RIP_SETTINGS_BITRATE_HBOX = CDRIP_BASE + "bitrate-hbox";
54 const RIP_SETTINGS_QUALITY_LABEL = CDRIP_BASE + "quality-label";
55 const RIP_SETTINGS_BUTTON = CDRIP_BASE + "settings-button";
56 
57 const RIP_SPACER = CDRIP_BASE + "spacer";
58 
59 const RIP_STATUS_HBOX = CDRIP_BASE + "actionstatus-hbox";
60 
61 const RIP_STATUS_IMAGE_PRE_SPACER = CDRIP_BASE + "statusimage-pre-spacer";
62 const RIP_STATUS_IMAGE_HBOX = CDRIP_BASE + "actionstatus-image-box";
63 const RIP_STATUS_IMAGE = CDRIP_BASE + "status-image";
64 const RIP_STATUS_IMAGE_POST_SPACER = CDRIP_BASE + "statusimage-post-spacer";
65 
66 const RIP_STATUS_LABEL_HBOX = CDRIP_BASE + "actionstatus-label-box";
67 const RIP_STATUS_LABEL = CDRIP_BASE + "status-label";
68 
69 const RIP_STATUS_LOGO_PRE_SPACER = CDRIP_BASE + "logo-pre-spacer";
70 const RIP_STATUS_LOGO_HBOX = CDRIP_BASE + "actionstatus-logo-box";
71 const RIP_STATUS_LOGO = CDRIP_BASE + "provider-logo";
72 const RIP_STATUS_LOGO_POST_SPACER = CDRIP_BASE + "logo-post-spacer";
73 
74 const RIP_STATUS_BUTTONS_PRE_SPACER = CDRIP_BASE + "buttons-pre-spacer";
75 const RIP_STATUS_BUTTON_HBOX = CDRIP_BASE + "status-buttons";
76 const RIP_STATUS_RIP_CD_BUTTON = CDRIP_BASE + "rip-cd-button";
77 const RIP_STATUS_STOP_RIP_BUTTON = CDRIP_BASE + "stop-rip-button";
78 const RIP_STATUS_EJECT_CD_BUTTON = CDRIP_BASE + "eject-cd-button";
79 
80 const RIP_COMMAND_EJECT = CDRIP_BASE + "eject-command";
81 const RIP_COMMAND_STARTRIP = CDRIP_BASE + "startrip-command";
82 const RIP_COMMAND_STOPRIP = CDRIP_BASE + "stoprip-command";
83 
84 const CDRIP_PLAYLIST = CDRIP_BASE + "playlist";
85 
86 // Time to display lookup provider logo
88 // Delay before displaying lookup message
90 
91 //------------------------------------------------------------------------------
92 // Misc internal constants
93 
98 
99 
100 //------------------------------------------------------------------------------
101 // CD rip view controller
102 
103 window.cdripController =
104 {
105  // The sbIMediaListView that this page is to display
106  _mediaListView: null,
107  // The playlist we bind our CD Tracks to.
108  _playlist: null,
109  // The device we are working with
110  _device: null,
111  _deviceID: null,
112  _deviceLibrary: null,
113 
114  // Closure for device manager events
115  _onDeviceManagerEventClosure: null,
116 
117  // Transcoding pref branch
118  _transcodePrefBranch: null,
119 
120  _supportedProfiles: null,
121 
122  _logoTimer: null,
123  logoTimerEnabled: false,
124  _lookupDelayTimer: null,
125 
126  _libraryListener: null,
127 
128  onLoad: function cdripController_onLoad() {
129  // Add our device listener to listen for lookup notification events
130  this._device = this._getDevice();
131 
132  // This will set this._deviceLibrary
133  this._getDeviceLibrary();
134 
135  // Go back to previous page and return if device or device library are not
136  // available.
137  if (!this._device || !this._deviceLibrary) {
138  var browser = SBGetBrowser();
139  browser.getTabForDocument(document).backWithDefault();
140  return;
141  }
142 
143  // Update the header information
144  this._updateHeaderView();
145  this._toggleRipStatus(false);
146 
147  this._logoTimer = Cc["@mozilla.org/timer;1"]
148  .createInstance(Ci.nsITimer);
149  this._lookupDelayTimer = Cc["@mozilla.org/timer;1"]
150  .createInstance(Ci.nsITimer);
151 
152  this._toggleLookupNotification(false);
153 
154  var eventTarget = this._device.QueryInterface(Ci.sbIDeviceEventTarget);
155  eventTarget.addEventListener(this);
156 
157  var self = this;
158  this._onDeviceManagerEventClosure =
159  function cdripController__onDeviceManagerEventClosure(aEvent)
160  { self._onDeviceManagerEvent(aEvent); };
161  var deviceManagerSvc = Cc["@songbirdnest.com/Songbird/DeviceManager;2"]
162  .getService(Ci.sbIDeviceManager2);
163  deviceManagerSvc.addEventListener(this._onDeviceManagerEventClosure);
164 
165  // Disable player controls & load the playlist
166  this._updateRipSettings();
167  this._togglePlayerControls(true);
168  this._loadPlaylist();
169 
170  var servicePaneNode =
171  Cc["@songbirdnest.com/servicepane/device;1"]
172  .getService(Ci.sbIDeviceServicePaneService)
173  .getNodeForDevice(this._device);
174  if (servicePaneNode) {
175  document.title = servicePaneNode.displayName;
176  }
177  else {
178  // failed to find the node, stick with the default node name
179  document.title = SBString("cdrip.service.default.node_name");
180  }
181 
182  // Listen to transcoding pref changes.
183  this._transcodePrefBranch = Cc["@mozilla.org/preferences-service;1"]
184  .getService(Ci.nsIPrefService)
185  .getBranch("songbird.cdrip.transcode_profile.")
186  .QueryInterface(Ci.nsIPrefBranch2);
187  this._transcodePrefBranch.addObserver("", this, false);
188 
189  // Now that we are all initialized we can act like a regular media page.
190  window.mediaPage = new MediaPageImpl();
191 
192  // Now enable the buttons, they're disabled to prevent clicking on them
193  // before all the device information has been wired up.
194  this._enableCommand(RIP_COMMAND_EJECT);
195  this._enableCommand(RIP_COMMAND_STARTRIP);
196  this._enableCommand(RIP_COMMAND_STOPRIP);
197 
198  // Check if we need to preform a look up on this disc.
199  this._checkIfNeedsLookup();
200  },
201 
202  onUnload: function cdripController_onUnload() {
203  // Cleanup the medialist listener
204  this._mediaListView.mediaList.removeListener(this._libraryListener);
205  this._libraryListener = null;
206 
207  if (this._onDeviceManagerEventClosure) {
208  var deviceManagerSvc = Cc["@songbirdnest.com/Songbird/DeviceManager;2"]
209  .getService(Ci.sbIDeviceManager2);
210  deviceManagerSvc.removeEventListener(this._onDeviceManagerEventClosure);
211  }
212 
213  this._togglePlayerControls(false);
214  if (this._device) {
215  var eventTarget = this._device.QueryInterface(Ci.sbIDeviceEventTarget);
216  eventTarget.removeEventListener(this);
217  this._device.removeEventListener(this);
218  }
219 
220  this._transcodePrefBranch.removeObserver("", this, false);
221  this._transcodePrefBranch = null;
222  },
223 
224  onDeviceEvent: function cdripController_onDeviceEvent(aEvent) {
225  switch (aEvent.type) {
226  case Ci.sbIDeviceEvent.EVENT_DEVICE_READY:
227  this._checkIfNeedsLookup();
228  break;
229 
230  // START CD LOOKUP
231  case Ci.sbICDDeviceEvent.EVENT_CDLOOKUP_INITIATED:
232  this._toggleLookupNotification(true);
233  break;
234 
235  // CD LOOKUP COMPLETE
236  case Ci.sbICDDeviceEvent.EVENT_CDLOOKUP_COMPLETED:
237  this._toggleLookupNotification(false);
238  break;
239 
240  case Ci.sbIDeviceEvent.EVENT_DEVICE_STATE_CHANGED:
241  var device = aEvent.origin.QueryInterface(Ci.sbIDevice);
242  if (device.id.toString() == this._deviceID) {
243  this._updateHeaderView();
244  }
245  break;
246 
247  case Ci.sbICDDeviceEvent.EVENT_CDRIP_COMPLETED:
248  this._toggleRipStatus(true);
249  break;
250 
251  default:
252  break;
253  }
254  },
255 
256  observe: function cdripController_observe(subject, topic, data) {
257  if (topic == 'timer-callback') {
258  if (subject == this._logoTimer) {
259  this.logoTimerEnabled = false;
260  // Hide any lookup objects if lookup is complete
261  if (this._device.state != Ci.sbICDDeviceEvent.STATE_LOOKINGUPCD)
262  this._toggleLookupNotification(false);
263  } else if (subject == this._lookupDelayTimer) {
264  // Enable lookup image/label if still looking up
265  if (this._device.state == Ci.sbICDDeviceEvent.STATE_LOOKINGUPCD) {
266  this._toggleLookupLabel(true);
267  }
268  }
269  } else {
270  // Something changed in the rip transcoding prefs, update that UI now.
271  this._updateRipSettings();
272  }
273  },
274 
275  showCDRipSettings: function cdripController_showCDRipSettings() {
276  Cc["@mozilla.org/appshell/window-mediator;1"]
277  .getService(Ci.nsIWindowMediator)
278  .getMostRecentWindow("Songbird:Main")
279  .SBOpenPreferences("paneCDRip");
280  },
281 
282  _onDeviceManagerEvent:
283  function cdripController__onDeviceManagerEvent(aEvent) {
284  switch (aEvent.type) {
285  case Ci.sbIDeviceEvent.EVENT_DEVICE_REMOVED :
286  // Go back to previous page if device removed.
287  var device = aEvent.data.QueryInterface(Ci.sbIDevice);
288  if (device.id.toString() == this._deviceID) {
289  var browser = SBGetBrowser();
290  browser.getTabForDocument(document).backWithDefault();
291  }
292  break;
293 
294  default:
295  break;
296  }
297  },
298 
299  _toggleLookupNotification: function
300  cdripController_toggleLookupNotification(show) {
301  if (show) {
302  var manager = Cc["@songbirdnest.com/Songbird/MetadataLookup/manager;1"]
303  .getService(Ci.sbIMetadataLookupManager);
304  try {
305  var provider = manager.defaultProvider;
306  var providerLogo = provider.logoURL;
307  var providerName = provider.name;
308  var providerURL = provider.infoURL;
309 
310  if (providerLogo) {
311  this._showLookupVendorInfo(providerLogo, providerName, providerURL);
312 
313  // Arm the logo timer.
314  this._logoTimer.init(this,
316  Ci.nsITimer.TYPE_ONE_SHOT);
317  this.logoTimerEnabled = true;
318  }
319  }
320  catch (e) {
321  Cu.reportError(e);
322  }
323 
324  // Arm the lookup delay timer now.
325  this._lookupDelayTimer.init(this,
327  Ci.nsITimer.TYPE_ONE_SHOT);
328 
329  // Disable visible buttons
330  this._disableCommand(RIP_COMMAND_EJECT);
331  this._disableCommand(RIP_COMMAND_STARTRIP);
332  this._disableCommand(RIP_COMMAND_STOPRIP);
333  }
334  else {
335  // If the logo timer isn't enabled, hide all of the lookup vendor
336  // logo stuff.
337  if (!this.logoTimerEnabled) {
338  this._hideLookupVendorInfo();
339  }
340 
341  this._toggleLookupLabel(false);
342 
343  // Reset the action buttons.
344  this._showElement(RIP_STATUS_BUTTON_HBOX);
345  this._hideElement(RIP_STATUS_BUTTONS_PRE_SPACER);
346  }
347 
348  this._updateHeaderView();
349  },
350 
351  _toggleLookupLabel: function cdripController_lookupLabel(show) {
352  if (show) {
353  this._showElement(RIP_STATUS_IMAGE_PRE_SPACER);
354  this._showElement(RIP_STATUS_IMAGE_POST_SPACER);
355  this._showElement(RIP_STATUS_IMAGE_HBOX);
356 
357  this._setImageSrc(RIP_STATUS_IMAGE,
358  "chrome://songbird/skin/base-elements/icon-loading-large.png");
359  this._setLabelValue(RIP_STATUS_LABEL,
360  SBString("cdrip.mediaview.status.lookup"));
361 
362  this._showElement(RIP_STATUS_LABEL_HBOX);
363  this._showElement(RIP_STATUS_BUTTONS_PRE_SPACER);
364  }
365  else {
366  this._hideElement(RIP_STATUS_IMAGE_PRE_SPACER);
367  this._hideElement(RIP_STATUS_IMAGE_POST_SPACER);
368  this._hideElement(RIP_STATUS_IMAGE_HBOX);
369  this._hideElement(RIP_STATUS_LABEL_HBOX);
370 
371  this._setImageSrc(RIP_STATUS_IMAGE, "");
372  this._setLabelValue(RIP_STATUS_LABEL, "");
373 
374  this._hideElement(RIP_STATUS_BUTTONS_PRE_SPACER);
375  }
376 
377  this._updateHeaderView();
378  },
379 
380  _toggleRipStatus: function cdripController_toggleRipStatus(aShouldShow) {
381  if (aShouldShow) {
382  // Unhide the status elements.
383  this._showElement(RIP_STATUS_IMAGE_PRE_SPACER);
384  this._showElement(RIP_STATUS_IMAGE_POST_SPACER);
385  this._showElement(RIP_STATUS_LABEL_HBOX);
386  this._showElement(RIP_STATUS_IMAGE_HBOX);
387  this._showElement(RIP_STATUS_BUTTONS_PRE_SPACER);
388 
389  // Update the status label and image based on the final
390  // transcode job status.
391  var status = this._getLastTranscodeStatus();
392  var statusLabel = "";
393  var statusImageSrc = "";
394  switch (status) {
396  statusLabel = "cdrip.mediaview.status.rip_success";
397  statusImageSrc = "chrome://songbird/skin/device/icon-complete.png";
398  break;
399 
401  statusLabel = "cdrip.mediaview.status.rip_failed";
402  statusImageSrc = "chrome://songbird/skin/device/icon-warning.png";
403  break;
404 
406  statusLabel = "cdrip.mediaview.status.rip_partial";
407  statusImageSrc = "chrome://songbird/skin/device/icon-warning.png";
408  break;
409 
411  statusLabel.Truncate();
412  statusImageSrc.Truncate();
413  break;
414 
415  default:
416  Cu.reportError("Unhandled transcode state!");
417  }
418 
419  this._setLabelValue(RIP_STATUS_LABEL, SBString(statusLabel));
420  this._setImageSrc(RIP_STATUS_IMAGE, statusImageSrc);
421  }
422  else {
423  this._hideElement(RIP_STATUS_LABEL_HBOX);
424  this._hideElement(RIP_STATUS_IMAGE_HBOX);
425  this._hideElement(RIP_STATUS_BUTTONS_PRE_SPACER);
426  this._hideElement(RIP_STATUS_IMAGE_PRE_SPACER);
427  this._hideElement(RIP_STATUS_IMAGE_POST_SPACER);
428  }
429  },
430 
431  _updateHeaderView: function cdripController_updateHeaderView() {
432  switch (this._device.state) {
433  // STATE_LOOKINGUPCD
434  case Ci.sbICDDeviceEvent.STATE_LOOKINGUPCD:
435  // Ignore this state since it is handled by the events in the
436  // onDeviceEvent function.
437  break;
438 
439  case Ci.sbIDevice.STATE_TRANSCODE:
440  // Currently ripping
441  this._showSettingsView();
442  this._toggleRipStatus(false);
443 
444  // Display only the Stop Rip button.
445  this._hideElement(RIP_STATUS_RIP_CD_BUTTON);
446  this._showElement(RIP_STATUS_STOP_RIP_BUTTON);
447  this._hideElement(RIP_STATUS_EJECT_CD_BUTTON);
448 
449  // Update commands:
450  this._disableCommand(RIP_COMMAND_EJECT);
451  this._disableCommand(RIP_COMMAND_STARTRIP);
452  this._enableCommand(RIP_COMMAND_STOPRIP);
453  break;
454 
455  default:
456  // By default we assume IDLE
457  this._showSettingsView();
458 
459  // Display only the Rip button and the eject button.
460  this._showElement(RIP_STATUS_RIP_CD_BUTTON);
461  this._hideElement(RIP_STATUS_STOP_RIP_BUTTON);
462  this._showElement(RIP_STATUS_EJECT_CD_BUTTON);
463 
464  // Update commands:
465  this._enableCommand(RIP_COMMAND_EJECT);
466  this._enableCommand(RIP_COMMAND_STARTRIP);
467  this._disableCommand(RIP_COMMAND_STOPRIP);
468  break;
469  }
470  },
471 
472  _updateRipSettings: function cdripController_updateRipSettings() {
473  var profileId = this._device.getPreference("transcode_profile.profile_id");
474  var profile;
475  var bitrate = 0;
476 
477  var profiles = this._findSupportedProfiles();
478 
479  if (profileId) {
480  profile = this._profileFromProfileId(profileId, profiles);
481  bitrate = parseInt(this._device.getPreference(
482  "transcode_profile.audio_properties.bitrate"));
483  }
484 
485  var highestPriority = 0;
486  if (!profile) {
487  // Nothing set in preferences, or set value is not available. Determine the
488  // default profile.
489  for (var i = 0; i < profiles.length; i++) {
490  var current = profiles[i];
491  if (!profile || current.priority > highestPriority) {
492  profile = current;
493  highestPriority = profile.priority;
494  }
495  }
496  }
497 
498  var hasBitrate = false;
499  var propertiesArray = profile.audioProperties;
500  if (propertiesArray) {
501  for (var i = 0; i < propertiesArray.length; i++) {
502  var prop = propertiesArray.queryElementAt(i,
503  Ci.sbITranscodeProfileProperty);
504  if (prop.propertyName == "bitrate") {
505  hasBitrate = true;
506  if (!bitrate) {
507  bitrate = parseInt(prop.value);
508  }
509  }
510  }
511  }
512 
513  if (hasBitrate) {
514  this._showElement(RIP_SETTINGS_BITRATE_HBOX);
515  var bitrateLabel = document.getElementById(RIP_SETTINGS_QUALITY_LABEL);
516  bitrateLabel.setAttribute("value", "" + (bitrate / 1000) + " kbps");
517  }
518  else {
519  this._hideElement(RIP_SETTINGS_BITRATE_HBOX);
520  }
521 
522  var formatLabel = document.getElementById(RIP_SETTINGS_FORMAT_LABEL);
523  formatLabel.setAttribute("value", profile.description);
524  },
525 
526  _findSupportedProfiles:
527  function cdripController_findSupportedProfiles()
528  {
529  if (this._supportedProfiles)
530  return this._supportedProfiles;
531 
532  this._supportedProfiles = [];
533 
534  var transcodeManager =
535  Cc["@songbirdnest.com/Songbird/Mediacore/TranscodeManager;1"]
536  .getService(Ci.sbITranscodeManager);
537  var profiles = transcodeManager.getTranscodeProfiles(
538  Ci.sbITranscodeProfile.TRANSCODE_TYPE_AUDIO);
539 
540  for (var i = 0; i < profiles.length; i++) {
541  var profile = profiles.queryElementAt(i, Ci.sbITranscodeProfile);
542 
543  if (profile.type == Ci.sbITranscodeProfile.TRANSCODE_TYPE_AUDIO)
544  {
545  this._supportedProfiles.push(profile);
546  }
547  }
548  return this._supportedProfiles;
549  },
550 
551  _profileFromProfileId:
552  function cdripController_profileFromProfileId(aId, aProfiles)
553  {
554  for (var i = 0; i < aProfiles.length; i++) {
555  var profile = aProfiles[i];
556  if (profile.id == aId)
557  return profile;
558  }
559  return null;
560  },
561 
562  disableTags : ['sb-player-back-button', 'sb-player-playpause-button',
563  'sb-player-forward-button', 'sb-player-volume-slider',
564  'sb-player-shuffle-button', 'sb-player-repeat-button'],
565  disableMenuControls : ['play', 'next', 'prev', 'shuf', 'repx',
566  'repa', 'rep1'],
567  _togglePlayerControls: function
568  cdripController_togglePlayerControls(disabled) {
569 
570  // disable player controls in faceplate
571  var mainWin = Cc["@mozilla.org/appshell/window-mediator;1"]
572  .getService(Ci.nsIWindowMediator)
573  .getMostRecentWindow("Songbird:Main");
574  if (!mainWin) {
575  // Shutdown sequence? Don't do anything
576  return;
577  }
578 
579  for (var i in this.disableTags) {
580  var elements = mainWin.document.getElementsByTagName(this.disableTags[i]);
581  for (var j=0; j<elements.length; j++) {
582  if (disabled) {
583  elements[j].setAttribute('disabled', 'true');
584  } else {
585  elements[j].removeAttribute('disabled');
586  }
587  }
588  }
589 
590  // disable playlist play events & menu controls
591  var pls = document.getElementById("sb-cdrip-playlist");
592  var playMenuItem = mainWin.document.getElementById("menuitem_control_play");
593  if (disabled) {
594  pls.addEventListener("Play", this._onPlay, false);
595  mainWin.addEventListener("keypress", this._onKeypress, true);
596  for each (var i in this.disableMenuControls) {
597  var menuItem = mainWin.document.getElementById("menuitem_control_" + i);
598  menuItem.setAttribute("disabled", "true");
599  }
600  } else {
601  pls.removeEventListener("Play", this._onPlay, false);
602  mainWin.removeEventListener("keypress", this._onKeypress, true);
603  playMenuItem.removeAttribute("disabled");
604  for each (var i in this.disableMenuControls) {
605  var menuItem = mainWin.document.getElementById("menuitem_control_" + i);
606  menuItem.removeAttribute("disabled");
607  }
608  }
609  },
610 
611  _onPlay: function(e) {
612  e.stopPropagation();
613  e.preventDefault();
614  },
615 
616  _onKeypress: function(e) {
617  // somebody else has focus
618  if (document.commandDispatcher.focusedWindow != window ||
619  (document.commandDispatcher.focusedElement &&
620  document.commandDispatcher.focusedElement.id != "sb-playlist-tree"))
621  {
622  return true;
623  }
624 
625  if (e.charCode != KeyboardEvent.DOM_VK_SPACE ||
626  e.ctrlKey || e.altKey || e.metaKey || e.altGraphKey)
627  {
628  return true;
629  }
630 
631  e.stopPropagation();
632  e.preventDefault();
633  return true;
634  },
635 
636  _ejectDevice: function cdripController_ejectDevice() {
637  if (this._device) {
638  this._device.eject();
639  }
640  },
641 
642  _startRip: function cdripController_startRip() {
643  if (this._device) {
644  sbCDDeviceUtils.doCDRip(this._device);
645  } else {
646  Cu.reportError("No device defined to start rip on.");
647  }
648  },
649 
650  _stopRip: function cdripController_stopRip() {
651  if (this._device) {
652  this._device.cancelRequests();
653  } else {
654  Cu.reportError("No device defined to stop rip on.");
655  }
656  },
657 
658  _hideSettingsView: function cdripController_hideSettingsView() {
659  // Hack: Prevent the header box from resizing by setting the |min-height|
660  // CSS value of the status hbox.
661  var settingsHeight =
662  document.getElementById(RIP_SETTINGS_HBOX).boxObject.height;
663 
664  this._hideElement(RIP_SPACER);
665  this._hideElement(RIP_SETTINGS_HBOX);
666 
667  var ripStatusHbox = document.getElementById(RIP_STATUS_HBOX);
668  ripStatusHbox.setAttribute("flex", "1");
669  ripStatusHbox.setAttribute("class", CDRIP_HBOX_PLAIN_CLASS);
670  ripStatusHbox.style.minHeight = settingsHeight + "px";
671 
672  this._isSettingsViewVisible = false;
673  },
674 
675  _showSettingsView: function cdripController_showSettingsView() {
676  this._showElement(RIP_SPACER);
677  this._showElement(RIP_SETTINGS_HBOX);
678 
679  var ripStatusHbox = document.getElementById(RIP_STATUS_HBOX);
680  ripStatusHbox.removeAttribute("flex");
681  ripStatusHbox.setAttribute("class", CDRIP_HBOX_CLASS);
682 
683  this._isSettingsViewVisible = true;
684  },
685 
686  _hideElement: function cdripController_hideElement(aElementId) {
687  document.getElementById(aElementId).setAttribute("hidden", "true");
688  },
689 
690  _showElement: function cdripController_showElement(aElementId) {
691  document.getElementById(aElementId).removeAttribute("hidden");
692  },
693 
694  _isElementHidden: function cdripController_isElementHidden(aElementId) {
695  return document.getElementById(aElementId).hasAttribute("hidden");
696  },
697 
698  _disableCommand: function cdripController_disableCommand(aElementId) {
699  document.getElementById(aElementId).setAttribute("disabled", "true");
700  },
701 
702  _enableCommand: function cdripController_enableCommand(aElementId) {
703  document.getElementById(aElementId).removeAttribute("disabled");
704  },
705 
706  _setLabelValue: function cdripController_setLabelValue(aElementId, aValue) {
707  document.getElementById(aElementId).value = aValue;
708  },
709 
710  _setImageSrc: function cdripController_setImageSrc(aElementId, aValue) {
711  document.getElementById(aElementId).src = aValue;
712  },
713 
714  _setToolTip: function cdripController_setToolTip(aElementId, aValue) {
715  document.getElementById(aElementId).setAttribute('tooltiptext', aValue);
716  },
717 
718  _showLookupVendorInfo: function
719  cdripController_showLookupLogo(aProviderLogoSrc,
720  aProviderName,
721  aProviderURL) {
722  if (aProviderLogoSrc) {
723  this._showElement(RIP_STATUS_LOGO_PRE_SPACER);
724  this._showElement(RIP_STATUS_LOGO_HBOX);
725  this._showElement(RIP_STATUS_LOGO_POST_SPACER);
726  this._setImageSrc(RIP_STATUS_LOGO, aProviderLogoSrc);
727 
728  // Set the tooltip on the provider logo if provided.
729  if (aProviderName) {
730  this._setToolTip(
732  SBFormattedString("cdrip.mediaview.status.lookup_provided_by",
733  [aProviderName]));
734  }
735 
736  // Set the click handler URL if the URL is also provided.
737  if (aProviderURL) {
738  this._setLogoURLListener(RIP_STATUS_LOGO, aProviderURL);
739  }
740  }
741  },
742 
743  _hideLookupVendorInfo: function cdripController_hideLookupVendorInfo() {
744  this._hideElement(RIP_STATUS_LOGO_PRE_SPACER);
745  this._hideElement(RIP_STATUS_LOGO_HBOX);
746  this._hideElement(RIP_STATUS_LOGO_POST_SPACER);
747  this._setImageSrc(RIP_STATUS_LOGO, "");
748  },
749 
750  _setLogoURLListener: function cdripController_setLogoURLListener(aElementId, aURL) {
751  var element = document.getElementById(aElementId);
752  if (element) {
753  element.setAttribute('class', "sb-cdrip-link");
754  element.addEventListener('click', function(event) {
755  var browser = SBGetBrowser();
756  browser.loadURI(aURL, null, null);
757  }, false);
758  }
759  },
760 
761  _loadPlaylist: function cdripController_loadPlaylist() {
762  this._mediaListView = this._getMediaListView();
763  if (!this._mediaListView) {
764  Cu.reportError("Unable to get media list view for device.");
765  return;
766  }
767 
768  this._playlist = document.getElementById(CDRIP_PLAYLIST);
769 
770  // Clear the playlist filters. This is a filter-free view.
771  this._clearFilterLists();
772 
773  // Set a flag so sb-playlist won't try to writeback metadata to files
774  this._mediaListView.mediaList.setProperty(
775  "http://songbirdnest.com/data/1.0#dontWriteMetadata", 1);
776 
777  // Make sure we can not drag items from the cd to libraries or playlists
778  this._playlist.disableDrag = true;
779  // Make sure we can not drop items to the cd rip library
780  this._playlist.disableDrop = true;
781 
782  // Setup our columns for the CD View
783  if (!this._mediaListView.mediaList.getProperty(SBProperties.columnSpec)) {
784  this._mediaListView.mediaList.setProperty(SBProperties.columnSpec,
785  "http://songbirdnest.com/data/1.0#shouldRip 25 " +
786  "http://songbirdnest.com/data/1.0#trackNumber 50 a " +
787  "http://songbirdnest.com/data/1.0#cdRipStatus 75 " +
788  "http://songbirdnest.com/data/1.0#trackName 255 " +
789  "http://songbirdnest.com/data/1.0#duration 70 " +
790  "http://songbirdnest.com/data/1.0#artistName 122 " +
791  "http://songbirdnest.com/data/1.0#albumName 122 " +
792  "http://songbirdnest.com/data/1.0#genre 70");
793  }
794 
795  // Get playlist commands (context menu, keyboard shortcuts, toolbar)
796  // Note: playlist commands currently depend on the playlist widget.
797  var mgr =
798  Components.classes["@songbirdnest.com/Songbird/PlaylistCommandsManager;1"]
799  .createInstance(Components.interfaces.sbIPlaylistCommandsManager);
800  var cmds = mgr.request(kPlaylistCommands.MEDIALIST_CDDEVICE_LIBRARY);
801 
802  // Set up the playlist widget
803  this._playlist.bind(this._mediaListView, cmds);
804 
805  // Listen to changes for readonly status.
806  this._libraryListener =
807  new CDRipLibraryListener(this, this._playlist);
808  var flags = Ci.sbIMediaList.LISTENER_FLAGS_ITEMUPDATED;
809  this._mediaListView.mediaList.addListener(this._libraryListener,
810  false,
811  flags);
812  },
813 
817  _getMediaListView: function cdripController_getMediaListView() {
818  if (!this._device) {
819  return null;
820  }
821 
822  var lib = this._getDeviceLibrary();
823  if (!lib) {
824  Cu.reportError("Unable to get library for device: " + this._device.id);
825  return null;
826  }
827 
828  // Create a view for the library and return it.
829  return lib.createView();
830  },
831 
835  _clearFilterLists: function cdripController_clearFilterLists() {
836  this._mediaListView.filterConstraint =
837  LibraryUtils.standardFilterConstraint;
838  },
839 
843  _checkIfNeedsLookup: function cdripController_checkIfNeedsLookup() {
844  if (!this._device) {
845  return;
846  }
847 
848  var lib = this._getDeviceLibrary();
849  if (!lib) {
850  Cu.reportError("Unable to get library for device: " + this._device.id);
851  return;
852  }
853 
854  var deviceNeedsLookupPref = "songbird.cdrip." + lib.guid + ".needsLookup";
855  if (Application.prefs.has(deviceNeedsLookupPref)) {
856  var shouldLookup = Application.prefs.getValue(deviceNeedsLookupPref, false);
857  if (shouldLookup) {
858  Application.prefs.setValue(deviceNeedsLookupPref, false);
859  sbCDDeviceUtils.doCDLookUp(this._device);
860  }
861  }
862  },
863 
864  //----------------------------------------------------------------------------
865  // Transcode job status helpers
866 
867  _getLastTranscodeStatus: function cdripController_getLastTranscodeStatus() {
868  if (!this._device) {
869  return 0;
870  }
871 
872  //Check if any tracks were cancelled, ie the rip operation was cancelled
873  var deviceLibrary = this._getDeviceLibrary();
874  var abortCount = 0;
875  try {
876  // Get all the did not successfully ripped tracks
877  var propArray =
878  Cc["@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1"]
879  .createInstance(Ci.sbIMutablePropertyArray);
880  //tracks with 4|100 were aborted
881  propArray.appendProperty(SBProperties.cdRipStatus, "4|100");
882  propArray.appendProperty(SBProperties.shouldRip, "1");
883 
884  var abortedItems = deviceLibrary.getItemsByProperties(propArray);
885  abortCount = abortedItems.length;
886  }
887  catch (err if err.result == Cr.NS_ERROR_NOT_AVAILABLE) {
888  // deviceLibrary.getItemsByProperties() will throw NS_ERROR_NOT_AVAILABLE
889  // if there are no cancelled rips in the list. Ignore this error.
890  }
891  catch (err) {
892  Cu.reportError("ERROR GETTING TRANSCODE ERROR COUNT " + err);
893  }
894  if (abortCount > 0) return FINAL_TRANSCODE_STATUS_ABORT;
895 
896  // Check for any tracks that have a failed status ('3|100')
897  var errorCount = 0;
898  try {
899  // Get all the did not successfully ripped tracks
900  var propArray =
901  Cc["@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1"]
902  .createInstance(Ci.sbIMutablePropertyArray);
903  propArray.appendProperty(SBProperties.cdRipStatus, "3|100");
904  propArray.appendProperty(SBProperties.shouldRip, "1");
905 
906  var rippedItems = deviceLibrary.getItemsByProperties(propArray);
907  errorCount = rippedItems.length;
908  }
909  catch (err if err.result == Cr.NS_ERROR_NOT_AVAILABLE) {
910  // deviceLibrary.getItemsByProperties() will throw NS_ERROR_NOT_AVAILABLE
911  // if there are no failed rips in the list. Ignore this error.
912  }
913  catch (err) {
914  Cu.reportError("ERROR GETTING TRANSCODE ERROR COUNT " + err);
915  }
916 
917  var status = FINAL_TRANSCODE_STATUS_SUCCESS;
918  if (errorCount > 0) {
919  // Compare the error count to how many tracks were supposed to be ripped.
920  var shouldRipItems =
921  deviceLibrary.getItemsByProperty(SBProperties.shouldRip, "1");
922  if (errorCount == shouldRipItems.length)
924  else
926  }
927 
928  return status;
929  },
930 
931  _getDeviceLibrary: function cdripController_getDeviceLibrary() {
932  if (this._deviceLibrary)
933  return this._deviceLibrary;
934 
935  if (!this._device) {
936  return null;
937  }
938 
939  // Get the libraries for device
940  var libraries = this._device.content.libraries;
941  if (libraries.length < 1) {
942  // Oh no, we have no libraries
943  Cu.reportError("Device " + this._device + " has no libraries!");
944  return null;
945  }
946 
947  // Get the requested library
948  this._deviceLibrary = libraries.queryElementAt(0, Ci.sbIMediaList);
949  if (!this._deviceLibrary) {
950  Cu.reportError("Unable to get library for device: " + aDevice.id);
951  return null;
952  }
953 
954  return this._deviceLibrary;
955  },
956 
960  _getDevice: function cdripController_getDevice() {
961  // Get the device id from the query string in the uri
962  var queryMap = this._parseQueryString();
963 
964  this._deviceID = queryMap["device-id"];
965 
966  // Get the device for this media view
967  var deviceManager = Cc["@songbirdnest.com/Songbird/DeviceManager;2"]
968  .getService(Ci.sbIDeviceManager2);
969 
970  try {
971  return deviceManager.getDevice(Components.ID(this._deviceID));
972  } catch (x) {
973  Cu.reportError("Device: " + this._deviceID + " does not exist");
974  }
975 
976  return null;
977  },
978 
983  _parseQueryString: function cdripController_parseQueryString() {
984  var queryString = (location.search || "?").substr(1).split("&");
985  var queryObject = {};
986  var key, value;
987  for each (var pair in queryString) {
988  [key, value] = pair.split("=");
989  queryObject[key] = unescape(value);
990  }
991  return queryObject;
992  }
993 };
994 
995 //------------------------------------------------------------------------------
996 // sbIMediaPage implementation
997 
998 function MediaPageImpl() {}
999 MediaPageImpl.prototype = {
1000  get mediaListView() {
1001  return window.cdripController._mediaListView;
1002  },
1003  set mediaListView(value) {},
1004 
1005  get isOnlyView() true,
1006 
1007  highlightItem: function(aIndex) {
1008  window.cdripController._playlist.highlightItem(aIndex);
1009  },
1010 
1011  canDrop: function(aEvent, aSession) {
1012  return false;
1013  },
1014  onDrop: function(aEvent, aSession) {
1015  return false;
1016  }
1017 }
1018 
1019 //==============================================================================
1020 //
1021 // @brief sbIMediaListListener class for validating the playlist tree when the
1022 // readonly state changes.
1023 //
1024 //==============================================================================
1025 
1026 function CDRipLibraryListener(aCallback, aPlaylistObject)
1027 {
1028  this._mCallback = aCallback;
1029  this._mPlaylist = aPlaylistObject;
1030  this._mTreeBoxObject = aPlaylistObject.tree.boxObject;
1031 }
1032 
1033 CDRipLibraryListener.prototype =
1034 {
1035  _mCallback: null,
1036  _mTreeBoxObject: null,
1037  _mPlaylist: null,
1038 
1039  //----------------------------------------------------------------------------
1040  // sbIMediaListListener
1041 
1042  onItemAdded: function(aMediaList, aMediaItem, aIndex) {
1043  return true; // ignore
1044  },
1045 
1046  onBeforeItemRemoved: function(aMediaList, aMediaItem, aIndex) {
1047  return true; // ignore
1048  },
1049 
1050  onAfterItemRemoved: function(aMediaList, aMediaItem, aIndex) {
1051  return true; // ignore
1052  },
1053 
1054  onItemUpdated: function(aMediaList, aMediaItem, aProperties) {
1055  // Check for the |isReadOnly| property.
1056  for (var i = 0; i < aProperties.length; i++) {
1057  if (aProperties.getPropertyAt(i).id == SBProperties.isReadOnly) {
1058  this._mTreeBoxObject.invalidate();
1059  this._mPlaylist.refreshCommands(false);
1060  break;
1061  }
1062  }
1063 
1064  return true;
1065  },
1066 
1067  onItemMoved: function(aMediaList, aFromIndex, aToIndex) {
1068  return true; // ignore.
1069  },
1070 
1071  onBeforeListCleared: function(aMediaList, aExcludeLists) {
1072  return true; // ignore.
1073  },
1074 
1075  onListCleared: function(aMediaList, aExcludeLists) {
1076  return true; // ignore
1077  },
1078 
1079  onBatchBegin: function(aMediaList) {
1080  // ignore
1081  },
1082 
1083  onBatchEnd: function(aMediaList) {
1084  // ignore
1085  },
1086 
1087  QueryInterface: XPCOMUtils.generateQI([Ci.sbIMediaListListener])
1088 };
1089 
const RIP_STATUS_EJECT_CD_BUTTON
const Cu
const FINAL_TRANSCODE_STATUS_PARTIAL
const RIP_STATUS_BUTTON_HBOX
const Cc
const RIP_STATUS_STOP_RIP_BUTTON
var Application
Definition: sbAboutDRM.js:37
var menuItem
Definition: FeedWriter.js:970
function SBFormattedString(aKey, aParams, aDefault, aStringBundle)
onPageChanged aValue
Definition: FeedWriter.js:1395
const CDRIP_PLAYLIST
const RIP_SETTINGS_HBOX
const SB_NS
sbDeviceFirmwareAutoCheckForUpdate prototype flags
var event
const RIP_STATUS_IMAGE_PRE_SPACER
sbOSDControlService prototype QueryInterface
const RIP_SETTINGS_FORMAT_LABEL
const RIP_SETTINGS_QUALITY_LABEL
const RIP_STATUS_LOGO_HBOX
const CDRIP_HBOX_PLAIN_CLASS
const RIP_STATUS_LOGO_POST_SPACER
function SBString(aKey, aDefault, aStringBundle)
Definition: StringUtils.jsm:93
const RIP_STATUS_RIP_CD_BUTTON
let window
const LOGO_DISPLAY_MIN_TIMER
const RIP_STATUS_IMAGE_POST_SPACER
function MediaPageImpl()
const RIP_STATUS_HBOX
const RIP_SETTINGS_BITRATE_HBOX
const RIP_STATUS_IMAGE
const RIP_STATUS_IMAGE_HBOX
Lastfm onLoad
Definition: mini.js:36
const RIP_STATUS_LABEL
const RIP_STATUS_LOGO_PRE_SPACER
const FINAL_TRANSCODE_STATUS_FAILED
var CDRIP_BASE
return null
Definition: FeedWriter.js:1143
const RIP_STATUS_LABEL_HBOX
countRef value
Definition: FeedWriter.js:1423
const RIP_COMMAND_STARTRIP
const RIP_STATUS_BUTTONS_PRE_SPACER
const Cr
const RIP_STATUS_LOGO
const FINAL_TRANSCODE_STATUS_ABORT
observe topic
Definition: FeedWriter.js:1326
const Ci
Javascript wrappers for common library tasks.
const RIP_SPACER
const RIP_COMMAND_STOPRIP
var browser
Definition: openLocation.js:42
function onUnload()
onUnload - called when the cover preview window unloads.
Definition: coverPreview.js:36
const LOOKUP_MESSAGE_DELAY_TIMER
const CDRIP_HBOX_CLASS
observe data
Definition: FeedWriter.js:1329
_getSelectedPageStyle s i
const RIP_SETTINGS_BUTTON
var sbCDDeviceUtils
const RIP_COMMAND_EJECT
const FINAL_TRANSCODE_STATUS_SUCCESS
sbDeviceFirmwareAutoCheckForUpdate prototype observe