sbMigrate18to19pre0.fileURL.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 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
26 Components.utils.import("resource://app/jsmodules/SBJobUtils.jsm");
27 Components.utils.import("resource://app/jsmodules/sbLocalDatabaseMigrationUtils.jsm");
28 Components.utils.import("resource://app/jsmodules/sbProperties.jsm");
29 
30 const Cc = Components.classes;
31 const Ci = Components.interfaces;
32 const Cr = Components.results;
33 
34 const FROM_VERSION = 24;
35 const TO_VERSION = 25;
36 
37 function LOG(s) {
38  dump("----++++----++++sbLibraryMigration " +
39  FROM_VERSION + " to " + TO_VERSION + ": " +
40  s +
41  "\n----++++----++++\n");
42 }
43 
45 {
46  SBLocalDatabaseMigrationUtils.BaseMigrationHandler.call(this);
47  this._errors = [];
48 }
49 
50 //-----------------------------------------------------------------------------
51 //
52 // sbLocalDatabaseMigration Implementation
53 //
54 // Mac OS/X stores file paths using unicode normalization form D (NFD).
55 // However, when creating an nsIFile in xulrunner 1.9.0 using
56 // nsIFile.initWithPath, the nsIFile keeps the path in unicode normalization
57 // form C (NFC). When such an nsIFile is converted to a local file URL, the URL
58 // encoding is NFC.
59 // xulrunner 1.9.2 changed nsIFile so that the file path is maintained using
60 // the OS format, NFD on Mac OS/X. In addition, in both xulrunner 1.9.0 and
61 // 1.9.2, enumerating files with nsIFile.directoryEntries produces file paths
62 // in the OS format, NFD on Mac OS/X.
63 // Thus, in Songbird 1.8.0 and earlier, importing a playlist file (e.g., M3U)
64 // containing local files results in media items with content URLs in NFC format
65 // since the playlist importers use nsIFile.initWithPath for local files.
66 // However, importing via directory scan or importing playlists with Songbird
67 // 1.9.0 and later results in media items with local file content URLs in NFD
68 // format on Mac OS/X.
69 // This migration fixes local file URLs by converting them to nsIFiles and
70 // back to URLs. Doing so ensures that they're encoded in the native OS format
71 // (NFD on Mac OS/X). The migration could directly do an NFC to NFD conversion,
72 // but converting them via nsIFile should better ensure that the final result is
73 // correct in case other conversions are required.
74 // Since the content URL is copied to the origin URL when media items are
75 // copied to a device, the origin URL property is migrated as well as the
76 // content URL.
77 // The album artwork uses nsIFile.initWithPath for local artwork files (e.g.,
78 // folder.jpg), so the primaryImageURL property needs to be migrated.
79 // See http://bugzilla.songbirdnest.com/show_bug.cgi?id=21568 and
80 // http://bugzilla.songbirdnest.com/show_bug.cgi?id=21612 for reference.
81 //
82 //-----------------------------------------------------------------------------
83 
84 sbLibraryMigration.prototype = {
85  __proto__: SBLocalDatabaseMigrationUtils.BaseMigrationHandler.prototype,
86  classDescription: 'Songbird Migration Handler, version ' +
87  FROM_VERSION + ' to ' + TO_VERSION,
88  classID: Components.ID("{d460596d-aa22-4bd4-ae83-e82fb5a70019}"),
89  contractID: SBLocalDatabaseMigrationUtils.baseHandlerContractID +
90  FROM_VERSION + 'to' + TO_VERSION,
91 
92  _errors: [],
93  _progress: 0,
94  _total: 0,
95 
96  fromVersion: FROM_VERSION,
97  toVersion: TO_VERSION,
98 
99  batchSize: 1000,
100 
101  migrate: function sbLibraryMigration_migrate(aLibrary) {
102  try {
103  // Get the platform.
104  var sysInfo = Cc["@mozilla.org/system-info;1"]
105  .getService(Ci.nsIPropertyBag2);
106  var platform = sysInfo.getProperty("name");
107 
108  // Get the database GUID and location.
109  this._databaseGUID = aLibrary.databaseGuid;
110  this._databaseLocation = aLibrary.databaseLocation;
111 
112  // Convert URL property values on Mac OS/X.
113  if (platform == "Darwin") {
114  this._convertTopLevelURLPropertyValues(aLibrary, "content_url");
115  this._convertNonTopLevelURLPropertyValues(aLibrary,
116  SBProperties.originURL);
117  this._convertNonTopLevelURLPropertyValues(aLibrary,
118  SBProperties.primaryImageURL);
119  }
120 
121  // Run a query that will mark the library as migrated
122  var query = this.createMigrationQuery(aLibrary);
123  query.addQuery("commit");
124  query.setAsyncQuery(false);
125  query.execute();
126 
127  // Raise a flag indicating that this library will need all
128  // sort info to be recomputed.
129  // Normally we'd call propertyCache.invalidateSortData(), but
130  // at this point in startup the property cache does not exist yet.
131  var prefs = Cc["@mozilla.org/preferences-service;1"]
132  .getService(Ci.nsIPrefBranch);
133  prefs.setBoolPref("songbird.propertycache." +
134  this._databaseGUID + ".invalidSortData",
135  true);
136  prefs.QueryInterface(Ci.nsIPrefService).savePrefFile(null);
137  }
138  catch (e) {
139  LOG("Exception occured: " + e);
140  throw e;
141  }
142  },
143 
144 
153  _convertTopLevelURLPropertyValues: function
154  sbLibraryMigration__convertTopLevelURLPropertyValues(aLibrary,
155  aPropertyName) {
156  // Set up the query strings.
157  var selectQueryStr = <>SELECT guid, {aPropertyName} FROM media_items
158  WHERE {aPropertyName} LIKE "file:%"</>;
159  var updatePreparedQueryStr = <>UPDATE media_items
160  SET {aPropertyName} = ?
161  WHERE guid = ?</>;
162 
163  // Convert the URL property values.
164  this._convertURLPropertyValues(aLibrary,
165  selectQueryStr,
166  updatePreparedQueryStr);
167  },
168 
169 
178  _convertNonTopLevelURLPropertyValues: function
179  sbLibraryMigration__convertNonTopLevelURLPropertyValues(aLibrary,
180  aPropertyName) {
181  // Get the property ID from the property name.
182  var propertyID = this._getPropertyID(aLibrary, aPropertyName);
183 
184  // Set up the query strings.
185  var selectQueryStr = <>SELECT media_item_id, obj
186  FROM resource_properties
187  WHERE property_id = "{propertyID}" AND
188  obj LIKE "file:%"</>;
189  var updatePreparedQueryStr = <>UPDATE resource_properties
190  SET obj = ?
191  WHERE media_item_id = ? AND
192  property_id = "{propertyID}"</>;
193 
194  // Convert the URL property values.
195  this._convertURLPropertyValues(aLibrary,
196  selectQueryStr,
197  updatePreparedQueryStr);
198  },
199 
200 
216  _convertURLPropertyValues: function
217  sbLibraryMigration__convertURLPropertyValues(aLibrary,
218  aSelectQueryStr,
219  aUpdatePreparedQueryStr) {
220  // Select the list of URL property values to update.
221  var query = Cc["@songbirdnest.com/Songbird/DatabaseQuery;1"]
222  .createInstance(Ci.sbIDatabaseQuery);
223  query.databaseLocation = aLibrary.databaseLocation;
224  query.setDatabaseGUID(aLibrary.databaseGuid);
225  query.addQuery(aSelectQueryStr);
226  query.setAsyncQuery(false);
227  if (query.execute() != 0)
228  throw "Media item fetch failed";
229  var result = query.getResultObject();
230 
231  // Convert property URLs to nsIFiles and back again.
232  var preparedStatement;
233  var queryCount = 0;
234  query = null;
235  this._total = result.getRowCount();
236  for (this._progress = 0; this._progress < this._total; ++this._progress) {
237  // Get the property info.
238  var id = result.getRowCell(this._progress, 0);
239  var url = result.getRowCell(this._progress, 1);
240 
241  // Convert the URL to an nsIFile and back. Skip property if no converted
242  // URL or converted URL is the same as the original.
243  var convertedURL = this._convertURL(url);
244  if (!convertedURL || (convertedURL == url))
245  continue;
246 
247  // Set up query to write converted URL back.
248  if (!query) {
249  query = Cc["@songbirdnest.com/Songbird/DatabaseQuery;1"]
250  .createInstance(Ci.sbIDatabaseQuery);
251  query.databaseLocation = aLibrary.databaseLocation;
252  query.setDatabaseGUID(aLibrary.databaseGuid);
253  preparedStatement = query.prepareQuery(aUpdatePreparedQueryStr);
254  }
255  query.addPreparedStatement(preparedStatement);
256  query.bindStringParameter(0, convertedURL);
257  query.bindStringParameter(1, id);
258  queryCount++;
259 
260  // Execute query if batch is full.
261  if (queryCount >= this.batchSize) {
262  query.setAsyncQuery(false);
263  if (query.execute() != 0)
264  throw "Media item write failed";
265  query = null;
266  queryCount = 0;
267  }
268  }
269 
270  // Execute any remaining queries.
271  if (query && (queryCount > 0)) {
272  query.setAsyncQuery(false);
273  if (query.execute() != 0)
274  throw "Media item write failed";
275  }
276  },
277 
278 
288  _convertURL: function sbLibraryMigration__convertURL(aURL) {
289  // Get the IO service.
290  var ioService = Cc["@mozilla.org/network/io-service;1"]
291  .getService(Ci.nsIIOService);
292 
293  // Convert the URL to an nsIFile and back.
294  try {
295  var uri = ioService.newURI(aURL, null, null);
296  var fileURL = uri.QueryInterface(Ci.nsIFileURL);
297  var convertedURL = ioService.newFileURI(fileURL.file).spec;
298  }
299  catch (e) {
300  LOG("URL should have been a file URL: " + aURL + ": " + e);
301  return null;
302  }
303 
304  return convertedURL;
305  },
306 
307 
318  _getPropertyID: function sbLibraryMigration__getPropertyID(aLibrary,
319  aPropertyName) {
320  // Query for the property ID.
321  var query = Cc["@songbirdnest.com/Songbird/DatabaseQuery;1"]
322  .createInstance(Ci.sbIDatabaseQuery);
323  query.databaseLocation = aLibrary.databaseLocation;
324  query.setDatabaseGUID(aLibrary.databaseGuid);
325  query.addQuery(<>SELECT property_id FROM properties
326  WHERE property_name = "{aPropertyName}"</>);
327  query.setAsyncQuery(false);
328  if (query.execute() != 0)
329  throw "Property ID query failed";
330 
331  return query.getResultObject().getRowCell(0, 0);
332  },
333 
334 
336  get status() Ci.sbIJobProgress.STATUS_RUNNING,
337  get blocked() false,
338  get progress() this._progress,
339  get total() this._total,
340  get errorCount() this._errors.length,
341  getErrorMessages: function sbLibraryMigration_getErrorMessages()
342  ArrayConverter.StringEnumerator(this._errors),
345  get canCancel() false,
346  cancel: function sbLibraryMigration_cancel()
347  { throw Cr.NS_ERROR_NOT_IMPLEMENTED }
348 };
349 
350 //-----------------------------------------------------------------------------
351 // Module
352 //-----------------------------------------------------------------------------
353 function NSGetModule(compMgr, fileSpec) {
354  return XPCOMUtils.generateModule([
356  ]);
357 }
358 
var total
sbDeviceFirmwareAutoCheckForUpdate prototype contractID
function LOG(s)
sbDeviceFirmwareAutoCheckForUpdate prototype classDescription
var ioService
return null
Definition: FeedWriter.js:1143
function sbLibraryMigration()
var uri
Definition: FeedWriter.js:1135
function url(spec)
var prefs
Definition: FeedWriter.js:1169
sbDeviceFirmwareAutoCheckForUpdate prototype classID
const FROM_VERSION
function NSGetModule(compMgr, fileSpec)