home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Freelog 112
/
FreelogNo112-NovembreDecembre2012.iso
/
Multimedia
/
Songbird
/
Songbird_2.0.0-2311_windows-i686-msvc8.exe
/
components
/
sbDirectoryImportService.js
< prev
next >
Wrap
Text File
|
2012-06-08
|
34KB
|
940 lines
/*
//
// BEGIN SONGBIRD GPL
//
// This file is part of the Songbird web player.
//
// Copyright(c) 2005-2008 POTI, Inc.
// http://songbirdnest.com
//
// This file may be licensed under the terms of of the
// GNU General Public License Version 2 (the "GPL").
//
// Software distributed under the License is distributed
// on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
// express or implied. See the GPL for the specific language
// governing rights and limitations.
//
// You should have received a copy of the GPL along with this
// program. If not, go to http://www.gnu.org/licenses/gpl.html
// or write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// END SONGBIRD GPL
//
*/
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Ce = Components.Exception;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://app/jsmodules/SBJobUtils.jsm");
Cu.import("resource://app/jsmodules/ArrayConverter.jsm");
Cu.import("resource://app/jsmodules/sbProperties.jsm");
Cu.import("resource://app/jsmodules/sbLibraryUtils.jsm");
Cu.import("resource://app/jsmodules/StringUtils.jsm");
Cu.import("resource://app/jsmodules/DebugUtils.jsm");
/**
* To log this module, set the following environment variable:
* NSPR_LOG_MODULES=sbDirectoryImportJob:5
*/
const LOG = DebugUtils.generateLogFunction("sbDirectoryImportJob");
// used to identify directory import profiling runs
var gCounter = 0;
/******************************************************************************
* Object implementing sbIDirectoryImportJob, responsible for finding media
* items on disk, adding them to the library and performing a metadata scan.
*
* Call begin() to start the job.
*****************************************************************************/
function DirectoryImportJob(aInputArray,
aTypeSniffer,
aMetadataScanner,
aTargetMediaList,
aTargetIndex,
aImportService) {
if (!(aInputArray instanceof Ci.nsIArray &&
aInputArray.length > 0) ||
!(aTargetMediaList instanceof Ci.sbIMediaList)) {
throw Components.results.NS_ERROR_INVALID_ARG;
}
// Call super constructor
SBJobUtils.JobBase.call(this);
this._inputFiles = ArrayConverter.JSArray(aInputArray);
this.targetMediaList = aTargetMediaList;
this.targetIndex = aTargetIndex;
this._importService = aImportService;
this._typeSniffer = aTypeSniffer;
this._metadataScanner = aMetadataScanner;
// TODO these strings probably need updating
this._titleText = SBString("media_scan.scanning");
this._statusText = SBString("media_scan.adding");
// Initially cancelable
this._canCancel = true;
// initialize with an empty array
this._itemURIStrings = [];
this._libraryUtils = Cc["@songbirdnest.com/Songbird/library/Manager;1"]
.getService(Ci.sbILibraryUtils);
if ("@songbirdnest.com/Songbird/TimingService;1" in Cc) {
this._timingService = Cc["@songbirdnest.com/Songbird/TimingService;1"]
.getService(Ci.sbITimingService);
this._timingIdentifier = "DirImport" + gCounter++;
}
}
DirectoryImportJob.prototype = {
__proto__: SBJobUtils.JobBase.prototype,
QueryInterface: XPCOMUtils.generateQI(
[Ci.sbIDirectoryImportJob, Ci.sbIJobProgress, Ci.sbIJobProgressUI,
Ci.sbIJobProgressListener, Ci.sbIJobCancelable, Ci.nsIObserver,
Ci.nsIClassInfo]),
/** For nsIClassInfo **/
getInterfaces: function(count) {
var interfaces = [Ci.sbIDirectoryImportJob, Ci.sbIJobProgress, Ci.sbIJobProgressListener,
Ci.sbIJobCancelable, Ci.nsIClassInfo, Ci.nsISupports];
count.value = interfaces.length;
return interfaces;
},
totalAddedToMediaList : 0,
totalAddedToLibrary : 0,
totalDuplicates : 0,
targetMediaList : null,
targetIndex : null,
// nsITimer used to poll the filescanner. *sigh*
_pollingTimer : null,
FILESCAN_POLL_INTERVAL : 33,
// sbIFileScan used to find media files in directories
_fileScanner : null,
_fileScanQuery : null,
// Array of nsIFile directory paths or files
_inputFiles : null,
_fileExtensions : null,
_flaggedFileExtensions : null,
_foundFlaggedExtensions : null,
// The sbIDirectoryImportService. Called back on job completion.
_importService : null,
_typeSniffer : null,
_metadataScanner : null,
// JS Array of URI strings for all found media items
_itemURIStrings : [],
// Optional JS array of items found to already exist in the main library
_itemsInMainLib : [],
// Rather than create all the items in one pass, then scan them all,
// we want to create, read, repeat with small batches. This avoids
// wasting/fragmenting memory when importing 10,000+ tracks.
// TODO tweak
BATCHCREATE_SIZE : 300,
// Index into _itemURIStrings for the beginning of the
// next create/read/add batch
_nextURIIndex : 0,
// Temporary nsIArray of previously unknown sbIMediaItems,
// used to pass newly created items to a metadata scan job.
// Set in _onItemCreation.
_currentMediaItems : null,
// The size of the current batch
_currentBatchSize : 0,
// True if we've forced the library into a batch state for
// performance reasons
_inLibraryBatch : false,
// sbILibraryUtils used to produce content URI's
_libraryUtils : null,
// Used to track performance
_timingService : null,
_timingIdentifier : null,
/**
* Get an enumerator over all media items found in this job.
* Will return an empty enumerator until the batch creation
* phase is complete. Best called when the entire job is done.
*/
enumerateAllItems: function DirectoryImportJob_enumerateAllItems() {
// Ultimately the batch create job would give us this list,
// but at the moment we have to recreate it ourselves
// using the list of URIs from the file scan.
// If no URIs, just return the empty enumerator
if (this._itemURIStrings.length == 0) {
return ArrayConverter.enumerator([]);
}
// If all the URIs resulted in new items, and
// were processed in a single batch, then we can
// just return the current items.
if (this._currentMediaItems &&
this._itemURIStrings.length == this._currentMediaItems.length) {
return this._currentMediaItems.enumerate();
}
// Otherwise, we'll need to get media items for all the
// URIs that have been added. We want to avoid instantiating
// all the items at once (since there may be hundreds of thousands),
// so instead get a few at a time.
var uriEnumerator = ArrayConverter.enumerator(this._itemURIStrings);
var library = this.targetMediaList.library;
const BATCHSIZE = this.BATCHCREATE_SIZE;
// This enumerator instantiates the new media items on demand.
var enumerator = {
mediaItems: [],
// sbIMediaListEnumerationListener, used to fetch items
onEnumerationBegin: function() {},
onEnumeratedItem: function(list, item) {
this.mediaItems.push(item);
},
onEnumerationEnd: function() {},
// nsISimpleEnumerator, used to dispense items
hasMoreElements: function() {
return (this.mediaItems.length ||
uriEnumerator.hasMoreElements());
},
getNext: function() {
// When the buffer runs out, fetch more items from the library
if (this.mediaItems.length == 0) {
// Get the next set of URIs
var propertyArray = SBProperties.createArray();
var counter = 0;
while (uriEnumerator.hasMoreElements() && counter < BATCHSIZE) {
var itemURIStr = uriEnumerator.getNext().QueryInterface(Ci.nsISupportsString);
propertyArray.appendProperty(SBProperties.contentURL, itemURIStr.data);
counter++;
}
library.enumerateItemsByProperties(propertyArray, this);
}
return this.mediaItems.shift();
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsISimpleEnumerator, Ci.sbIMediaListEnumerationListener]),
};
return enumerator;
},
/**
* Begin the import job
*/
begin: function DirectoryImportJob_begin() {
if (this._timingService) {
this._timingService.startPerfTimer(this._timingIdentifier);
}
// Start by finding all the files in the given directories
var Application = Cc["@mozilla.org/fuel/application;1"]
.getService(Ci.fuelIApplication);
this._fileScanner = Cc["@songbirdnest.com/Songbird/FileScan;1"]
.createInstance(Components.interfaces.sbIFileScan);
// Figure out what files we are looking for.
try {
var extensions = this._typeSniffer.mediaFileExtensions;
if (!Application.prefs.getValue("songbird.mediascan.enableVideoImporting", true)) {
// disable video, so scan only audio - see bug 13173
extensions = this._typeSniffer.audioFileExtensions;
}
this._fileExtensions = [];
while (extensions.hasMore()) {
this._fileExtensions.push(extensions.getNext());
}
} catch (e) {
dump("WARNING: DirectoryImportJob_begin could not find supported file extensions. " +
"Assuming test mode, and using a hardcoded list.\n");
this._fileExtensions = ["mp3", "ogg", "flac"];
}
// Add the unsupported file extensions as flagged extensions to the file
// scanner so that the user can be notified if any unsupported extensions
// were discovered.
//
// NOTE: if the user choose to ignore import warnings, do not bother adding
// the filter list here.
var shouldWarnFlagExtensions = Application.prefs.getValue(
"songbird.mediaimport.warn_filtered_exts", true);
if (shouldWarnFlagExtensions) {
this._foundFlaggedExtensions =
Cc["@songbirdnest.com/moz/xpcom/threadsafe-array;1"]
.createInstance(Ci.nsIMutableArray);
this._flaggedFileExtensions = [];
try {
var unsupportedExtensions = this._typeSniffer.unsupportedVideoFileExtensions;
while (unsupportedExtensions.hasMore()) {
var item = unsupportedExtensions.getNext();
this._flaggedFileExtensions.push(item);
}
}
catch (e) {
Components.utils.reportError(
e + "\nCould not add unsupported file extensions to the file scan!");
}
}
// XXX If possible, wrap the entire operation in an update batch
// so that onbatchend listeners dont go to work between the
// end of the batchcreate and the start of the metadata scan.
// This is an ugly hack, but it prevents a few second hang
// when importing 100k tracks.
var library = this.targetMediaList.library;
if (library instanceof Ci.sbILocalDatabaseLibrary) {
this._inLibraryBatch = true;
library.forceBeginUpdateBatch();
}
this._startNextDirectoryScan();
// Now poll the file scan, since it apparently perf is much better this way
this._pollingTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._pollingTimer.init(this, this.FILESCAN_POLL_INTERVAL, Ci.nsITimer.TYPE_REPEATING_SLACK);
},
/**
* Kick off a FileScanQuery for the next directory in _inputFiles.
* Assumes there are directories left to scan.
*/
_startNextDirectoryScan: function DirectoryImportJob__startNextDirectoryScan() {
// Just report the error rather then throwing, since this method
// is called by the timer.
if (this._fileScanQuery && this._fileScanQuery.isScanning()) {
Cu.reportError(
"DirectoryImportJob__startDirectoryScan called with a scan in progress?");
return;
}
var file = this._inputFiles.shift(); // Process directories in the order provided
if (file && file instanceof Ci.nsIFileURL) {
file = file.file;
}
// If something is messed up, just report and wait. The poll function will
// move on to the next step.
if (!file || !(file instanceof Ci.nsIFile)) {
Cu.reportError("DirectoryImportJob__startNextDirectoryScan: invalid directory");
return;
}
// We use the same _fileScanQuery object for scanning multiple directories.
if (!this._fileScanQuery) {
this._fileScanQuery = Cc["@songbirdnest.com/Songbird/FileScanQuery;1"]
.createInstance(Components.interfaces.sbIFileScanQuery);
var fileScanQuery = this._fileScanQuery;
this._fileExtensions.forEach(function(ext) { fileScanQuery.addFileExtension(ext) });
// Assign the unsupported file extensions as flagged extensions in the scanner.
if (this._flaggedFileExtensions) {
this._flaggedFileExtensions.forEach(function(ext) {
fileScanQuery.addFlaggedFileExtension(ext);
});
}
}
if (file.exists() && file.isDirectory()) {
this._fileScanQuery.setDirectory(file.path);
this._fileScanQuery.setRecurse(true);
this._fileScanner.submitQuery(this._fileScanQuery);
} else {
var urispec = this._libraryUtils.getFileContentURI(file).spec;
// Ensure that the file extension is not blacklisted.
var isValidExt = false;
var dotIndex = urispec.lastIndexOf(".");
if (dotIndex > -1) {
var fileExt = urispec.substr(dotIndex + 1);
for (var i = 0; i < this._fileExtensions.length; i++) {
if (this._fileExtensions[i] == fileExt) {
isValidExt = true;
break;
}
}
}
if (isValidExt) {
var supportsString = Cc["@mozilla.org/supports-string;1"]
.createInstance(Ci.nsISupportsString);
supportsString.data = urispec;
this._itemURIStrings.push(supportsString);
}
}
},
/**
* Called by _pollingTimer. Checks on the current fileScanQuery,
* reporting progress and checking for completion.
*/
_onPollFileScan: function DirectoryImportJob__onPollFileScan() {
// If the current query is finished, collect the results and move on to
// the next directory
if (!this._fileScanQuery.isScanning()) {
// If there are more directories to scan, start the next one
if (this._inputFiles.length > 0) {
this._startNextDirectoryScan();
} else {
// Otherwise, we're done scanning directories, and it is time to collect
// information and create media items
var fileCount = this._fileScanQuery.getFileCount();
if (fileCount > 0 ) {
var strings = this._fileScanQuery.getResultRangeAsURIStrings(0, fileCount - 1);
Array.prototype.push.apply(this._itemURIStrings, ArrayConverter.JSArray(strings));
}
this._finishFileScan();
this._startMediaItemCreation();
}
// If the file scan query is still running just update the UI
} else {
var text = this._fileScanQuery.getLastFileFound();
text = decodeURIComponent(text.split("/").pop());
if (text.length > 60) {
text = text.substring(0, 10) + "..." + text.substring(text.length - 40);
}
this._statusText = text;
this.notifyJobProgressListeners();
}
},
/**
* Called when the file scanner finishes with the target directories.
* Responsible for shutting down the scanner.
*/
_finishFileScan: function DirectoryImportJob__finishFileScan() {
if (this._fileScanQuery.flaggedExtensionsFound) {
var ioService = Cc["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
// Add the leaf name of any flagged file paths to show the user at the
// end of the import. See bug 19553.
var flaggedExtCount = this._fileScanQuery.getFlaggedFileCount();
for (var i = 0; i < flaggedExtCount; i++) {
var curFlaggedPath = this._fileScanQuery.getFlaggedFilePath(i);
var curFlaggedURL = ioService.newURI(curFlaggedPath, null, null);
if (curFlaggedURL instanceof Ci.nsIFileURL) {
var curFlaggedFile = curFlaggedURL.QueryInterface(Ci.nsIFileURL).file;
var curSupportsStr = Cc["@mozilla.org/supports-string;1"]
.createInstance(Ci.nsISupportsString);
curSupportsStr.data = curFlaggedFile.leafName;
this._foundFlaggedExtensions.appendElement(curSupportsStr, false);
}
}
}
if (this._fileScanner) {
this._fileScanner.finalize();
this._fileScanner = null;
}
if (this._pollingTimer) {
this._pollingTimer.cancel();
this._pollingTimer = null;
}
// Set total number of items to process
this._total = this._itemURIStrings.length;
},
/**
* This filters out items that are either duplicates or already exits in the
* main library. The existing items in the main library are put in
* aItemsInMainLib array. The remaining items are returned as an array.
* The duplicate check only involves the origin URL as the
* batchCreateMediaItemsAsync will perform that check.
*
* \param aURIs the list of URI's to filter
* \param aItemsInMainLib
*/
_filterItems: function(aURIs, aItemsInMainLib) {
// If we're copying to another library we need to look in the main library
// for items that might have the origin URL the same as our URL's.
var targetLib = this.targetMediaList.library;
var mainLib = LibraryUtils.mainLibrary;
var isMainLib = this.targetMediaList.library.equals(mainLib);
/**
* This function is used by the JS Array filter method to filter
* out duplicates. When a matching item is found the main library
* the item is added to the aItemsInMainLib array and removed
* from the array filterDupes is operating on. When the item
* is found in the target library it is just removed from the array
* and the dupe count incremented
*/
function filterDupes(uri)
{
var uriObj = uri.QueryInterface(Ci.nsISupportsString);
var uriSpec = uriObj.data;
LOG("Searching for URI: " + uriSpec);
// If we're importing to a non-main library the see if it exists in
// the main library
if (!isMainLib) {
var items = [];
try {
// If we find it in the main library then save the item off and
// filter it out of the array
items = ArrayConverter.JSArray(
mainLib.getItemsByProperty(SBProperties.contentURL,
uriSpec));
}
catch (e) {
LOG("Exception: " + e);
// Exception expected if nothing is found. Just continue
}
if (items.length > 0) {
LOG(" Found in main library");
aItemsInMainLib.push(items[0].QueryInterface(Ci.sbIMediaItem));
return false;
}
}
var items = [];
try
{
// If we find the item by origin URL in the target library then
// bump the dupe count and filter it out of the array
items = ArrayConverter.JSArray(
targetLib.getItemsByProperty(SBProperties.originURL,
uriSpec));
}
catch (e) {
LOG("Exception: " + e);
}
if (items.length > 0) {
LOG(" Found in target library");
++this.totalDuplicates;
return false;
}
LOG(" Item needs to be created");
// Item wasn't found so leave it in the array
return true;
}
return aURIs.filter(filterDupes);
},
/**
* Begin creating sbIMediaItems for a batch of found media URIs.
* Does BATCHCREATE_SIZE at a time, looping back after
* onJobDelegateCompleted.
*/
_startMediaItemCreation:
function DirectoryImportJob__startMediaItemCreation() {
if (!this._fileScanQuery) {
Cu.reportError(
"DirectoryImportJob__startMediaItemCreation called with invalid state");
this.complete();
return;
}
var targetLib = this.targetMediaList.library;
if (this._nextURIIndex >= this._itemURIStrings.length &&
this._itemsInMainLib.length === 0) {
LOG("Finish creating and adding all items");
this.complete();
return;
}
// For the items we found in the main library add them to the target library
else if (this._itemsInMainLib.length) {
LOG("Finish creating all items, now adding items");
// Setup listener object so we can mark the items as
var self = this;
var addSomeListener = {
onProgress: function(aItemsProcessed, aCompleted) {},
onItemAdded: function(aMediaItem) {
aMediaItem.setProperty(SBProperties.originIsInMainLibrary, "1");
},
onComplete: function() {
LOG("Adding items completed");
self._itemsInMainLib = [];
self.complete();
}
}
// Now process items we found in the main library
targetLib.addMediaItems(ArrayConverter.enumerator(this._itemsInMainLib),
addSomeListener,
true);
return;
}
// Update status
this._statusText = SBString("media_scan.adding");
this.notifyJobProgressListeners();
// Process the URIs a slice at a time, since creating all
// of them at once may require a very large amount of memory.
this._currentBatchSize = Math.min(this.BATCHCREATE_SIZE,
this._itemURIStrings.length - this._nextURIIndex);
var endIndex = this._nextURIIndex + this._currentBatchSize;
var uris = this._itemURIStrings.slice(this._nextURIIndex, endIndex);
this._nextURIIndex = endIndex;
LOG("Creating media items");
this._itemsInMainLib = [];
LOG("Total items=" + uris.length);
uris = this._filterItems(uris, this._itemsInMainLib);
LOG("Items in main library=" + this._itemsInMainLib.length);
LOG("Items needing to be created=" + uris.length);
// Bug 10228 - this needs to be replaced with an sbIJobProgress interface
var thisJob = this;
var batchCreateListener = {
onProgress: function(aIndex) {},
onComplete: function(aMediaItems, aResult) {
LOG("Finished creating batch of items");
thisJob._onItemCreation(aMediaItems, aResult, uris.length);
}
};
// Create items that weren't in the main library or already in the target
// library
targetLib.batchCreateMediaItemsAsync(batchCreateListener,
ArrayConverter.nsIArray(uris),
null,
false);
},
/**
* Called by sbILibrary.batchCreateMediaItemsAsync.
* BatchCreateMediaItemsAsync needs to be updated to actually send progress.
* At the moment it only notifies when the process is over.
*/
_onItemCreation: function DirectoryImportJob__onItemCreation(aMediaItems,
aResult,
itemsToCreateCount) {
// Get the completed item array. Don't use the given item array on error.
// Use an empty one instead.
LOG("batchCreateMediaItemsAsync created " + aMediaItems.length)
if (Components.isSuccessCode(aResult)) {
this.totalDuplicates += (itemsToCreateCount - aMediaItems.length);
this._currentMediaItems = aMediaItems;
} else {
Cu.reportError("DirectoryImportJob__onItemCreation: aResult == " + aResult);
this._currentMediaItems = Components.classes["@songbirdnest.com/moz/xpcom/threadsafe-array;1"]
.createInstance(Components.interfaces.nsIArray);
}
this.totalAddedToLibrary += this._currentMediaItems.length;
if (this._currentMediaItems.length > 0) {
// Make sure we have metadata for all the added items
this._startMetadataScan();
} else {
// no items were created, probably because they were all old.
// try the next batch.
this._startMediaItemCreation();
}
},
/**
* Begin finding the metadata for the current set of media items.
* Forward sbIJobProgress through to the metadata job
*/
_startMetadataScan:
function DirectoryImportJob__startMetadataScan() {
if (this._currentMediaItems && this._currentMediaItems.length > 0) {
var metadataJob = this._metadataScanner.read(this._currentMediaItems);
// Pump metadata job progress to the UI
this.delegateJobProgress(metadataJob);
} else {
// Nothing to do.
this.complete();
}
},
/**
* Insert found media items into the specified target location
*/
_insertFoundItemsIntoTarget:
function DirectoryImportJob__insertFoundItemsIntoTarget() {
// If we are inserting into a list, there is more to do than just importing
// the tracks into its library, we also need to insert all the items (even
// the ones that previously existed) into the list at the requested position
if (!(this.targetMediaList instanceof Ci.sbILibrary)) {
try {
if (this._itemURIStrings.length > 0) {
var originalLength = this.targetMediaList.length;
// If we need to insert, then do so
if ((this.targetMediaList instanceof Ci.sbIOrderableMediaList) &&
(this.targetIndex >= 0) &&
(this.targetIndex < this.targetMediaList.length)) {
this.targetMediaList.insertSomeBefore(this.targetIndex, this.enumerateAllItems());
} else {
// Otherwise, just add
this.targetMediaList.addSome(this.enumerateAllItems());
}
this.totalAddedToMediaList = this.targetMediaList.length - originalLength;
}
} catch (e) {
Cu.reportError(e);
}
}
},
/**
* Called when a sub-job (set via Job.delegateJobProgress) completes.
*/
onJobDelegateCompleted: function DirectoryImportJob_onJobDelegateCompleted() {
// Track overall progress, so that the progress bar reflects
// the total number of items to process, not the individual
// batches.
this._progress += this._jobProgressDelegate.progress;
// Stop delegating
this.delegateJobProgress(null);
// For now the only job we delegate to is metadata... so when
// it completes we go back to create the next set of media items.
this._startMediaItemCreation();
},
/**
* Override JOBBase.progress, to make sure the metadata scan progress
* bar reflects the total item count, not the current batch
*/
get progress() {
return (this._jobProgressDelegate) ?
this._jobProgressDelegate.progress + this._progress : this._progress;
},
get total() {
return this._total;
},
/**
* Override JOBBase.titleText, since we want to maintain
* the same title throughout the import process
*/
get titleText() {
return this._titleText;
},
/** sbIJobCancelable **/
cancel: function DirectoryImportJob_cancel() {
if (!this.canCancel) {
throw new Error("DirectoryImportJob not currently cancelable")
}
if (this._fileScanner) {
this._finishFileScan();
this.complete();
} else if (this._jobProgressDelegate) {
// Cancelling the sub-job will trigger onJobDelegateCompleted
this._jobProgressDelegate.cancel();
}
if (this._fileScanQuery) {
this._fileScanQuery.cancel();
this._fileScanQuery = null;
}
// Remove anything that we've only partially processed
if (this._currentMediaItems && this._currentMediaItems.length > 0) {
this.targetMediaList.library.removeSome(this._currentMediaItems.enumerate());
this.totalAddedToMediaList = 0;
this.totalAddedToLibrary = 0;
this.totalDuplicates = 0;
}
},
/**
* Stop everything, complete the job.
*/
complete: function DirectoryImportJob_complete() {
// Handle inserting into a media list at a specific index
this._insertFoundItemsIntoTarget();
this._status = Ci.sbIJobProgress.STATUS_SUCCEEDED;
this._statusText = SBString("media_scan.complete");
this.notifyJobProgressListeners();
this._importService.onJobComplete();
// XXX If we forced the library into a batch mode
// in order to improve performance, make sure
// we end the batch (if we fail to do this
// the tree view will never update)
var library = this.targetMediaList.library;
if (library instanceof Ci.sbILocalDatabaseLibrary &&
this._inLibraryBatch)
{
this._inLibraryBatch = false;
library.forceEndUpdateBatch();
// XXX Performance hack
// If we've imported a ton of items then most of the
// library database is probably in memory.
// This isn't useful, and makes a bad first impression,
// so lets just dump the entire DB cache.
if (this.totalAddedToLibrary > this.BATCHCREATE_SIZE) {
// More performance hackery. We run optimize with the ANALYZE step.
// This will ensure that all queries can use the best possible indexes.
library.optimize(true);
// Analyze will load a bunch of stuff into memory so we want to release
// after analyze completes.
var dbEngine = Cc["@songbirdnest.com/Songbird/DatabaseEngine;1"]
.getService(Ci.sbIDatabaseEngine);
dbEngine.releaseMemory();
}
}
if (this._fileScanQuery) {
this._fileScanQuery.cancel();
this._fileScanQuery = null;
}
if (this._timingService) {
this._timingService.stopPerfTimer(this._timingIdentifier);
}
// If flagged extensions were found, show the dialog.
if (this._foundFlaggedExtensions &&
this._foundFlaggedExtensions.length > 0)
{
var winMed = Cc["@mozilla.org/appshell/window-mediator;1"]
.getService(Ci.nsIWindowMediator);
var sbWin = winMed.getMostRecentWindow("Songbird:Main");
var prompter = Cc["@songbirdnest.com/Songbird/Prompter;1"]
.getService(Ci.sbIPrompter);
prompter.waitForWindow = false;
var dialogBlock = Cc["@mozilla.org/embedcomp/dialogparam;1"]
.createInstance(Ci.nsIDialogParamBlock);
// Assign the flagged files
dialogBlock.objects = this._foundFlaggedExtensions;
// Now open the dialog.
prompter.openDialog(sbWin,
"chrome://songbird/content/xul/mediaimportWarningDialog.xul",
"mediaimportWarningDialog",
"chrome,centerscreen,modal=yes",
dialogBlock);
}
},
/**
* nsIObserver, for nsITimer
*/
observe: function DirectoryImportJob_observe(aSubject, aTopic, aData) {
this._onPollFileScan();
},
/**
* sbIJobProgressUI
*/
crop: "center"
}
/******************************************************************************
* Object implementing sbIDirectoryImportService. Used to start a
* new media import job.
*****************************************************************************/
function DirectoryImportService() {
this._importJobs = [];
}
DirectoryImportService.prototype = {
classDescription: "Songbird Directory Import Service",
classID: Components.ID("{6e542f90-44a0-11dd-ae16-0800200c9a66}"),
contractID: "@songbirdnest.com/Songbird/DirectoryImportService;1",
QueryInterface: XPCOMUtils.generateQI([Ci.sbIDirectoryImportService]),
// List of pending jobs. We only want to allow one to run at a time,
// since this can lock up the UI.
_importJobs: null,
/**
* \brief Import any media files found in the given directories to
* the specified media list.
*/
import: function DirectoryImportService_import(aDirectoryArray, aTargetMediaList, aTargetIndex) {
return this.importWithCustomSnifferAndMetadataScanner(
aDirectoryArray, null, null, aTargetMediaList, aTargetIndex);
},
importWithCustomSnifferAndMetadataScanner: function DirectoryImportService_importWithCustomSniffer(
aDirectoryArray, aTypeSniffer, aMetadataScanner, aTargetMediaList, aTargetIndex) {
if (!aTypeSniffer) {
aTypeSniffer = Cc["@songbirdnest.com/Songbird/Mediacore/TypeSniffer;1"]
.createInstance(Ci.sbIMediacoreTypeSniffer);
}
if (!aMetadataScanner) {
aMetadataScanner = Cc["@songbirdnest.com/Songbird/FileMetadataService;1"]
.getService(Ci.sbIFileMetadataService);
}
// Default to main library if not target is provided
if (!aTargetMediaList) {
aTargetMediaList = LibraryUtils.mainLibrary;
}
var job = new DirectoryImportJob(
aDirectoryArray, aTypeSniffer, aMetadataScanner, aTargetMediaList, aTargetIndex, this);
this._importJobs.push(job);
// If this is the only job, just start immediately.
// Otherwise it will be started when the current job finishes.
if (this._importJobs.length == 1) {
job.begin();
}
return job;
},
/**
* \brief Called by the active import job when it completes
*/
onJobComplete: function DirectoryImportService_onJobComplete() {
this._importJobs.shift();
if (this._importJobs.length > 0) {
this._importJobs[0].begin();
}
}
} // DirectoryImportService.prototype
function NSGetModule(compMgr, fileSpec) {
return XPCOMUtils.generateModule([DirectoryImportService]);
}