sbURIImportService.js
Go to the documentation of this file.
1 /*
2 //
3 // BEGIN SONGBIRD GPL
4 //
5 // This file is part of the Songbird web player.
6 //
7 // Copyright(c) 2005-2008 POTI, Inc.
8 // http://songbirdnest.com
9 //
10 // This file may be licensed under the terms of of the
11 // GNU General Public License Version 2 (the "GPL").
12 //
13 // Software distributed under the License is distributed
14 // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
15 // express or implied. See the GPL for the specific language
16 // governing rights and limitations.
17 //
18 // You should have received a copy of the GPL along with this
19 // program. If not, go to http://www.gnu.org/licenses/gpl.html
20 // or write to the Free Software Foundation, Inc.,
21 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 // END SONGBIRD GPL
24 //
25 */
26 
27 const Cc = Components.classes;
28 const Ci = Components.interfaces;
29 const Cu = Components.utils;
30 
31 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
32 Cu.import("resource://app/jsmodules/sbProperties.jsm");
33 Cu.import("resource://app/jsmodules/SBJobUtils.jsm");
34 Cu.import("resource://app/jsmodules/ArrayConverter.jsm");
35 
36 //------------------------------------------------------------------------------
37 
38 //
39 // @brief A XPCOM service to help with importing URI's into a specified media
40 // list. For more information, see sbIURIImportService.idl.
41 //
43 {
44 }
45 
46 sbURIImportService.prototype =
47 {
48  _importInProgress: false, // are we currently importing a drop ?
49  _uriArray: null, // array of URI's to import
50  _directoryList: [], // queue of nsIFile directories to scan
51  _targetList: null, // target mediaList for the drop
52  _targetPosition: -1, // position in the mediaList we should drop at
53  _firstMediaItem: null, // first mediaItem that was handled in this drop
54  _scanList: null, // list of newly created medaItems for metadata scan
55  _window: null, // window that received this drop
56  _listener: null, // listener object, for notifications
57  _totalImported: 0, // number of items imported in the library
58  _totalInserted: 0, // number of items inserted in the medialist
59  _totalDups: 0, // number of items we already had in the library
60  _otherDrops: 0, // number of other drops handled (eg, XPI, JAR)
61  _mainLibrary: null, // The main library
62 
63 
64  // sbIURIImportService
65  importURIArray: function(aMutableURIArray,
66  aDOMWindow,
67  aTargetMediaList,
68  aTargetPosition,
69  aImportListener)
70  {
71  this._window = aDOMWindow;
72  this._targetList = aTargetMediaList;
73  this._targetPosition = aTargetPosition;
74  this._listener = aImportListener;
75 
76  // the array to record items to feed to the metadata scanner
77  if (!this._scanList) {
78  this._scanList = Cc["@songbirdnest.com/moz/xpcom/threadsafe-array;1"]
79  .createInstance(Ci.nsIMutableArray);
80  }
81 
82  // Reset stats and various job variables
83  this._firstMediaItem = null;
84  this._totalImported = 0;
85  this._totalInserted = 0;
86  this._totalDups = 0;
87  this._mainLibrary = Cc["@songbirdnest.com/Songbird/library/Manager;1"]
88  .getService(Ci.sbILibraryManager)
89  .mainLibrary;
90 
91  this._uriArray = aMutableURIArray;
92 
93  // If we are already importing, our entries will be processed at the end
94  // of the current batch. Otherwise, we need to start a new one.
95  if (!this._importInProgress) {
96  // Remember that we are currently processing a batch - if more files are
97  // dropped while we're importing, they will be added to the end of the
98  // batch import.
99  this._importInProgress = true;
100 
101  // Begin processing the URI array.
102  // (The first item will be handled immediately, and the subsequent ones
103  // will be processed each time a new "frame" occurs, on a timer).
104  this._importDropFrame();
105  }
106  },
107 
108  // give a little bit of time for the main thread to react to UI events, and
109  // then execute the next frame of the drop import session.
110  _nextImportDropFrame: function()
111  {
112  this._timer = Cc["@mozilla.org/timer;1"]
113  .createInstance(Ci.nsITimer);
114  this._timer.initWithCallback(this, 10, Ci.nsITimer.TYPE_ONE_SHOT);
115  },
116 
117  // nsITimer
118  notify: function(timer)
119  {
120  this._timer = null;
121  this._importDropFrame();
122  },
123 
124  // Executes one frame of the drop import session
125  _importDropFrame: function()
126  {
127  try {
128  // any more files to process ?
129  if (this._uriArray.length > 0) {
130 
131  // get the first of the remaining files
132  var uri = this._uriArray.queryElementAt(0, Ci.nsIURI);
133 
134  // remove it from the array of files to process
135  this._uriArray.removeElementAt(0);
136 
137  if (uri) {
138  // make a file URL object and check if the object is a directory.
139  // if it is a directory, then we record it in a separate array,
140  // which we will process at the end of the batch.
141  var fileUrl;
142  try {
143  fileUrl = uri.QueryInterface(Ci.nsIFileURL);
144  }
145  catch (e) {
146  fileUrl = null;
147  }
148  if (fileUrl) {
149  // is this is a directory?
150  if (fileUrl.file.isDirectory()) {
151  // yes, record it and delay processing
152  this._directoryList.push(fileUrl.file);
153  // continue with the current batch
154  this._nextImportDropFrame();
155  return;
156  }
157  }
158 
159  // process this item
160  this._importDropFile(uri);
161  }
162 
163  // continue with the current batch
164  this._nextImportDropFrame();
165  }
166  else {
167  // there are no more items in the drop array, start the metadata scanner
168  // if we created any mediaitem
169  if (this._scanList && this._scanList.length > 0) {
170  var metadataService =
171  Cc["@songbirdnest.com/Songbird/FileMetadataService;1"]
172  .getService(Ci.sbIFileMetadataService);
173 
174  metadataService.read(this._scanList);
175  this._scanList = null;
176  }
177 
178  // see if there are any directories to be scanned now.
179  if (this._directoryList.length > 0) {
180 
181  // yes, there are, import them.
182  this._importDropDirectories();
183 
184  // continue with the current batch, in case more stuff
185  // has been dropped. this shouldnt be possible because the
186  // mediascan dialog is modal, but it is better to check than
187  // to lose drops
188  this._nextImportDropFrame();
189  }
190  else {
191  // all done.
192  this._dropComplete();
193  }
194  }
195  }
196  catch (e) {
197  // oops, something wrong happened, we do not want to abort the whole
198  // import batch tho, so just print a debug message and try the next
199  // frame
200  Components.utils.reportError(e);
201  this._nextImportDropFrame();
202  }
203  },
204 
205  // import the given file URI into the target library and optionally inserts in
206  // into the target playlist at the desired spot. if the item already exists,
207  // it is only inserted to the playlist
208  _importDropFile: function(aURI) {
209  try {
210  // is this a media url ?
211  let typeSniffer =
212  Cc["@songbirdnest.com/Songbird/Mediacore/TypeSniffer;1"]
213  .createInstance(Ci.sbIMediacoreTypeSniffer);
214  let Application = Cc["@mozilla.org/fuel/application;1"]
215  .getService(Ci.fuelIApplication);
216 
217  let isValidMediaURL = typeSniffer.isValidMediaURL(aURI);
218  if (!Application.prefs.getValue("songbird.mediascan.enableVideoImporting", true)
219  && typeSniffer.isValidVideoURL(aURI)) {
220  isValidMediaURL = false;
221  }
222 
223  if (isValidMediaURL) {
224  // check whether the item already exists in the library
225  // for the target list
226  let item =
227  this._getFirstItemByProperty(this._targetList.library,
228  SBProperties.contentURL,
229  aURI.spec);
230  // If we didn't find the content URL try the originURL
231  if (!item) {
232  item =
233  this._getFirstItemByProperty(this._targetList.library,
234  SBProperties.originURL,
235  aURI.spec);
236  }
237  let mainLibraryItem = false;
238  // If we're not dropping on the main library look to see if the main
239  // library knows about this item. If it does, then use that item to add
240  if (!this._targetList.library.equals(this._mainLibrary)) {
241  if (!item) {
242  item = this._getFirstItemByProperty(this._mainLibrary,
243  SBProperties.contentURL,
244  aURI.spec);
245  }
246  // If we didn't find the content URL try the originURL
247  if (!item) {
248  item =
249  this._getFirstItemByProperty(this._mainLibrary,
250  SBProperties.originURL,
251  aURI.spec);
252  }
253  mainLibraryItem = item != null;
254  }
255  // if the item didnt exist before, create it now
256  let itemAdded = false;
257  if (mainLibraryItem || !item) {
258  let holder = {};
259  if (!mainLibraryItem) {
260 
261  itemAdded =
262  this._targetList.library.createMediaItemIfNotExist(aURI,
263  null,
264  holder);
265  item = holder.value;
266  }
267  else {
268 
269  // If we add it from the main library, we need to get the item
270  // that might have been created and use that
271  let newItem = this._targetList.library.addItem(item);
272  itemAdded = newItem && !newItem.equals(item);
273  if (newItem) {
274  item = newItem;
275  }
276  }
277  if (itemAdded) {
278  this._scanList.appendElement(item, false);
279  this._totalImported++;
280  }
281  }
282 
283  // The item was not added because it was a duplicate.
284  if (!itemAdded) {
285  this._totalDups++;
286  }
287 
288  // if the item is valid, and we are inserting in a medialist, insert it
289  // to the requested position
290  if (item) {
291  if ((this._targetList instanceof Ci.sbIOrderableMediaList) &&
292  (this._targetPosition >= 0) &&
293  (this._targetPosition < this._targetList.length))
294  {
295  this._targetList.insertBefore(this._targetPosition, item);
296  this._targetPosition++;
297  }
298  else {
299  this._targetList.add(item);
300  }
301  this._totalInserted++;
302  }
303  // report the first item that was dropped
304  if (!this._firstMediaItem) {
305  this._firstMediaItem = item;
306  if (this._listener) {
307  this._listener.onFirstMediaItem(this._targetList, item);
308  }
309  }
310  }
311  }
312  catch (e) {
313  Components.utils.reportError(e);
314  }
315  },
316 
317  // search for an item inside a list based on a property value. this is used
318  // by the drop handler to determine if a drop item is already in the target
319  // library, by looking for its contentURL.
320  _getFirstItemByProperty: function(aMediaList, aProperty, aValue)
321  {
322  var listener = {
323  item: null,
324  onEnumerationBegin: function() {
325  },
326  onEnumeratedItem: function(list, item) {
327  this.item = item;
328  return Components.interfaces.sbIMediaListEnumerationListener.CANCEL;
329  },
330  onEnumerationEnd: function() {
331  }
332  };
333 
334  aMediaList.enumerateItemsByProperty(aProperty,
335  aValue,
336  listener);
337  return listener.item;
338  },
339 
340  // trigger the import of all directory entries that have been defered until
341  // this point. this launches the media scanner dialog box.
342  _importDropDirectories: function()
343  {
344  var importService =
345  Cc['@songbirdnest.com/Songbird/DirectoryImportService;1']
346  .getService(Ci.sbIDirectoryImportService);
347 
348  var job = importService.import(ArrayConverter.nsIArray(this._directoryList),
349  this._targetList, this._targetPosition);
350 
351  // Reset list of directories
352  this._directoryList = [];
353 
354  SBJobUtils.showProgressDialog(job, this._window, 0);
355 
356  // If this job provided the first item, we may want to play it
357  if (!this._firstMediaItem) {
358  var allItems = job.enumerateAllItems();
359  if (allItems.hasMoreElements()) {
360  this._firstMediaItem =
361  allItems.getNext().QueryInterface(Ci.sbIMediaItem);
362 
363  if (this._listener) {
364  this._listener.
365  onFirstMediaItem(this._targetList, this._firstMediaItem);
366  }
367  }
368  }
369 
370  this._totalImported += job.totalAddedToLibrary;
371  this._totalInserted += job.totalAddedToMediaList;
372  this._totalDups += job.totalDuplicates;
373  },
374 
375  // called when the whole drop handling operation has completed, used
376  // to notify the original caller and free up any resources we can
377  _dropComplete: function()
378  {
379  if (this._listener) {
380  this._listener.onImportComplete(this._targetList,
381  this._totalImported,
382  this._totalDups,
383  this._otherDrops,
384  this._totalInserted,
385  this._otherDrops);
386  }
387 
388  // and reset references we do not need anymore, coz leaks suck
389  this._importInProgress = false;
390  this._targetList = null;
391  this._scanList = null;
392  this._window = null;
393  this._listener = null;
394  this._mainLibrary = null;
395  },
396 
397  QueryInterface: XPCOMUtils.generateQI([Ci.sbIURIImportService])
398 };
399 
400 //------------------------------------------------------------------------------
401 // XPCOM Registration
402 
403 sbURIImportService.prototype.classDescription =
404  "Songbird URI Import Service";
405 sbURIImportService.prototype.classID =
406  Components.ID("{CC435D34-F76A-458F-B786-FAF897CA69BD}");
407 sbURIImportService.prototype.contractID =
408  "@songbirdnest.com/uri-import-service;1";
409 
410 function NSGetModule(compMgr, fileSpec) {
411  return XPCOMUtils.generateModule([sbURIImportService]);
412 }
413 
var Application
Definition: sbAboutDRM.js:37
onPageChanged aValue
Definition: FeedWriter.js:1395
function NSGetModule(compMgr, fileSpec)
sbOSDControlService prototype QueryInterface
sbDeviceFirmwareAutoCheckForUpdate prototype _timer
TimerLoop prototype notify
this _window
Definition: FeedWriter.js:1158
const Ci
return null
Definition: FeedWriter.js:1143
var uri
Definition: FeedWriter.js:1135
const Cc
const Cu
function sbURIImportService()