26 const Cc = Components.classes;
27 const Ci = Components.interfaces;
28 const Cr = Components.results;
29 const Ce = Components.Exception;
30 const Cu = Components.utils;
32 Cu.import(
"resource://gre/modules/XPCOMUtils.jsm");
33 Cu.import(
"resource://app/jsmodules/SBJobUtils.jsm");
34 Cu.import(
"resource://app/jsmodules/ArrayConverter.jsm");
35 Cu.import(
"resource://app/jsmodules/sbProperties.jsm");
36 Cu.import(
"resource://app/jsmodules/sbLibraryUtils.jsm");
37 Cu.import(
"resource://app/jsmodules/StringUtils.jsm");
38 Cu.import(
"resource://app/jsmodules/DebugUtils.jsm");
39 Cu.import(
"resource://app/jsmodules/URLUtils.jsm");
45 const LOG = DebugUtils.generateLogFunction(
"sbDirectoryImportJob");
62 if (!(aInputArray instanceof
Ci.nsIArray &&
63 aInputArray.length > 0) ||
64 !(aTargetMediaList instanceof
Ci.sbIMediaList)) {
65 throw Components.results.NS_ERROR_INVALID_ARG;
68 SBJobUtils.JobBase.call(
this);
70 this._inputFiles = ArrayConverter.JSArray(aInputArray);
71 this.targetMediaList = aTargetMediaList;
72 this.targetIndex = aTargetIndex;
74 this._importService = aImportService;
75 this._typeSniffer = aTypeSniffer;
76 this._metadataScanner = aMetadataScanner;
79 this._titleText =
SBString(
"media_scan.scanning");
80 this._statusText =
SBString(
"media_scan.adding");
83 this._canCancel =
true;
86 this._itemURIStrings = [];
88 this._libraryUtils =
Cc[
"@songbirdnest.com/Songbird/library/Manager;1"]
89 .getService(
Ci.sbILibraryUtils);
91 if (
"@songbirdnest.com/Songbird/TimingService;1" in
Cc) {
92 this._timingService = Cc[
"@songbirdnest.com/Songbird/TimingService;1"]
93 .getService(
Ci.sbITimingService);
94 this._timingIdentifier =
"DirImport" + gCounter++;
98 __proto__: SBJobUtils.JobBase.prototype,
101 [
Ci.sbIDirectoryImportJob,
Ci.sbIJobProgress,
Ci.sbIJobProgressUI,
102 Ci.sbIJobProgressListener,
Ci.sbIJobCancelable,
Ci.nsIObserver,
107 var
interfaces = [
Ci.sbIDirectoryImportJob,
Ci.sbIJobProgress,
Ci.sbIJobProgressListener,
108 Ci.sbIJobCancelable,
Ci.nsIClassInfo,
Ci.nsISupports];
109 count.value = interfaces.length;
113 totalAddedToMediaList : 0,
114 totalAddedToLibrary : 0,
116 targetMediaList :
null,
120 _pollingTimer :
null,
121 FILESCAN_POLL_INTERVAL : 33,
125 _fileScanQuery :
null,
129 _fileExtensions :
null,
130 _flaggedFileExtensions :
null,
131 _foundFlaggedExtensions :
null,
134 _importService :
null,
138 _metadataScanner :
null,
141 _itemURIStrings : [],
144 _itemsInMainLib : [],
149 BATCHCREATE_SIZE : 300,
158 _currentMediaItems :
null,
160 _currentBatchSize : 0,
164 _inLibraryBatch :
false,
167 _libraryUtils :
null,
170 _timingService :
null,
171 _timingIdentifier :
null,
178 enumerateAllItems:
function DirectoryImportJob_enumerateAllItems() {
184 if (this._itemURIStrings.length == 0) {
185 return ArrayConverter.enumerator([]);
191 if (this._currentMediaItems &&
192 this._itemURIStrings.length ==
this._currentMediaItems.length) {
193 return this._currentMediaItems.enumerate();
202 var uriEnumerator = ArrayConverter.enumerator(this._itemURIStrings);
203 var library = this.targetMediaList.library;
204 const BATCHSIZE = this.BATCHCREATE_SIZE;
211 onEnumerationBegin:
function() {},
212 onEnumeratedItem:
function(list, item) {
213 this.mediaItems.push(item);
215 onEnumerationEnd:
function() {},
219 return (this.mediaItems.length ||
220 uriEnumerator.hasMoreElements());
222 getNext:
function() {
224 if (this.mediaItems.length == 0) {
226 var propertyArray = SBProperties.createArray();
228 while (uriEnumerator.hasMoreElements() && counter < BATCHSIZE) {
229 var itemURIStr = uriEnumerator.getNext().QueryInterface(
Ci.nsISupportsString);
230 propertyArray.appendProperty(SBProperties.contentURL, itemURIStr.data);
233 library.enumerateItemsByProperties(propertyArray,
this);
236 return this.mediaItems.shift();
240 Ci.nsISimpleEnumerator,
Ci.sbIMediaListEnumerationListener]),
250 begin:
function DirectoryImportJob_begin() {
251 if (this._timingService) {
252 this._timingService.startPerfTimer(this._timingIdentifier);
258 .getService(
Ci.fuelIApplication);
260 this._fileScanner =
Cc[
"@songbirdnest.com/Songbird/FileScan;1"]
261 .createInstance(Components.interfaces.sbIFileScan);
265 var extensions = this._typeSniffer.mediaFileExtensions;
266 if (!Application.prefs.getValue(
"songbird.mediascan.enableVideoImporting",
true)) {
268 extensions = this._typeSniffer.audioFileExtensions;
270 this._fileExtensions = [];
271 while (extensions.hasMore()) {
272 this._fileExtensions.push(extensions.getNext());
275 dump(
"WARNING: DirectoryImportJob_begin could not find supported file extensions. " +
276 "Assuming test mode, and using a hardcoded list.\n");
277 this._fileExtensions = [
"mp3",
"ogg",
"flac"];
286 var shouldWarnFlagExtensions = Application.prefs.getValue(
287 "songbird.mediaimport.warn_filtered_exts",
true);
288 if (shouldWarnFlagExtensions) {
289 this._foundFlaggedExtensions =
290 Cc[
"@songbirdnest.com/moz/xpcom/threadsafe-array;1"]
291 .createInstance(
Ci.nsIMutableArray);
292 this._flaggedFileExtensions = [];
294 var unsupportedExtensions = this._typeSniffer.unsupportedVideoFileExtensions;
295 while (unsupportedExtensions.hasMore()) {
296 var item = unsupportedExtensions.getNext();
297 this._flaggedFileExtensions.push(item);
301 Components.utils.reportError(
302 e +
"\nCould not add unsupported file extensions to the file scan!");
311 var library = this.targetMediaList.library;
312 if (library instanceof
Ci.sbILocalDatabaseLibrary) {
313 this._inLibraryBatch =
true;
314 library.forceBeginUpdateBatch();
317 this._startNextDirectoryScan();
320 this._pollingTimer =
Cc[
"@mozilla.org/timer;1"].createInstance(
Ci.nsITimer);
321 this._pollingTimer.init(
this, this.FILESCAN_POLL_INTERVAL,
Ci.nsITimer.TYPE_REPEATING_SLACK);
328 _startNextDirectoryScan:
function DirectoryImportJob__startNextDirectoryScan() {
331 if (this._fileScanQuery && this._fileScanQuery.isScanning()) {
333 "DirectoryImportJob__startDirectoryScan called with a scan in progress?");
337 var
file = this._inputFiles.shift();
339 if (file && file instanceof
Ci.nsIFileURL) {
345 if (!file || !(file instanceof
Ci.nsIFile)) {
346 Cu.reportError(
"DirectoryImportJob__startNextDirectoryScan: invalid directory");
351 if (!this._fileScanQuery) {
352 this._fileScanQuery =
Cc[
"@songbirdnest.com/Songbird/FileScanQuery;1"]
353 .createInstance(Components.interfaces.sbIFileScanQuery);
354 var fileScanQuery = this._fileScanQuery;
355 this._fileExtensions.forEach(
function(ext) { fileScanQuery.addFileExtension(ext) });
358 if (this._flaggedFileExtensions) {
359 this._flaggedFileExtensions.forEach(
function(ext) {
360 fileScanQuery.addFlaggedFileExtension(ext);
365 if (file.exists() && file.isDirectory()) {
366 this._fileScanQuery.setDirectory(file.path);
367 this._fileScanQuery.setRecurse(
true);
368 this._fileScanner.submitQuery(this._fileScanQuery);
370 var urispec = this._libraryUtils.getFileContentURI(file).spec;
373 var isValidExt =
false;
374 var dotIndex = urispec.lastIndexOf(
".");
376 var fileExt = urispec.substr(dotIndex + 1);
377 for (var
i = 0;
i < this._fileExtensions.length;
i++) {
378 if (this._fileExtensions[
i] == fileExt) {
386 var supportsString =
Cc[
"@mozilla.org/supports-string;1"]
387 .createInstance(
Ci.nsISupportsString);
388 supportsString.data = urispec;
389 this._itemURIStrings.push(supportsString);
398 _onPollFileScan:
function DirectoryImportJob__onPollFileScan() {
402 if (!this._fileScanQuery.isScanning()) {
404 if (this._inputFiles.length > 0) {
405 this._startNextDirectoryScan();
409 var fileCount = this._fileScanQuery.getFileCount();
410 if (fileCount > 0 ) {
411 var
strings = this._fileScanQuery.getResultRangeAsURIStrings(0, fileCount - 1);
412 Array.prototype.push.apply(this._itemURIStrings, ArrayConverter.JSArray(strings));
414 this._finishFileScan();
415 this._startMediaItemCreation();
419 var text = this._fileScanQuery.getLastFileFound();
420 text = decodeURIComponent(text.split(
"/").pop());
421 if (text.length > 60) {
422 text = text.substring(0, 10) +
"..." + text.substring(text.length - 40);
424 this._statusText = text;
425 this.notifyJobProgressListeners();
433 _finishFileScan:
function DirectoryImportJob__finishFileScan() {
434 if (this._fileScanQuery.flaggedExtensionsFound) {
435 var
ioService =
Cc[
"@mozilla.org/network/io-service;1"]
436 .getService(
Ci.nsIIOService);
439 var flaggedExtCount = this._fileScanQuery.getFlaggedFileCount();
440 for (var
i = 0;
i < flaggedExtCount;
i++) {
441 var curFlaggedPath = this._fileScanQuery.getFlaggedFilePath(
i);
442 var curFlaggedURL = ioService.newURI(curFlaggedPath,
null,
null);
443 if (curFlaggedURL instanceof
Ci.nsIFileURL) {
444 var curFlaggedFile = curFlaggedURL.QueryInterface(
Ci.nsIFileURL).file;
445 var curSupportsStr =
Cc[
"@mozilla.org/supports-string;1"]
446 .createInstance(
Ci.nsISupportsString);
447 curSupportsStr.data = curFlaggedFile.leafName;
449 this._foundFlaggedExtensions.appendElement(curSupportsStr,
false);
454 if (this._fileScanner) {
455 this._fileScanner.finalize();
456 this._fileScanner =
null;
458 if (this._pollingTimer) {
459 this._pollingTimer.cancel();
460 this._pollingTimer =
null;
463 this._total = this._itemURIStrings.length;
476 _filterItems:
function(aURIs, aItemsInMainLib) {
479 var targetLib = this.targetMediaList.library;
481 var isMainLib = this.targetMediaList.library.equals(mainLib);
491 function filterDupes(
uri)
493 var uriObj =
uri.QueryInterface(
Ci.nsISupportsString);
494 var uriSpec = uriObj.data;
496 LOG(
"Searching for URI: " + uriSpec);
505 items = ArrayConverter.JSArray(
506 mainLib.getItemsByProperty(SBProperties.contentURL,
510 LOG(
"Exception: " + e);
513 if (items.length > 0) {
514 LOG(
" Found in main library");
525 items = ArrayConverter.JSArray(
526 targetLib.getItemsByProperty(SBProperties.originURL,
530 LOG(
"Exception: " + e);
532 if (items.length > 0) {
533 LOG(
" Found in target library");
534 ++this.totalDuplicates;
537 LOG(
" Item needs to be created");
542 return aURIs.filter(filterDupes);
549 _startMediaItemCreation:
550 function DirectoryImportJob__startMediaItemCreation() {
551 if (!this._fileScanQuery) {
553 "DirectoryImportJob__startMediaItemCreation called with invalid state");
558 var targetLib = this.targetMediaList.library;
560 if (this._nextURIIndex >= this._itemURIStrings.length &&
561 this._itemsInMainLib.length === 0) {
562 LOG(
"Finish creating and adding all items");
567 else if (this._itemsInMainLib.length) {
568 LOG(
"Finish creating all items, now adding items");
571 var addSomeListener = {
572 onProgress:
function(aItemsProcessed, aCompleted) {},
573 onItemAdded:
function(aMediaItem) {
574 aMediaItem.setProperty(SBProperties.originIsInMainLibrary,
"1");
577 LOG(
"Adding items completed");
578 self._itemsInMainLib = [];
583 targetLib.addMediaItems(ArrayConverter.enumerator(
this._itemsInMainLib),
589 this._statusText =
SBString(
"media_scan.adding");
590 this.notifyJobProgressListeners();
594 this._currentBatchSize = Math.min(this.BATCHCREATE_SIZE,
595 this._itemURIStrings.length -
this._nextURIIndex);
596 var endIndex = this._nextURIIndex + this._currentBatchSize;
597 var
uris = this._itemURIStrings.slice(this._nextURIIndex, endIndex);
598 this._nextURIIndex = endIndex;
600 LOG(
"Creating media items");
602 this._itemsInMainLib = [];
603 LOG(
"Total items=" + uris.length);
604 uris = this._filterItems(uris, this._itemsInMainLib);
606 LOG(
"Items in main library=" + this._itemsInMainLib.length);
607 LOG(
"Items needing to be created=" + uris.length);
615 const NO_PROPS = SBProperties.createArray();
616 const IMAGE_PROPS =
function () {
617 var props = SBProperties.createArray();
618 props.appendProperty(SBProperties.contentType,
"image");
621 var propsArray = uris.map(
function (aURISpec) {
622 var
uri = URLUtils.newURI(aURISpec);
623 if (this._typeSniffer.isValidImageURL(uri)) {
631 var batchCreateListener = {
632 onProgress:
function(aIndex) {},
634 LOG(
"Finished creating batch of items");
635 thisJob._onItemCreation(aMediaItems, aResult, uris.length);
641 targetLib.batchCreateMediaItemsAsync(batchCreateListener,
642 ArrayConverter.nsIArray(uris),
643 ArrayConverter.nsIArray(propsArray),
652 _onItemCreation:
function DirectoryImportJob__onItemCreation(aMediaItems,
654 itemsToCreateCount) {
657 LOG(
"batchCreateMediaItemsAsync created " + aMediaItems.length)
658 if (Components.isSuccessCode(aResult)) {
659 this.totalDuplicates += (itemsToCreateCount - aMediaItems.length);
660 this._currentMediaItems = aMediaItems;
662 Cu.reportError(
"DirectoryImportJob__onItemCreation: aResult == " + aResult);
663 this._currentMediaItems = Components.classes[
"@songbirdnest.com/moz/xpcom/threadsafe-array;1"]
664 .createInstance(Components.interfaces.nsIArray);
667 this.totalAddedToLibrary += this._currentMediaItems.length;
668 if (this._currentMediaItems.length > 0) {
671 this._startMetadataScan();
675 this._startMediaItemCreation();
684 function DirectoryImportJob__startMetadataScan() {
685 if (this._currentMediaItems && this._currentMediaItems.length > 0) {
687 var metadataJob = this._metadataScanner.read(this._currentMediaItems);
690 this.delegateJobProgress(metadataJob);
700 _insertFoundItemsIntoTarget:
701 function DirectoryImportJob__insertFoundItemsIntoTarget() {
705 if (!(this.targetMediaList instanceof
Ci.sbILibrary)) {
707 if (this._itemURIStrings.length > 0) {
708 var originalLength = this.targetMediaList.length;
710 if ((this.targetMediaList instanceof
Ci.sbIOrderableMediaList) &&
711 (
this.targetIndex >= 0) &&
712 (
this.targetIndex <
this.targetMediaList.length)) {
713 this.targetMediaList.insertSomeBefore(this.targetIndex, this.enumerateAllItems());
716 this.targetMediaList.addSome(this.enumerateAllItems());
718 this.totalAddedToMediaList = this.targetMediaList.length - originalLength;
729 onJobDelegateCompleted:
function DirectoryImportJob_onJobDelegateCompleted() {
734 this._progress += this._jobProgressDelegate.progress;
737 this.delegateJobProgress(
null);
741 this._startMediaItemCreation();
749 return (this._jobProgressDelegate) ?
750 this._jobProgressDelegate.progress + this._progress : this._progress;
762 return this._titleText;
766 cancel:
function DirectoryImportJob_cancel() {
767 if (!this.canCancel) {
768 throw new Error(
"DirectoryImportJob not currently cancelable")
771 if (this._fileScanner) {
772 this._finishFileScan();
774 }
else if (this._jobProgressDelegate) {
776 this._jobProgressDelegate.cancel();
779 if (this._fileScanQuery) {
780 this._fileScanQuery.cancel();
781 this._fileScanQuery =
null;
785 if (this._currentMediaItems && this._currentMediaItems.length > 0) {
786 this.targetMediaList.library.removeSome(this._currentMediaItems.enumerate());
787 this.totalAddedToMediaList = 0;
788 this.totalAddedToLibrary = 0;
789 this.totalDuplicates = 0;
796 complete:
function DirectoryImportJob_complete() {
799 this._insertFoundItemsIntoTarget();
801 this._status =
Ci.sbIJobProgress.STATUS_SUCCEEDED;
802 this._statusText =
SBString(
"media_scan.complete");
803 this.notifyJobProgressListeners();
805 this._importService.onJobComplete();
811 var library = this.targetMediaList.library;
812 if (library instanceof
Ci.sbILocalDatabaseLibrary &&
813 this._inLibraryBatch)
815 this._inLibraryBatch =
false;
816 library.forceEndUpdateBatch();
823 if (this.totalAddedToLibrary > this.BATCHCREATE_SIZE) {
826 library.optimize(
true);
830 var dbEngine =
Cc[
"@songbirdnest.com/Songbird/DatabaseEngine;1"]
831 .getService(
Ci.sbIDatabaseEngine);
832 dbEngine.releaseMemory();
836 if (this._fileScanQuery) {
837 this._fileScanQuery.cancel();
838 this._fileScanQuery =
null;
841 if (this._timingService) {
842 this._timingService.stopPerfTimer(this._timingIdentifier);
846 if (this._foundFlaggedExtensions &&
847 this._foundFlaggedExtensions.length > 0)
849 var winMed =
Cc[
"@mozilla.org/appshell/window-mediator;1"]
850 .getService(
Ci.nsIWindowMediator);
851 var sbWin = winMed.getMostRecentWindow(
"Songbird:Main");
853 var prompter =
Cc[
"@songbirdnest.com/Songbird/Prompter;1"]
854 .getService(
Ci.sbIPrompter);
855 prompter.waitForWindow =
false;
857 var dialogBlock =
Cc[
"@mozilla.org/embedcomp/dialogparam;1"]
858 .createInstance(
Ci.nsIDialogParamBlock);
861 dialogBlock.objects = this._foundFlaggedExtensions;
864 prompter.openDialog(sbWin,
865 "chrome://songbird/content/xul/mediaimportWarningDialog.xul",
866 "mediaimportWarningDialog",
867 "chrome,centerscreen,modal=yes",
875 observe:
function DirectoryImportJob_observe(aSubject, aTopic,
aData) {
876 this._onPollFileScan();
894 function DirectoryImportService() {
895 this._importJobs = [];
898 DirectoryImportService.prototype = {
900 classID: Components.ID(
"{6e542f90-44a0-11dd-ae16-0800200c9a66}"),
901 contractID:
"@songbirdnest.com/Songbird/DirectoryImportService;1",
912 import:
function DirectoryImportService_import(aDirectoryArray, aTargetMediaList, aTargetIndex) {
913 return this.importWithCustomSnifferAndMetadataScanner(
914 aDirectoryArray,
null,
null, aTargetMediaList, aTargetIndex);
917 importWithCustomSnifferAndMetadataScanner:
function DirectoryImportService_importWithCustomSniffer(
918 aDirectoryArray, aTypeSniffer, aMetadataScanner, aTargetMediaList, aTargetIndex) {
920 aTypeSniffer =
Cc[
"@songbirdnest.com/Songbird/Mediacore/TypeSniffer;1"]
921 .createInstance(
Ci.sbIMediacoreTypeSniffer);
923 if (!aMetadataScanner) {
924 aMetadataScanner =
Cc[
"@songbirdnest.com/Songbird/FileMetadataService;1"]
925 .getService(
Ci.sbIFileMetadataService);
928 if (!aTargetMediaList) {
933 aDirectoryArray, aTypeSniffer, aMetadataScanner, aTargetMediaList, aTargetIndex,
this);
935 this._importJobs.push(job);
939 if (this._importJobs.length == 1) {
949 onJobComplete:
function DirectoryImportService_onJobComplete() {
950 this._importJobs.shift();
951 if (this._importJobs.length > 0) {
952 this._importJobs[0].begin();
960 return XPCOMUtils.generateModule([DirectoryImportService]);
function DirectoryImportJob(aInputArray, aTypeSniffer, aMetadataScanner, aTargetMediaList, aTargetIndex, aImportService)
sbDeviceFirmwareAutoCheckForUpdate prototype contractID
sbOSDControlService prototype QueryInterface
sbDeviceFirmwareAutoCheckForUpdate prototype classDescription
function SBString(aKey, aDefault, aStringBundle)
SimpleArrayEnumerator prototype hasMoreElements
sbDeviceFirmwareAutoCheckForUpdate prototype classID
Javascript wrappers for common library tasks.
sbDeviceFirmwareAutoCheckForUpdate prototype getInterfaces
sbDeviceFirmwareAutoCheckForUpdate prototype interfaces
_getSelectedPageStyle s i
_updateTextAndScrollDataForFrame aData
sbDeviceFirmwareAutoCheckForUpdate prototype observe