home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Freelog 112
/
FreelogNo112-NovembreDecembre2012.iso
/
Multimedia
/
Songbird
/
Songbird_2.0.0-2311_windows-i686-msvc8.exe
/
components
/
sbMigrate19to110pre0.addMetadataHashIdentity.js
< prev
next >
Wrap
Text File
|
2012-06-08
|
14KB
|
381 lines
/*
*=BEGIN SONGBIRD GPL
*
* This file is part of the Songbird web player.
*
* Copyright(c) 2005-2011 POTI, Inc.
* http://www.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 Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://app/jsmodules/sbLocalDatabaseMigrationUtils.jsm");
Cu.import("resource://app/jsmodules/SBJobUtils.jsm");
Cu.import("resource://app/jsmodules/sbProperties.jsm");
Cu.import("resource://app/jsmodules/GeneratorThread.jsm");
Cu.import("resource://app/jsmodules/sbLibraryUtils.jsm");
const FROM_VERSION = 28;
const TO_VERSION = 29;
function LOG(s) {
dump("----++++----++++sbLibraryMigration " +
FROM_VERSION + " to " + TO_VERSION + ": " +
s +
"\n----++++----++++\n");
}
function sbLibraryMigration()
{
SBLocalDatabaseMigrationUtils.BaseMigrationHandler.call(this);
this._errors = [];
/* We handle each contentType individually, and we keep extra information
* about each in these helper objects */
this._contentMigrations = {
"audio": {
/* The properties used when hashing metadata of an audio mediaitem.
* The order here is important and should match the order used by
* sbIdentityService */
props: [
SBProperties.trackName,
SBProperties.artistName,
SBProperties.albumName,
SBProperties.genre
],
// flag to indicate that audio files have been migrated
completed: false,
},
"video": {
/* The properties used when hashing metadata of a video mediaitem.
* The order here is important and should match the order used by
* sbIdentityService */
props: [
SBProperties.trackName,
SBProperties.artistName,
SBProperties.albumName,
SBProperties.genre
],
// flag to indicate that video files have been migrated
completed: false,
},
}
/* The separator character to put between each property when concatenated
* for hashing. This should match that used by sbIdentityService */
this.separator = '|';
/* Initial values for sbIJobProgress attributes, will be updated when the
* number of hashes that will need to be calculated is known */
this._progress = 0;
this._total = 0;
}
//-----------------------------------------------------------------------------
// sbLocalDatabaseMigration Implementation
//-----------------------------------------------------------------------------
sbLibraryMigration.prototype = {
__proto__: SBLocalDatabaseMigrationUtils.BaseMigrationHandler.prototype,
classDescription: 'Songbird Migration Handler, version ' +
FROM_VERSION + ' to ' + TO_VERSION,
classID: Components.ID("{4eba22e9-d657-4599-a181-b8340852e7a2}"),
contractID: SBLocalDatabaseMigrationUtils.baseHandlerContractID +
FROM_VERSION + 'to' + TO_VERSION,
fromVersion: FROM_VERSION,
toVersion: TO_VERSION,
migrate: function sbLibraryMigration_migrate(aLibrary) {
this._library = aLibrary;
var sip = Cc["@mozilla.org/supports-interface-pointer;1"]
.createInstance(Ci.nsISupportsInterfacePointer);
sip.data = this;
/* We use a generator thread so that we can update the progress dialog
* periodically throughout the migration. That only takes relatively
* infrequent updates and low CPU so give the migration thread more CPU
* and a longer period */
this._thread = new GeneratorThread(this.processItems());
this._thread.maxPctCPU = 95;
this._thread.period = 50;
this._thread.start();
// Show the progress dialog tethered to this job
SBJobUtils.showProgressDialog(sip.data, null, 0);
},
processItems: function sbLibraryMigration_processItems() {
try {
this._databaseGUID = this._library.databaseGuid;
this._databaseLocation = this._library.databaseLocation;
// Set some basics for the dialog and check if we should update the dialog
this._titleText = "Library Migration Helper";
this._statusText = "Preparing to migrate 1.9 database to 1.10 database...";
yield this.checkIfShouldUpdateAndYield();
// first query is for adding our new metadata hash col and index on it
var query = this.createMigrationQuery(this._library);
query.addQuery("alter table media_items add column metadata_hash_identity");
query.addQuery("alter table library_media_item add column metadata_hash_identity");
query.addQuery("create index idx_media_items_metadata_hash_identity" +
"on media_items (metadata_hash_identity)");
query.addQuery("REINDEX");
query.addQuery("ANALYZE");
query.addQuery("COMMIT");
query.setAsyncQuery(true);
query.execute();
/* With hash column added, now we generate a hash for each existing item.
* The properties used for each contentType could be different so we
* handle them separately. */
for (var contentType in this._contentMigrations) {
yield this.hashExistingMediaItems(contentType);
}
}
catch (e) {
dump("Exception occured: " + e);
throw e;
}
},
hashExistingMediaItems: function sbLibraryMigration_hashExistingMediaItems
(contentType) {
/* Make maps of property_name to property_id for the properties used when
* hashing audio and video files. The ids are used to get the property
* values for each mediaitem. */
var propNames = this._contentMigrations[contentType].props;
var propMap = this.getPropertyIDs(propNames);
var idService = Cc["@songbirdnest.com/Songbird/IdentityService;1"]
.getService(Ci.sbIIdentityService);
/* Build a query that will associate each media_item_id to all it's relevant
* properties in a single row so that we can easily access, concatenate, and
* then hash those properties.
* The order of the properties will be the same as in the passed propNames
* and should match the order used in sbIdentityService. As content_type is
* not stored in the resource_properties table but needs to be hashed,
* it is explicitly added in the initialization of selectSQL */
let selectSQL = "SELECT media_items.guid, media_items.content_mime_type";
let fromSQL = "FROM media_items"
let joinSQL = "";
let whereSQL = "WHERE content_mime_type = '" + contentType + "'" +
" AND media_items.is_list = '0'";
for (var i = 0; i < propNames.length; i++) {
// Check if it's time for us to yield, and update the dialog if so
yield this.checkIfShouldUpdateAndYield();
var propID = propMap[propNames[i]];
var resourcePropAlias = 'rp' + i;
selectSQL += ", " + resourcePropAlias + ".obj"; // rp_.obj
joinSQL += " LEFT OUTER JOIN resource_properties as " + resourcePropAlias +
" ON media_items.media_item_id = " +
resourcePropAlias + ".media_item_id" +
" AND " + resourcePropAlias + ".property_id = " + propID;
}
// When finished the query will look like:
/* SELECT media_items.guid, media_items.content_mime_type,
* rp0.obj, rp1.obj... */
/* FROM media_items */
/* LEFT OUTER JOIN resource_properties as rp0
* ON media_items.media_item_id = rp0.media_item_id
* AND rp0.property_id = <property_id>
* LEFT OUTER JOIN resource_properties as rp1
* ON media_items.media_item_id = rp1.media_item_id
* AND rp1.property_id = <property_id> */
/* WHERE content_mime_type = '<contentType>'"
* AND media_items.is_list = '0' */
let sql = selectSQL + " " +
fromSQL + " " +
joinSQL + " " +
whereSQL;
var selectPropertiesQuery = this.createMigrationQuery(this._library);
selectPropertiesQuery.addQuery(sql);
// execute the query to retrieve the property data
var retval;
selectPropertiesQuery.execute(retval);
/* The updateQuery will push all of the identities to the database.
* We add an update statement to updateQuery for each mediaitem as the
* identity for that mediaitem is calculate.
* All of the update statements are of a similar form, so we use the
* preparedUpdateStatement below. */
var updateQuery = this.createMigrationQuery(this._library);
updateQuery.addQuery("BEGIN");
var preparedUpdateStatement = updateQuery.prepareQuery
("UPDATE media_items SET metadata_hash_identity = ? WHERE guid = ?");
/* Our last query retrieved the property data for each mediaitem, so we'll
* walk through that to form our string that will be hashed */
var propertyResultSet = selectPropertiesQuery.getResultObject();
var colCount = propertyResultSet.getColumnCount();
var rowCount = propertyResultSet.getRowCount();
/* We know how many identities we will need to calculate, so make that the
* migration total. The dialog will be updated the next time we are
* told to yield in the following identity-calculation loop. */
this._total = rowCount;
this._statusText = "Migrating " + contentType + " files in 1.9 database to 1.10 database...";
var idService = Cc["@songbirdnest.com/Songbird/IdentityService;1"]
.getService(Ci.sbIIdentityService);
for(let currentRow = 0; currentRow < rowCount; currentRow++) {
// Check if it's time for us to yield, and update the dialog if so
yield this.checkIfShouldUpdateAndYield();
var guid = propertyResultSet.getRowCell(currentRow, 0);
var hasHashableMetadata = false;
var propsToHash = [];
// Form the string that will be hashed to form the identity
for (let currentCol = 1; currentCol < colCount; currentCol++) {
let propVal = propertyResultSet.getRowCell(currentRow, currentCol);
if (propVal) {
propsToHash.push(propVal);
hasHashableMetadata = true;
}
else {
propsToHash.push("");
}
}
/* If there was hashable metadata, calculate the identity now and add it
* to the update query */
if (hasHashableMetadata) {
var stringToHash = propsToHash.join(this.separator);
var identity = idService.hashString(stringToHash);
updateQuery.addPreparedStatement(preparedUpdateStatement);
updateQuery.bindStringParameter(0, identity);
updateQuery.bindStringParameter(1, guid);
}
// This lets the dialog know that another item's identity was calculated
this._progress++;
}
updateQuery.addQuery("commit");
updateQuery.execute(retval);
/* We have finished handling this contentType. Mark this type as completed
* and check if all contentTypes have been completed. If no contentType
* still needs to be handled, mark the job as completed successfully and
* inform the dialog */
this._contentMigrations[contentType].completed = true;
for (var contentType in this._contentMigrations) {
if (!this._contentMigrations[contentType].completed) {
return;
}
}
this._status = Ci.sbIJobProgress.STATUS_SUCCEEDED;
this.notifyJobProgressListeners();
},
/* Takes an array of propertyNames and returns a map of those property_names
* to their corresponding property_id in the db */
getPropertyIDs: function sbLibraryMigration_getPropertyIDs(propertyNames) {
var propMap = {};
var sql = "SELECT property_name, property_id FROM properties WHERE " +
"property_name = '";
sql += propertyNames.join("' OR property_name = '");
sql += "'";
// When finished the query will look like:
/* SELECT property_name, property_id
* FROM properties
* WHERE property_name = '<propertyNames[0]>'
* OR property_name = '<propetyNames[1]>'... */
var query = this.createMigrationQuery(this._library);
query.addQuery(sql);
var retval;
query.execute(retval);
var resultSet = query.getResultObject();
/* The rows of the result are of the form | property_name | property_id |
* for each of the property names that we were passed and could find.
* Fill the propMap with the appropriate
* property_name => property_id mappings. */
var propertyIDs = [];
var rowCount = resultSet.getRowCount();
for(let currentRow = 0; currentRow < rowCount; ++currentRow) {
propName = resultSet.getRowCell(currentRow, 0);
propId = resultSet.getRowCell(currentRow, 1);
propMap[propName] = propId;
}
return propMap;
},
/* This utility method checks if it is time to yield, and if it is, we
* notify the dialog so that it will pick up the updated progress
* and then we yield so the dialog can update itself. */
checkIfShouldUpdateAndYield: function
sbLibraryMigration_checkIfShouldUpdateAndYield() {
/* GeneratorThread handles controlling when we should yield, but we need to
* check it to know if that time has come. */
if (GeneratorThread.shouldYield()) {
this.notifyJobProgressListeners();
yield;
}
},
/* We override these methods to report the current state in the
* progress dialog. */
get status() {
return this._status;
},
get progress() {
return this._progress;
},
get total() {
return this._total;
},
};
//-----------------------------------------------------------------------------
// Module
//-----------------------------------------------------------------------------
function NSGetModule(compMgr, fileSpec) {
return XPCOMUtils.generateModule([
sbLibraryMigration
]);
}