sbiTunesImporter.cpp
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-2009 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 #include "sbiTunesImporter.h"
28 
29 // C/C++ includes
30 #include <algorithm>
31 
32 // Mozilla includes
33 #include <nsAppDirectoryServiceDefs.h>
34 #include <nsArrayUtils.h>
35 #include <nsAutoPtr.h>
36 #include <nsCOMArray.h>
37 #include <nsComponentManagerUtils.h>
38 #include <nsDirectoryServiceUtils.h>
39 #include <nsXPCOMCIDInternal.h>
40 #include <nsIArray.h>
41 #include <nsIBufferedStreams.h>
42 #include <nsIFile.h>
43 #include <nsIFileURL.h>
44 #include <nsIInputStream.h>
45 #include <nsIIOService.h>
46 #include <nsILineInputStream.h>
47 #include <nsILocalFile.h>
48 #include <nsIProperties.h>
49 #include <nsISimpleEnumerator.h>
50 #include <nsIURI.h>
51 #include <nsIXULRuntime.h>
52 #include <nsMemory.h>
53 #include <nsNetUtil.h>
54 #include <nsThreadUtils.h>
55 
56 // NSPR includes
57 #include <prtime.h>
58 
59 // Songbird includes
60 #include <sbIAlbumArtFetcherSet.h>
61 #include <sbIAlbumArtListener.h>
62 #include <sbFileUtils.h>
63 #include <sbILibrary.h>
64 #include <sbILocalDatabaseLibrary.h>
65 #include <sbIMediaList.h>
66 #include <sbIPropertyArray.h>
67 #include <sbLibraryUtils.h>
68 #include <sbIMediacoreTypeSniffer.h>
69 #include <sbPrefBranch.h>
70 #include <sbPropertiesCID.h>
71 #include <sbStringUtils.h>
72 #include <sbStandardProperties.h>
73 #ifdef SB_ENABLE_TEST_HARNESS
74 #include <sbITimingService.h>
75 #endif
76 
78 #include "sbiTunesImporterJob.h"
79 #include "sbiTunesImporterStatus.h"
80 
81 char const SB_ITUNES_LIBRARY_IMPORT_PREF_PREFIX[] = "library_import.itunes";
82 char const SB_ITUNES_LIBRARY_IMPORTER_PREF_PREFIX[] = "songbird.library_importer.";
83 
84 #ifdef PR_LOGGING
85 static PRLogModuleInfo* giTunesImporter = nsnull;
86 #define TRACE(args) \
87  PR_BEGIN_MACRO \
88  if (!giTunesImporter) \
89  giTunesImporter = PR_NewLogModule("sbiTunesImporter"); \
90  PR_LOG(giTunesImporter, PR_LOG_DEBUG, args); \
91  PR_END_MACRO
92 #define LOG(args) \
93  PR_BEGIN_MACRO \
94  if (!giTunesImporter) \
95  giTunesImporter = PR_NewLogModule("sbiTunesImporter"); \
96  PR_LOG(giTunesImporter, PR_LOG_WARN, args); \
97  PR_END_MACRO
98 
99 
100 #else
101 #define TRACE(args) /* nothing */
102 #define LOG(args) /* nothing */
103 #endif /* PR_LOGGING */
104 
109 typedef nsString (*ValueConversion)(nsAString const & aValue);
110 
121 nsString ConvertRating(nsAString const & aRating) {
122  nsresult rv;
123  if (aRating.IsEmpty()) {
124  return nsString();
125  }
126  PRInt32 rating = aRating.ToInteger(&rv, 10);
127  nsString result;
128  if (NS_SUCCEEDED(rv)) {
129  result.AppendInt((rating + 10) / 20, 10);
130  }
131  return result;
132 }
133 
143 nsString ConvertDuration(nsAString const & aDuration) {
144  nsresult rv;
145  if (aDuration.IsEmpty()) {
146  return nsString();
147  }
148  PRInt32 const duration = aDuration.ToInteger(&rv, 10);
149  nsString result;
150  if (NS_SUCCEEDED(rv)) {
151  result.AppendInt(duration * 1000);
152  }
153  return result;
154 }
155 
165 nsString ConvertDateTime(nsAString const & aDateTime) {
166  // If empty just return empty
167  if (aDateTime.IsEmpty()) {
168  return nsString();
169  }
170 
171  // Convert "1970-01-31T00:00:00Z" to "01-31-1970 00:00:00 GMT" for parsing
172  // by PR_ParseTimeString
173 
174  // Split off "Z" and anything after (time zone spec?)
175  nsCAutoString dateTime = ::NS_LossyConvertUTF16toASCII(aDateTime);
176  nsTArray<nsCString> splitString;
177  nsCString_Split(dateTime, NS_LITERAL_CSTRING("Z"), splitString);
178  NS_ENSURE_TRUE(splitString.Length() > 0, nsString());
179  dateTime = splitString[0];
180 
181  // Split up the date and time strings
182  nsCString_Split(dateTime, NS_LITERAL_CSTRING("T"), splitString);
183  NS_ENSURE_TRUE(splitString.Length() > 1, nsString());
184  nsCAutoString dateString = splitString[0];
185  nsCAutoString timeString = splitString[1];
186 
187  // Split up the day, month, and year strings
188  nsCString_Split(dateString, NS_LITERAL_CSTRING("-"), splitString);
189  NS_ENSURE_TRUE(splitString.Length() > 2, nsString());
190  nsCAutoString yearString = splitString[0];
191  nsCAutoString monthString = splitString[1];
192  nsCAutoString dayString = splitString[2];
193 
194  // Produce the "01-31-1970 00:00:00 GMT" format
195  char cDateTime[128];
196  cDateTime[0] = '\0';
197  PR_snprintf(cDateTime,
198  sizeof(cDateTime),
199  "%s-%s-%s %s GMT",
200  monthString.get(),
201  dayString.get(),
202  yearString.get(),
203  timeString.get());
204 
205  // Parse the date/time string into epoch time
206  PRTime prTime;
207  PRStatus status = PR_ParseTimeString(cDateTime, PR_TRUE, &prTime);
208 
209  // On failure just return an emptry string
210  if (status == PR_FAILURE) {
211  NS_WARNING("Failed to parse time in sbiTunesImporter ConvertDateTime");
212  return nsString();
213  }
214 
215  // Convert time to microseconds
216  prTime /= PR_USEC_PER_MSEC;
217 
218  return sbAutoString(static_cast<PRUint64>(prTime));
219 }
220 
221 struct PropertyMap {
222  char const * SBProperty;
223  char const * ITProperty;
225 };
226 
231  { SB_PROPERTY_ITUNES_GUID, "Persistent ID", 0 },
232  { SB_PROPERTY_ALBUMARTISTNAME, "Album Artist", 0 },
233  { SB_PROPERTY_ALBUMNAME, "Album", 0 },
234  { SB_PROPERTY_ARTISTNAME, "Artist", 0 },
235  { SB_PROPERTY_BITRATE, "Bit Rate", 0 },
236  { SB_PROPERTY_BPM, "BPM", 0 },
237  { SB_PROPERTY_COMMENT, "Comments", 0 },
238  { SB_PROPERTY_COMPOSERNAME, "Composer", 0 },
239  { SB_PROPERTY_DISCNUMBER, "Disc Number", 0 },
240  { SB_PROPERTY_DURATION, "Total Time", ConvertDuration },
241  { SB_PROPERTY_GENRE, "Genre", 0},
242  { SB_PROPERTY_LASTPLAYTIME, "Play Date UTC", ConvertDateTime },
243  { SB_PROPERTY_LASTSKIPTIME, "Skip Date", ConvertDateTime },
244  { SB_PROPERTY_PLAYCOUNT, "Play Count", 0 },
245  { SB_PROPERTY_RATING, "Rating", ConvertRating },
246  { SB_PROPERTY_SAMPLERATE, "Sample Rate", 0 },
247  { SB_PROPERTY_SKIPCOUNT, "Skip Count", 0 },
248  { SB_PROPERTY_TOTALDISCS, "Disc Count", 0 },
249  { SB_PROPERTY_TOTALTRACKS, "Track Count", 0 },
250  { SB_PROPERTY_TRACKNAME, "Name", 0 },
251  { SB_PROPERTY_TRACKNUMBER, "Track Number", 0 },
252  { SB_PROPERTY_YEAR, "Year", 0 },
253 };
254 
257 
258 sbiTunesImporter::sbiTunesImporter() : mBatchEnded(PR_FALSE),
259  mDataFormatVersion(DATA_FORMAT_VERSION),
260  mFoundChanges(PR_FALSE),
261  mImportPlaylists(PR_TRUE),
262  mMissingMediaCount(0),
263  mOSType(UNINITIALIZED),
264  mTrackCount(0),
265  mUnsupportedMediaCount(0)
266 
267 {
268  mTrackBatch.reserve(BATCH_SIZE);
269 }
270 
271 sbiTunesImporter::~sbiTunesImporter()
272 {
273  // Just make sure we were finalized
274  Finalize();
275 }
276 
277 nsresult
278 sbiTunesImporter::Cancel() {
279  nsString msg = SBLocalizedString("import_library.job.status.cancelled");
280  mStatus->SetStatusText(msg);
281  mStatus->Done();
282  mStatus->Update();
283  return NS_OK;
284 }
285 
286 /* readonly attribute AString libraryType; */
287 NS_IMETHODIMP
288 sbiTunesImporter::GetLibraryType(nsAString & aLibraryType)
289 {
290  aLibraryType = NS_LITERAL_STRING("iTunes");
291  return NS_OK;
292 }
293 
294 /* readonly attribute AString libraryReadableType; */
295 NS_IMETHODIMP
296 sbiTunesImporter::GetLibraryReadableType(nsAString & aLibraryReadableType)
297 {
298  aLibraryReadableType = NS_LITERAL_STRING("iTunes");
299  return NS_OK;
300 }
301 
302 /* readonly attribute AString libraryDefaultFileName; */
303 NS_IMETHODIMP
304 sbiTunesImporter::GetLibraryDefaultFileName(nsAString & aLibraryDefaultFileName)
305 {
306  aLibraryDefaultFileName = NS_LITERAL_STRING("iTunes Music Library.xml");
307  return NS_OK;
308 }
309 
310 /* readonly attribute AString libraryDefaultFilePath; */
311 NS_IMETHODIMP
312 sbiTunesImporter::GetLibraryDefaultFilePath(nsAString & aLibraryDefaultFilePath)
313 {
314  nsresult rv;
315  nsCOMPtr<nsIProperties> directoryService =
316  do_CreateInstance("@mozilla.org/file/directory_service;1", &rv);
317  NS_ENSURE_SUCCESS(rv, rv);
318 
319  nsCOMPtr<nsIFile> libraryFile;
320 
321  nsString defaultLibraryName;
322  rv = GetLibraryDefaultFileName(defaultLibraryName);
323  NS_ENSURE_SUCCESS(rv, rv);
324 
325  /* Search for an iTunes library database file. */
326  switch (GetOSType())
327  {
328  case MAC_OS : {
329  rv = directoryService->Get("Music",
330  NS_GET_IID(nsIFile),
331  getter_AddRefs(libraryFile));
332  NS_ENSURE_SUCCESS(rv, rv);
333 
334  rv = libraryFile->Append(NS_LITERAL_STRING("iTunes"));
335  NS_ENSURE_SUCCESS(rv, rv);
336  }
337  break;
338  case WINDOWS_OS : {
339  rv = directoryService->Get("Music",
340  NS_GET_IID(nsIFile),
341  getter_AddRefs(libraryFile));
342 
343  if (NS_FAILED(rv)) {
344  rv = directoryService->Get("Pers",
345  NS_GET_IID(nsIFile),
346  getter_AddRefs(libraryFile));
347  NS_ENSURE_SUCCESS(rv, rv);
348 
349  rv = libraryFile->Append(NS_LITERAL_STRING("My Music"));
350  NS_ENSURE_SUCCESS(rv, rv);
351  }
352 
353  rv = libraryFile->Append(NS_LITERAL_STRING("iTunes"));
354  NS_ENSURE_SUCCESS(rv, rv);
355  }
356  break;
357  default : {
358  /* We _could_ use XDGMusic, but since Linux doesn't actually have iTunes
359  * the user must have copied it manually - which means it wouldn't
360  * actually make sense to try to be smart.
361  */
362  rv = directoryService->Get("Home",
363  NS_GET_IID(nsIFile),
364  getter_AddRefs(libraryFile));
365  NS_ENSURE_SUCCESS(rv, rv);
366  }
367  break;
368  }
369 
370  rv = libraryFile->Append(defaultLibraryName);
371  NS_ENSURE_SUCCESS(rv, rv);
372 
373  /* If the library file exists, get its path. */
374  PRBool exists = PR_FALSE;
375  rv = libraryFile->Exists(&exists);
376  NS_ENSURE_SUCCESS(rv, rv);
377 
378  if (exists) {
379  nsString path;
380  rv = libraryFile->GetPath(path);
381  NS_ENSURE_SUCCESS(rv, rv);
382 
383  aLibraryDefaultFilePath = path;
384  return NS_OK;
385  }
386 
387  return NS_OK;
388 }
389 
390 /* readonly attribute AString libraryFileExtensionList; */
391 NS_IMETHODIMP
392 sbiTunesImporter::GetLibraryFileExtensionList(
393  nsAString & aLibraryFileExtensionList)
394 {
395  aLibraryFileExtensionList = NS_LITERAL_STRING("xml");
396  return NS_OK;
397 }
398 
399 /* readonly attribute boolean libraryPreviouslyImported; */
400 NS_IMETHODIMP
401 sbiTunesImporter::GetLibraryPreviouslyImported(
402  PRBool *aLibraryPreviouslyImported)
403 {
404  nsresult rv;
406  NS_ENSURE_SUCCESS(rv, rv);
407 
408  nsCString const & temp = prefs.GetCharPref("lib_prev_mod_time", nsCString());
409  *aLibraryPreviouslyImported = temp.IsEmpty() ? PR_FALSE : PR_TRUE;
410  return NS_OK;
411 }
412 
413 /* readonly attribute AString libraryPreviousImportPath; */
414 NS_IMETHODIMP
415 sbiTunesImporter::GetLibraryPreviousImportPath(
416  nsAString & aLibraryPreviousImportPath)
417 {
418  nsresult rv;
420  NS_ENSURE_SUCCESS(rv, rv);
421 
422  aLibraryPreviousImportPath =
423  ::NS_ConvertUTF8toUTF16(prefs.GetCharPref("lib_prev_path",
424  nsCString()));
425  return NS_OK;
426 }
427 
428 /* void initialize (); */
429 NS_IMETHODIMP
430 sbiTunesImporter::Initialize()
431 {
432  nsresult rv = miTunesLibSig.Initialize();
433  NS_ENSURE_SUCCESS(rv, rv);
434 
435  mIOService =
436  do_CreateInstance("@mozilla.org/network/io-service;1", &rv);
437  NS_ENSURE_SUCCESS(rv, rv);
438 
439  mAlbumArtFetcher =
440  do_CreateInstance("@songbirdnest.com/Songbird/album-art-fetcher-set;1", &rv);
441  NS_ENSURE_SUCCESS(rv, rv);
442 
443  // Reset flags to their default value
444  mBatchEnded = PR_FALSE;
445  mFoundChanges = PR_FALSE;
446  mUnsupportedMediaCount = 0;
447  mMissingMediaCount = 0;
448 
449  rv = GetMainLibrary(getter_AddRefs(mLibrary));
450  NS_ENSURE_SUCCESS(rv, rv);
451 
452  mLDBLibrary = do_QueryInterface(mLibrary, &rv);
453  NS_ENSURE_SUCCESS(rv, rv);
454 
455 #ifdef SB_ENABLE_TEST_HARNESS
456  // Ignore errors, we'll just not do timing
457  mTimingService = do_GetService("@songbirdnest.com/Songbird/TimingService;1", &rv);
458 #endif
459 
460  rv = miTunesDBServices.Initialize();
461  NS_ENSURE_SUCCESS(rv, rv);
462 
463  // Read in the added results file
464  do { /* scope, and to jump out of */
465  nsCOMPtr<nsIFile> resultsFile;
466  rv = NS_GetSpecialDirectory(NS_APP_APPLICATION_REGISTRY_DIR,
467  getter_AddRefs(resultsFile));
468  NS_ENSURE_SUCCESS(rv, rv);
469  rv = resultsFile->Append(NS_LITERAL_STRING("itunesexportresults.txt"));
470  NS_ENSURE_SUCCESS(rv, rv);
471 
472  PRBool success;
473  rv = resultsFile->Exists(&success);
474  NS_ENSURE_SUCCESS(rv, rv);
475 
476  if (!success) {
477  // no file, escape from scope
478  break;
479  }
480 
481  nsCOMPtr<nsIInputStream> inputStream;
482  rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), resultsFile);
483  NS_ENSURE_SUCCESS(rv, rv);
484  nsCOMPtr<nsILineInputStream> lineStream = do_QueryInterface(inputStream, &rv);
485  NS_ENSURE_SUCCESS(rv, rv);
486 
487  do { // loop over lines
488  nsCAutoString line;
489  rv = lineStream->ReadLine(line, &success);
490  NS_ENSURE_SUCCESS(rv, rv);
491 
492  if (StringBeginsWith(line, NS_LITERAL_CSTRING("["))) {
493  // skip for now
494  continue;
495  }
496 
497  // line format is
498  // <songbird guid>=<library persistent id>,<track database id>
499  // with only the last part be variable-length
500  const int LEN_GUID = 36;
501  const int LEN_PID = 16;
502 
503  if (line.Length() < LEN_GUID + LEN_PID + 2) {
504  continue;
505  }
506  if (line[LEN_GUID] != '=' ||
507  line[LEN_GUID + 1 + LEN_PID] != ',')
508  {
509  // invalidly formatted line
510  continue;
511  }
512 
513  NS_ConvertASCIItoUTF16 sbid(Substring(line, 0, LEN_GUID)),
514  libid(Substring(line, LEN_GUID + 1, LEN_PID)),
515  id(Substring(line, LEN_GUID + 2 + LEN_PID));
516  miTunesDBServices.MapID(libid, id, sbid);
517  } while (success);
518  /* rv = */ inputStream->Close();
519  /* rv = */ resultsFile->Remove(PR_FALSE);
520  } while(false);
521 
522  mPlaylistBlacklist =
523  SBLocalizedString("import_library.itunes.excluded_playlists");
524  return NS_OK;
525 }
526 
527 /* void finalize (); */
528 NS_IMETHODIMP
529 sbiTunesImporter::Finalize()
530 {
531  if (!mBatchEnded) {
532  mBatchEnded = PR_TRUE;
533  if (mLDBLibrary) {
534  mLDBLibrary->ForceEndUpdateBatch();
535  }
536  }
537  mListener = nsnull;
538  mLibrary = nsnull;
539  mLDBLibrary = nsnull;
540  if (mStatus.get()) {
541  mStatus->Finalize();
542  }
543  return NS_OK;
544 }
545 
546 nsresult sbiTunesImporter::DBModified(sbPrefBranch & aPrefs,
547  nsAString const & aLibPath,
548  PRBool * aModified) {
549  *aModified = PR_TRUE;
550  nsresult rv;
551 
552  // Check that the path is the same
553  nsString prevPath;
554  rv = GetLibraryPreviousImportPath(prevPath);
555  if (NS_FAILED(rv) || !aLibPath.Equals(prevPath)) {
556  return NS_OK;
557  }
558 
559  // Check the last modified times
560  nsCOMPtr<nsILocalFile> file = do_CreateInstance(NS_LOCALFILE_CONTRACTID);
561  rv = file->InitWithPath(aLibPath);
562  if (NS_FAILED(rv)) {
563  return NS_OK;
564  }
565 
566  PRInt64 lastModified;
567  rv = file->GetLastModifiedTime(&lastModified);
568  if (NS_FAILED(rv)) {
569  return NS_OK;
570  }
571  nsCString const & temp = aPrefs.GetCharPref("lib_prev_mod_time", nsCString());
572  if (temp.IsEmpty()) {
573  return NS_OK;
574  }
575  PRInt64 prevLastModified =
576  nsString_ToInt64(NS_ConvertASCIItoUTF16(temp), &rv);
577  if (NS_SUCCEEDED(rv)) {
578  *aModified = lastModified != prevLastModified;
579  }
580  return NS_OK;
581 }
582 /* sbIJobProgress import (in AString aLibFilePath,
583  * in AString aGUID,
584  * in boolean aCheckForChanges); */
585 NS_IMETHODIMP
586 sbiTunesImporter::Import(const nsAString & aLibFilePath,
587  const nsAString & aGUID,
588  PRBool aCheckForChanges,
589  sbIJobProgress ** aJobProgress)
590 {
591  NS_ENSURE_ARG_POINTER(aJobProgress);
592  TRACE(("sbiTunesImporter::Import(%s, %s, %s)",
593  NS_LossyConvertUTF16toASCII(aLibFilePath).get(),
594  NS_LossyConvertUTF16toASCII(aGUID).get(),
595  (aCheckForChanges ? "true" : "false")));
596  // Must be started on the main thread
597  NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_FAILURE);
598 
599  nsresult rv;
600 
601  // reset members
602  mFoundChanges = PR_FALSE;
603  mMissingMediaCount = 0;
604  mTrackCount = 0;
605  mUnsupportedMediaCount = 0;
606 
607  mLibraryPath = aLibFilePath;
608  mImport = aCheckForChanges ? PR_FALSE : PR_TRUE;
610  NS_ENSURE_SUCCESS(rv, rv);
611 
612  nsRefPtr<sbiTunesImporterJob> jobProgress = sbiTunesImporterJob::New();
613  mStatus =
614  std::auto_ptr<sbiTunesImporterStatus>(sbiTunesImporterStatus::New(jobProgress));
615  NS_ENSURE_TRUE(mStatus.get(), NS_ERROR_FAILURE);
616 
617  mStatus->Initialize();
618 
619  mDataFormatVersion = prefs.GetIntPref("version", DATA_FORMAT_VERSION);
620  // If we're checking for changes and the db wasn't modified, just exit
621  PRBool modified;
622  if (!mImport && NS_SUCCEEDED(DBModified(prefs,
623  mLibraryPath,
624  &modified)) && !modified) {
625  /* Update status. */
626  rv = mStatus->Reset();
627  NS_ENSURE_SUCCESS(rv, rv);
628 
629  mStatus->SetStatusText(SBLocalizedString("import_library.itunes.no_changes"));
630  mStatus->Done();
631  mStatus->Update();
632  return NS_OK;
633  }
634  mImportPlaylists = PR_FALSE;
635  mBatchEnded = PR_FALSE;
636 
637  if (mImport) {
639  NS_ENSURE_SUCCESS(rv, rv);
640 
641  mImportPlaylists = userPrefs.GetBoolPref("import_playlists", PR_FALSE);
642  }
643 
644  mTypeSniffer = do_CreateInstance("@songbirdnest.com/Songbird/Mediacore/TypeSniffer;1", &rv);
645  NS_ENSURE_SUCCESS(rv, rv);
646 
647 #ifdef SB_ENABLE_TEST_HARNESS
648  if (mTimingService) {
649  mTimingIdentifier = NS_LITERAL_STRING("ITunesImport-");
650  mTimingIdentifer.AppendInt(time(0));
651  mTimingService->StartPerfTimer(mTimingIdentifier);
652  }
653 #endif
654 
655  rv = sbOpenInputStream(mLibraryPath, getter_AddRefs(mStream));
656  NS_ENSURE_SUCCESS(rv, rv);
657 
658  nsCOMPtr<nsILocalFile> file = do_CreateInstance("@mozilla.org/file/local;1",
659  &rv);
660  NS_ENSURE_SUCCESS(rv, rv);
661 
662  rv = file->InitWithPath(mLibraryPath);
663  if (NS_SUCCEEDED(rv)) {
664  PRInt64 size;
665  rv = file->GetFileSize(&size);
666  if (NS_SUCCEEDED(rv)) {
667  mStatus->SetProgressMax(size);
668  }
669  }
670 
671 
672  nsAString const & msg =
673  mImport ? SBLocalizedString("import_library.itunes.importing") :
674  SBLocalizedString("import_library.itunes.updating");
675 
676  mStatus->SetStatusText(msg);
677 
678  // Scope batching
679  {
680  // Put the library in batch mode
681  mLDBLibrary->ForceBeginUpdateBatch();
682 
683  mParser = sbiTunesXMLParser::New();
684 
685 
686  rv = mParser->Parse(mStream, this);
687  NS_ENSURE_SUCCESS(rv, rv);
688  }
689 
690  *aJobProgress = jobProgress;
691  NS_IF_ADDREF(*aJobProgress);
692  return NS_OK;
693 }
694 
695 /* void setListener (in sbILibraryImporterListener aListener); */
696 NS_IMETHODIMP
697 sbiTunesImporter::SetListener(sbILibraryImporterListener *aListener)
698 {
699  NS_WARN_IF_FALSE(mListener == nsnull && aListener, "Listener was previously set");
700 
701  mListener = aListener;
702 
703  return NS_OK;
704 }
705 
706 sbiTunesImporter::OSType sbiTunesImporter::GetOSType()
707 {
708  if (mOSType == UNINITIALIZED) {
709  nsresult rv;
710  nsCOMPtr<nsIXULRuntime> appInfo =
711  do_CreateInstance(XULRUNTIME_SERVICE_CONTRACTID, &rv);
712  NS_ENSURE_SUCCESS(rv, UNKNOWN_OS);
713 
714  nsCString osName;
715  rv = appInfo->GetOS(osName);
716  NS_ENSURE_SUCCESS(rv, UNKNOWN_OS);
717 
718  ToLowerCase(osName);
719 
720  /* Determine the OS type. */
721  if (osName.Find("darwin") != -1) {
722  mOSType = MAC_OS;
723  }
724  else if (osName.Find("linux") != -1) {
725  mOSType = LINUX_OS;
726  }
727  else if (osName.Find("win") != -1) {
728  mOSType = WINDOWS_OS;
729  }
730  else {
731  mOSType = UNKNOWN_OS;
732  }
733  }
734  return mOSType;
735 }
736 
737 // sbIiTunesXMLParserListener implementation
738 
739 /* void onTopLevelProperties (in sbIStringMap aProperties); */
740 NS_IMETHODIMP sbiTunesImporter::OnTopLevelProperties(sbIStringMap *aProperties) {
741  NS_ENSURE_ARG_POINTER(aProperties);
742 
743  nsresult rv = aProperties->Get(NS_LITERAL_STRING("Library Persistent ID"),
744  miTunesLibID);
745  NS_ENSURE_SUCCESS(rv, rv);
746 
747  nsString id(NS_LITERAL_STRING("Library Persistent ID"));
748  id.Append(miTunesLibID);
749  rv = miTunesLibSig.Update(id);
750  NS_ENSURE_SUCCESS(rv, rv);
751 
752  return NS_OK;
753 }
754 
755 NS_IMETHODIMP
756 sbiTunesImporter::OnTrack(sbIStringMap *aProperties) {
757  NS_ENSURE_ARG_POINTER(aProperties);
758 
759  if (mStatus->CancelRequested()) {
760  Cancel();
761  return NS_ERROR_ABORT;
762  }
763  nsresult rv = UpdateProgress();
764 
765  nsAutoPtr<iTunesTrack> track(new iTunesTrack);
766  NS_ENSURE_TRUE(track, NS_ERROR_OUT_OF_MEMORY);
767 
768  rv = track->Initialize(aProperties);
769  NS_ENSURE_SUCCESS(rv, rv);
770 
771 #ifdef DEBUG
772  nsString uri16;
773  if (track->mProperties.Get(NS_LITERAL_STRING("Location"), &uri16)) {
774  LOG(("Importing Track %s\n", NS_ConvertUTF16toUTF8(uri16).get()));
775  }
776 #endif
777  // If there is no persistent ID, then skip it
778  nsString persistentID;
779  if (!track->mProperties.Get(NS_LITERAL_STRING(SB_PROPERTY_ITUNES_GUID),
780  &persistentID)) {
781  return NS_OK;
782  }
783  mTrackBatch.push_back(track.forget());
784  if (mTrackBatch.size() == BATCH_SIZE) {
785  ProcessTrackBatch();
786  }
787  return NS_OK;
788 }
789 
790 NS_IMETHODIMP
791 sbiTunesImporter::OnTracksComplete() {
792  if (!mStatus->CancelRequested() && mTrackBatch.size() > 0) {
793  ProcessTrackBatch();
794  }
795  return NS_OK;
796 }
797 
798 PRBool
799 sbiTunesImporter::ShouldImportPlaylist(sbIStringMap * aProperties) {
800 
801  nsString playlistName;
802  nsresult rv = aProperties->Get(NS_LITERAL_STRING("Name"), playlistName);
803  NS_ENSURE_SUCCESS(rv, PR_FALSE);
804 
805  // If we've seen the songbird folder check the parent ID of the playlist
806  // and if the parent is Songbird we don't need to import this playlist
807  // since this is a Songbird playlist
808  if (!mSongbirdFolderID.IsEmpty()) {
809  nsString parentID;
810  rv = aProperties->Get(NS_LITERAL_STRING("Parent Persistent ID"), parentID);
811  if (NS_FAILED(rv) || parentID.Equals(mSongbirdFolderID)) {
812  return PR_FALSE;
813  }
814  }
815  nsString master;
816  // Don't care if this errors
817  aProperties->Get(NS_LITERAL_STRING("Master"), master);
818 
819  nsString smartInfo;
820  aProperties->Get(NS_LITERAL_STRING("Smart Info"), smartInfo);
821 
822  nsString isFolder;
823  aProperties->Get(NS_LITERAL_STRING("Folder"), isFolder);
824 
825  nsString delimitedName;
826  delimitedName.AppendLiteral(":");
827  delimitedName.Append(playlistName);
828  delimitedName.AppendLiteral(":");
829  // If it has tracks, is not the master playlist, is not a folder, is not a
830  // smart playlist, and is not on the black list it should be imported
831  return !master.EqualsLiteral("true") &&
832  smartInfo.IsEmpty() &&
833  !isFolder.EqualsLiteral("true") &&
834  mPlaylistBlacklist.Find(delimitedName) == -1;
835 }
836 
840 static PRBool
842 
843  nsString playlistName;
844  nsresult rv = aProperties->Get(NS_LITERAL_STRING("Name"), playlistName);
845  NS_ENSURE_SUCCESS(rv, PR_FALSE);
846 
847  nsString smartInfo;
848  aProperties->Get(NS_LITERAL_STRING("Smart Info"), smartInfo);
849 
850  nsString isFolder;
851  aProperties->Get(NS_LITERAL_STRING("Folder"), isFolder);
852 
853  return smartInfo.IsEmpty() &&
854  isFolder.EqualsLiteral("true") &&
855  playlistName.EqualsLiteral("Songbird");
856 }
857 
858 static nsresult
860  nsAString const & aPlaylistName,
861  sbIMediaList ** aMediaList) {
862  NS_ENSURE_ARG_POINTER(aMediaList);
863  // Clear out so if we fail to find we return an empty string
864  nsCOMArray<sbIMediaItem> mediaItems;
865  nsresult rv =
867  NS_LITERAL_STRING(SB_PROPERTY_MEDIALISTNAME),
868  aPlaylistName,
869  mediaItems);
870  if (NS_SUCCEEDED(rv) && mediaItems.Count() > 0) {
871  NS_WARN_IF_FALSE(mediaItems.Count() > 1, "We got multiple playlists for "
872  "the same name in iTunesImport");
873  // nsCOMArray doesn't addref on access so no need for an nsCOMPtr here
874  sbIMediaItem * mediaItem = mediaItems.ObjectAt(0);
875  if (mediaItem) {
876  rv = mediaItem->QueryInterface(NS_GET_IID(sbIMediaList),
877  reinterpret_cast<void**>(aMediaList));
878  NS_ENSURE_SUCCESS(rv, rv);
879  return NS_OK;
880  }
881  }
882  // Not found so return null
883  *aMediaList = nsnull;
884  return NS_OK;
885 }
886 
887 nsresult
888 sbiTunesImporter::UpdateProgress() {
889  mStatus->SetProgress(mBytesRead);
890  return NS_OK;
891 }
892 
893 NS_IMETHODIMP
894 sbiTunesImporter::OnPlaylist(sbIStringMap *aProperties,
895  PRInt32 *aTrackIds,
896  PRUint32 aTrackIdsCount)
897 {
898  NS_ENSURE_ARG_POINTER(aProperties);
899  NS_ENSURE_ARG_POINTER(aTrackIds);
900 
901  if (mStatus->CancelRequested()) {
902  Cancel();
903  return NS_ERROR_ABORT;
904  }
905  nsresult rv = UpdateProgress();
906  // If this is the Songbird folder save its ID
907  if (IsSongbirdFolder(aProperties)) {
908  // Ignore errors, if not found it's left empty
909  aProperties->Get(NS_LITERAL_STRING("Playlist Persistent ID"),
910  mSongbirdFolderID);
911  }
912  else if (ShouldImportPlaylist(aProperties)) {
913  nsString playlistID;
914  rv = aProperties->Get(NS_LITERAL_STRING("Playlist Persistent ID"),
915  playlistID);
916  NS_ENSURE_SUCCESS(rv, rv);
917 
918  NS_NAMED_LITERAL_STRING(name, "Name");
919  nsString playlistName;
920  rv = aProperties->Get(name, playlistName);
921  NS_ENSURE_SUCCESS(rv, rv);
922 
923  LOG(("Importing playlist %s\n", NS_ConvertUTF16toUTF8(playlistName).get()));
924  nsString text(name);
925  text.Append(playlistName);
926 
927  rv = miTunesLibSig.Update(text);
928  NS_ENSURE_SUCCESS(rv, rv);
929 
930  if (mImportPlaylists) {
931  nsString playlistSBGUID;
932  rv = miTunesDBServices.GetSBIDFromITID(miTunesLibID, playlistID,
933  playlistSBGUID);
934  nsCOMPtr<sbIMediaList> mediaList;
935  if ((NS_FAILED(rv) || playlistSBGUID.IsEmpty())
936  && mDataFormatVersion < 2) {
937  rv = FindPlaylistByName(mLibrary,
938  playlistName,
939  getter_AddRefs(mediaList));
940  NS_ENSURE_SUCCESS(rv, rv);
941  }
942  else if (!playlistSBGUID.IsEmpty()) {
943  nsCOMPtr<sbIMediaItem> mediaListAsItem;
944  // Get the media list as an item, if it fails then just continue with
945  // mediaList being null
946  rv = mLibrary->GetItemByGuid(playlistSBGUID, getter_AddRefs(mediaListAsItem));
947  if (NS_SUCCEEDED(rv)) {
948  // if this errors, mediaList will be null which is fine
949  mediaList = do_QueryInterface(mediaListAsItem);
950  }
951  }
952  ImportPlaylist(aProperties,
953  aTrackIds,
954  aTrackIdsCount,
955  mediaList);
956  }
957  }
958  return NS_OK;
959 }
960 
961 static nsresult
963  sbIMediaList * aMediaList) {
964  PRUint32 length;
965  nsresult rv = aMediaList->GetLength(&length);
966  NS_ENSURE_SUCCESS(rv, rv);
967 
968  nsString guid;
969  nsCOMPtr<sbIMediaItem> mediaItem;
970  for (PRUint32 index = 0; index < length; ++index) {
971  rv = aMediaList->GetItemByIndex(index, getter_AddRefs(mediaItem));
972  NS_ENSURE_SUCCESS(rv, rv);
973 
974  rv = mediaItem->GetGuid(guid);
975  NS_ENSURE_SUCCESS(rv, rv);
976 
977  aSignature.Update(guid);
978  }
979  return NS_OK;
980 }
981 
982 static nsresult
984  PRBool & aIsDirty) {
985  sbiTunesSignature signature;
986  nsresult rv = signature.Initialize();
987  NS_ENSURE_SUCCESS(rv, rv);
988 
989  rv = ComputePlaylistSignature(signature, aMediaList);
990  NS_ENSURE_SUCCESS(rv, rv);
991 
992  nsString computedSignature;
993  rv = signature.GetSignature(computedSignature);
994  NS_ENSURE_SUCCESS(rv, rv);
995 
996  nsString playlistGuid;
997  rv = aMediaList->GetGuid(playlistGuid);
998  NS_ENSURE_SUCCESS(rv, rv);
999 
1000  nsString storedSignature;
1001  rv = signature.RetrieveSignature(playlistGuid, storedSignature);
1002  NS_ENSURE_SUCCESS(rv, rv);
1003 
1004  aIsDirty = !computedSignature.Equals(storedSignature);
1005 
1006  return NS_OK;
1007 }
1008 
1009 nsresult
1010 sbiTunesImporter::GetDirtyPlaylistAction(nsAString const & aPlaylistName,
1011  nsAString & aAction) {
1012  aAction = NS_LITERAL_STRING("replace");
1013  // If the user hasn't given an action for all yet, ask them again
1014  if (mPlaylistAction.IsEmpty()) {
1015 
1016  PRBool applyAll;
1017  nsresult rv = mListener->OnDirtyPlaylist(aPlaylistName,
1018  &applyAll,
1019  aAction);
1020  NS_ENSURE_SUCCESS(rv, rv);
1021  if (applyAll) {
1022  mPlaylistAction = aAction;
1023  }
1024  }
1025  else { // The user gave us an answer for the rest, so use that
1026  aAction = mPlaylistAction;
1027  }
1028  return NS_OK;
1029 }
1030 
1031 static nsresult
1033  nsIMutableArray * aItems) {
1034  nsCOMPtr<nsISimpleEnumerator> enumerator;
1035  nsresult rv = aItems->Enumerate(getter_AddRefs(enumerator));
1036  NS_ENSURE_SUCCESS(rv, rv);
1037 
1038  rv = aMediaList->AddSome(enumerator);
1039  NS_ENSURE_SUCCESS(rv, rv);
1040 
1041  return NS_OK;
1042 }
1043 
1044 nsresult
1045 sbiTunesImporter::ProcessPlaylistItems(sbIMediaList * aMediaList,
1046  PRInt32 * aTrackIds,
1047  PRUint32 aTrackIdsCount) {
1048  NS_ENSURE_ARG_POINTER(aMediaList);
1049  NS_ENSURE_ARG_POINTER(aTrackIds);
1050 
1051  nsresult rv;
1052  nsCOMPtr<nsIMutableArray> tracks =
1053  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
1054  NS_ENSURE_SUCCESS(rv, rv);
1055 
1056  nsCOMPtr<sbIMediaItem> mediaItem;
1057  for (PRUint32 index = 0; index < aTrackIdsCount; ++index) {
1058  // If we're at a batch point
1059  if (((index + 1) % BATCH_SIZE) == 0) {
1060  rv = AddItemsToPlaylist(aMediaList, tracks);
1061  NS_ENSURE_SUCCESS(rv, rv);
1062 
1063  rv = tracks->Clear();
1064  NS_ENSURE_SUCCESS(rv, rv);
1065  }
1066  nsString trackID;
1067  trackID.AppendInt(aTrackIds[index], 10);
1068 
1069  // Add the iTuens track persistent ID to the iTunes library signature
1070  nsString text;
1071  text.AppendLiteral("Persistent ID");
1072  text.Append(miTunesLibID);
1073  text.Append(trackID);
1074  rv = miTunesLibSig.Update(text);
1075  NS_ENSURE_SUCCESS(rv, rv);
1076 
1077  TrackIDMap::const_iterator iter = mTrackIDMap.find(trackID);
1078  if (iter != mTrackIDMap.end()) {
1079  rv = mLibrary->GetItemByGuid(iter->second, getter_AddRefs(mediaItem));
1080  NS_ENSURE_SUCCESS(rv, rv);
1081 
1082  rv = tracks->AppendElement(mediaItem, PR_FALSE);
1083  NS_ENSURE_SUCCESS(rv, rv);
1084  }
1085  }
1086  rv = AddItemsToPlaylist(aMediaList, tracks);
1087  NS_ENSURE_SUCCESS(rv, rv);
1088 
1089  return NS_OK;
1090 }
1091 
1092 static nsresult
1094 
1095  sbiTunesSignature signature;
1096  nsresult rv = signature.Initialize();
1097  NS_ENSURE_SUCCESS(rv, rv);
1098 
1099  rv = ComputePlaylistSignature(signature, aMediaList);
1100  NS_ENSURE_SUCCESS(rv, rv);
1101 
1102  nsString theSignature;
1103  rv = signature.GetSignature(theSignature);
1104  NS_ENSURE_SUCCESS(rv, rv);
1105 
1106  nsString guid;
1107  rv = aMediaList->GetGuid(guid);
1108  NS_ENSURE_SUCCESS(rv, rv);
1109 
1110  rv = signature.StoreSignature(guid, theSignature);
1111  NS_ENSURE_SUCCESS(rv, rv);
1112 
1113  return NS_OK;
1114 }
1115 
1116 nsresult
1117 sbiTunesImporter::ImportPlaylist(sbIStringMap *aProperties,
1118  PRInt32 *aTrackIds,
1119  PRUint32 aTrackIdsCount,
1120  sbIMediaList * aMediaList) {
1121  NS_ENSURE_ARG_POINTER(aProperties);
1122  NS_ENSURE_ARG_POINTER(aTrackIds);
1123 
1124  nsresult rv;
1125 
1126  nsCOMPtr<sbIMediaList> mediaList(aMediaList);
1127  PRBool isDirty = PR_TRUE;
1128  if (mediaList) {
1129  rv = IsPlaylistDirty(mediaList, isDirty);
1130  NS_ENSURE_SUCCESS(rv, rv);
1131  }
1132 
1133  nsString playlistiTunesID;
1134  rv = aProperties->Get(NS_LITERAL_STRING("Playlist Persistent ID"),
1135  playlistiTunesID);
1136  NS_ENSURE_SUCCESS(rv, rv);
1137 
1138  nsString playlistName;
1139  rv = aProperties->Get(NS_LITERAL_STRING("Name"), playlistName);
1140  NS_ENSURE_SUCCESS(rv, rv);
1141 
1142  nsCString action("replace");
1143  if (!mImportPlaylists) {
1144  action = "keep";
1145  }
1146  else if (mediaList && isDirty) {
1147  nsString userAction;
1148  rv = GetDirtyPlaylistAction(playlistName, userAction);
1149  NS_ENSURE_SUCCESS(rv, rv);
1150  action = NS_LossyConvertUTF16toASCII(userAction);
1151  }
1152 
1153  if (action.Equals("replace")) {
1154  mFoundChanges = PR_TRUE;
1155 
1156  if (aTrackIdsCount > 0) {
1157  nsString guid;
1158 
1159  if (mediaList) {
1160  // Clear the medialist in preparation for processing playlist items as
1161  // ordered and listed in iTunes.
1162  rv = mediaList->Clear();
1163  NS_ENSURE_SUCCESS(rv, rv);
1164 
1165  // Unconditionally set the imported playlist's name.
1166  rv = mediaList->SetName(playlistName);
1167  NS_ENSURE_SUCCESS(rv, rv);
1168  }
1169  else {
1170  // Setup the properties. We need to do properties so that when
1171  // export sees this it knows it's an iTunes playlist
1172  nsCOMPtr<sbIMutablePropertyArray> properties =
1173  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
1174  NS_ENSURE_SUCCESS(rv, rv);
1175 
1176  rv = properties->AppendProperty(NS_LITERAL_STRING(SB_PROPERTY_MEDIALISTNAME),
1177  playlistName);
1178  NS_ENSURE_SUCCESS(rv, rv);
1179 
1180  rv = properties->AppendProperty(NS_LITERAL_STRING(SB_PROPERTY_ITUNES_GUID),
1181  playlistiTunesID);
1182  NS_ENSURE_SUCCESS(rv, rv);
1183 
1184  rv = mLibrary->CreateMediaList(NS_LITERAL_STRING("simple"),
1185  properties,
1186  getter_AddRefs(mediaList));
1187  NS_ENSURE_SUCCESS(rv, rv);
1188 
1189  rv = mediaList->GetGuid(guid);
1190  NS_ENSURE_SUCCESS(rv, rv);
1191 
1192  // Now add the Songbird and iTunes IDs to the map table
1193  rv = miTunesDBServices.MapID(miTunesLibID, playlistiTunesID, guid);
1194  NS_ENSURE_SUCCESS(rv, rv);
1195  }
1196 
1197  rv = ProcessPlaylistItems(mediaList,
1198  aTrackIds,
1199  aTrackIdsCount);
1200  NS_ENSURE_SUCCESS(rv, rv);
1201 
1202  StorePlaylistSignature(mediaList);
1203  }
1204  }
1205  return NS_OK;
1206 }
1207 
1208 NS_IMETHODIMP
1209 sbiTunesImporter::OnPlaylistsComplete() {
1210  /* Update status. */
1211  mStatus->Reset();
1212  char const * completionMsg;
1213  if (mImport) {
1214  completionMsg = "import_library.itunes.complete";
1215  }
1216  else {
1217  if (mFoundChanges) {
1218  completionMsg = "import_library.itunes.updating.has_changes";
1219  }
1220  else {
1221  completionMsg = "import_library.itunes.updating.no_changes";
1222  }
1223  }
1224 
1225  if (!mBatchEnded) {
1226  mLDBLibrary->ForceEndUpdateBatch();
1227  mBatchEnded = PR_TRUE;
1228  }
1229 
1230  mStatus->SetStatusText(SBLocalizedString(completionMsg));
1231 
1232  mStatus->Done();
1233  mStatus->Update();
1234 
1235 #ifdef SB_ENABLE_TEST_HARNESS
1236  if (mTimingService) {
1237  mTimingService->StopPerfTimer(mTimingIdentifier);
1238  }
1239 #endif
1240 
1241  /* If checking for changes and changes were */
1242  /* found, send a library changed event. */
1243  if (!mImport && mFoundChanges) {
1244  mListener->OnLibraryChanged(mLibraryPath, miTunesLibID);
1245  }
1246  /* If non-existent media is encountered, send an event. */
1247  if (mImport) {
1248  nsresult rv;
1250  NS_ENSURE_SUCCESS(rv, rv);
1251 
1252  // Update the previous path preference
1253  prefs.SetCharPref("lib_prev_path", NS_ConvertUTF16toUTF8(mLibraryPath));
1254 
1255  // Update the last modified time
1256  nsCOMPtr<nsILocalFile> file =
1257  do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
1258  NS_ENSURE_SUCCESS(rv, rv);
1259 
1260  rv = file->InitWithPath(mLibraryPath);
1261  NS_ENSURE_SUCCESS(rv, rv);
1262 
1263  PRInt64 lastModified;
1264  file->GetLastModifiedTime(&lastModified);
1265  sbAutoString lastModifiedStr(static_cast<PRUint64>(lastModified));
1266  prefs.SetCharPref("lib_prev_mod_time",
1267  NS_ConvertUTF16toUTF8(lastModifiedStr));
1268  if (mMissingMediaCount > 0) {
1269  mListener->OnNonExistentMedia(mMissingMediaCount,
1270  mTrackCount);
1271  }
1272 
1273  /* If unsupported media is encountered, send an event. */
1274  if (mUnsupportedMediaCount > 0) {
1275  mListener->OnUnsupportedMedia();
1276  }
1277  }
1278  return NS_OK;
1279 }
1280 
1281 NS_IMETHODIMP
1282 sbiTunesImporter::OnError(const nsAString & aErrorMessage, PRBool *_retval)
1283 {
1284  LOG(("XML Parsing error: %s\n",
1285  ::NS_LossyConvertUTF16toASCII(aErrorMessage).get()));
1286  if (mStatus.get() && !mStatus->CancelRequested()) {
1287  mListener->OnImportError();
1288  }
1289  return NS_OK;
1290 }
1291 
1298 {
1303  nsresult * rv) :
1304  mProperties(aProperties) {
1305  mChangedProperties = do_CreateInstance("@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1", rv);
1306  }
1310  bool NeedsUpdating() const
1311  {
1312  PRUint32 length = 0;
1313  // In case of error, just let length be 0
1314  mChangedProperties->GetLength(&length);
1315  return length != 0;
1316  }
1320  nsCOMPtr<sbIPropertyArray> mProperties;
1324  nsCOMPtr<sbIMutablePropertyArray> mChangedProperties;
1325 };
1326 
1327 static PLDHashOperator
1328 EnumReadFunc(nsAString const & aKey,
1329  nsString aValue,
1330  void* aUserArg) {
1331  NS_ENSURE_TRUE(aUserArg, PL_DHASH_STOP);
1332 
1334  reinterpret_cast<sbiTunesImporterEnumeratePropertiesData *>(aUserArg);
1335  nsString currentValue;
1336  data->mProperties->GetPropertyValue(aKey, currentValue);
1337  if (!aValue.Equals(currentValue)) {
1338  data->mChangedProperties->AppendProperty(aKey, aValue);
1339  }
1340  return PL_DHASH_NEXT;
1341 }
1342 
1343 nsresult sbiTunesImporter::ProcessUpdates() {
1344  nsresult rv;
1345  TrackBatch::iterator const end = mTrackBatch.end();
1346  for (TrackBatch::iterator iter = mTrackBatch.begin();
1347  iter != end;
1348  ++iter) {
1349  if (!*iter) {
1350  continue;
1351  }
1352  nsCOMPtr<nsIURI> uri;
1353  iTunesTrack * const track = *iter;
1354  nsString guid;
1355  rv = miTunesDBServices.GetSBIDFromITID(miTunesLibID, track->mTrackID, guid);
1356  if (NS_SUCCEEDED(rv) && !guid.IsEmpty()) {
1357  nsString name;
1358  track->mProperties.Get(NS_LITERAL_STRING(SB_PROPERTY_TRACKNAME), &name);
1359 
1360  mTrackIDMap.insert(TrackIDMap::value_type(track->mTrackID, guid));
1361  track->mSBGuid = guid;
1362  nsCOMPtr<sbIMediaItem> mediaItem;
1363  rv = mLibrary->GetMediaItem(guid, getter_AddRefs(mediaItem));
1364  if (NS_SUCCEEDED(rv)) {
1365  mFoundChanges = PR_TRUE;
1366  *iter = nsnull;
1367 
1368  nsCOMPtr<sbIPropertyArray> properties;
1369  rv = mediaItem->GetProperties(nsnull, getter_AddRefs(properties));
1370  if (NS_SUCCEEDED(rv)) {
1372  NS_ENSURE_SUCCESS(rv, rv);
1373  // Get the content URL and compare against the track URL. If
1374  // we fail to get it continue on, and let the downstream code
1375  // deal with it.
1376  nsString contentURL;
1377  NS_NAMED_LITERAL_STRING(CONTENT_URL, SB_PROPERTY_CONTENTURL);
1378  rv = properties->GetPropertyValue(CONTENT_URL,
1379  contentURL);
1380  if (NS_SUCCEEDED(rv)) {
1381  // Get the track URI, compare to the songbird URI, if it's changed
1382  // we need to add it into the changed property array
1383  track->GetTrackURI(GetOSType(),
1384  mIOService,
1385  miTunesLibSig,
1386  getter_AddRefs(uri));
1387  nsCOMPtr<nsIURI> fixedUri;
1388  rv = sbLibraryUtils::GetContentURI(uri, getter_AddRefs(fixedUri));
1389  NS_ENSURE_SUCCESS(rv, rv);
1390  nsCString trackCURI;
1391  rv = fixedUri->GetSpec(trackCURI);
1392  if (NS_SUCCEEDED(rv)) {
1393  nsString const & trackURI = NS_ConvertUTF8toUTF16(trackCURI);
1394  if (!trackURI.Equals(contentURL)) {
1395  data.mChangedProperties->AppendProperty(CONTENT_URL,
1396  trackURI);
1397  }
1398  }
1399  }
1400  // Enumerate the track properties and compare them to the Songbird
1401  // ones and build a property array of the ones that are different
1402  track->mProperties.EnumerateRead(EnumReadFunc, &data);
1403  if (data.NeedsUpdating()) {
1404  rv = mediaItem->SetProperties(data.mChangedProperties);
1405  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
1406  "Failed to set a property on iTunes import");
1407  }
1408  }
1409  }
1410  }
1411  }
1412  return NS_OK;
1413 }
1414 
1415 nsresult
1416 sbiTunesImporter::ProcessNewItems(
1417  TracksByID & aTrackMap,
1418  nsIArray ** aNewItems) {
1419  NS_ENSURE_ARG_POINTER(aNewItems);
1420 
1421  nsresult rv;
1422 
1423  nsCOMPtr<nsIMutableArray> uriArray =
1424  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
1425  NS_ENSURE_SUCCESS(rv, rv);
1426 
1427  nsCOMPtr<nsIMutableArray> propertyArrays =
1428  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
1429  NS_ENSURE_SUCCESS(rv, rv);
1430 
1431  nsCOMPtr<nsIURI> uri;
1432 
1433  TrackBatch::iterator const begin = mTrackBatch.begin();
1434  TrackBatch::iterator const end = mTrackBatch.end();
1435  for (TrackBatch::iterator iter = begin; iter != end; ++iter) {
1436  // Skip if null (processed above)
1437  if (*iter) {
1438  nsString name;
1439  (*iter)->mProperties.Get(NS_LITERAL_STRING(SB_PROPERTY_TRACKNAME),
1440  &name);
1441 
1442  nsString persistentID;
1443  PRBool ok =
1444  (*iter)->mProperties.Get(NS_LITERAL_STRING(SB_PROPERTY_ITUNES_GUID),
1445  &persistentID);
1446  NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
1447 
1448  aTrackMap.insert(TracksByID::value_type(persistentID, iter - begin));
1449 
1450  nsCOMPtr<nsIFile> file;
1451  // Get the location and create a URI object and add it to the array
1452  rv = (*iter)->GetTrackURI(GetOSType(),
1453  mIOService,
1454  miTunesLibSig,
1455  getter_AddRefs(uri));
1456  if (NS_SUCCEEDED(rv)) {
1457  nsCOMPtr<nsIFileURL> trackFile = do_QueryInterface(uri, &rv);
1458  PRBool trackExists = PR_FALSE;
1459  if (NS_SUCCEEDED(rv)) {
1460  rv = trackFile->GetFile(getter_AddRefs(file));
1461  if (NS_SUCCEEDED(rv)) {
1462  file->Exists(&trackExists);
1463  }
1464  else {
1465  nsCString spec;
1466  uri->GetSpec(spec);
1467  LOG(("processTrack: File protocol error %d\n", rv));
1468  LOG(("%s\n", spec.BeginReading()));
1469  }
1470  if (!trackExists) {
1471  ++mMissingMediaCount;
1472  }
1473  }
1474  // Check if the track media is supported and add result to the iTunes
1475  // library signature. This ensures the signature changes if support
1476  // for the track media is added (e.g., by installing an extension).
1477  PRBool supported = PR_FALSE;
1478  // Ignore errors, default to not supported
1479  mTypeSniffer->IsValidMediaURL(uri, &supported);
1480 
1481  if (!supported) {
1482  ++mUnsupportedMediaCount;
1483  }
1484  nsString sig(NS_LITERAL_STRING("supported"));
1485  if (supported) {
1486  sig.AppendLiteral("true");
1487  }
1488  else {
1489  sig.AppendLiteral("false");
1490  }
1491  rv = miTunesLibSig.Update(sig);
1492  if (supported) {
1493  mFoundChanges = PR_TRUE;
1494  if (file) {
1495  // Add the track content length property.
1496  PRInt64 fileSize = 0;
1497  file->GetFileSize(&fileSize);
1498  (*iter)->mProperties.Put(NS_LITERAL_STRING(SB_PROPERTY_CONTENTLENGTH),
1499  sbAutoString(static_cast<PRUint64>(fileSize)));
1500  NS_ENSURE_SUCCESS(rv, rv);
1501  }
1502  ++mTrackCount;
1503  rv = uriArray->AppendElement(uri, PR_FALSE);
1504  NS_ENSURE_SUCCESS(rv, rv);
1505 
1506  // Add the track's property array to the array
1507  nsCOMPtr<sbIPropertyArray> propertyArray;
1508  rv = (*iter)->GetPropertyArray(getter_AddRefs(propertyArray));
1509  NS_ENSURE_SUCCESS(rv, rv);
1510 
1511  rv = propertyArrays->AppendElement(propertyArray, PR_FALSE);
1512  NS_ENSURE_SUCCESS(rv, rv);
1513  }
1514  }
1515  }
1516  }
1517  PRUint32 length;
1518  rv = propertyArrays->GetLength(&length);
1519  NS_ENSURE_SUCCESS(rv, rv);
1520 
1521  if (length > 0) {
1522  // Have to do synchronous to prevent playlist creation happening while
1523  // we're still creating its items.
1524  rv = mLibrary->BatchCreateMediaItems(uriArray,
1525  propertyArrays,
1526  PR_FALSE,
1527  aNewItems);
1528  }
1529  else {
1530  *aNewItems = nsnull;
1531  }
1532  return NS_OK;
1533 }
1534 
1535 nsresult
1536 sbiTunesImporter::ProcessCreatedItems(
1537  nsIArray * aCreatedItems,
1538  TracksByID const & aTrackMap) {
1539  NS_ENSURE_ARG_POINTER(aCreatedItems);
1540 
1541  PRUint32 length;
1542  nsresult rv = aCreatedItems->GetLength(&length);
1543  NS_ENSURE_SUCCESS(rv, rv);
1544 
1545  TracksByID::const_iterator trackMapEnd = aTrackMap.end();
1546  nsCOMPtr<sbIMediaItem> mediaItem;
1547  nsCOMPtr<sbIAlbumArtListener> albumArtListener =
1549  for (PRUint32 index = 0; index < length; ++index) {
1550 
1551  mediaItem = do_QueryElementAt(aCreatedItems,
1552  index,
1553  &rv);
1554  NS_ENSURE_SUCCESS(rv, rv);
1555 
1556  nsString guid;
1557  rv = mediaItem->GetGuid(guid);
1558  NS_ENSURE_SUCCESS(rv, rv);
1559 
1560  nsString iTunesGUID;
1561  rv = mediaItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ITUNES_GUID),
1562  iTunesGUID);
1563  TracksByID::const_iterator iter = aTrackMap.find(iTunesGUID);
1564  if (iter != trackMapEnd) {
1565  mTrackBatch[iter->second]->mSBGuid = guid;
1566  mTrackIDMap.insert(TrackIDMap::value_type(
1567  mTrackBatch[iter->second]->mTrackID,
1568  guid));
1569  }
1570  mAlbumArtFetcher->SetFetcherType(sbIAlbumArtFetcherSet::TYPE_LOCAL);
1571  mAlbumArtFetcher->FetchAlbumArtForTrack(mediaItem,
1572  albumArtListener);
1573  }
1574  // batchCreateMediaItemsAsync won't return media items for duplicate tracks.
1575  // In order to get the corresponding media item for each duplicate track,
1576  // createMediaItem must be called. Thus, for each track that does not have
1577  // a corresponding media item, call createMediaItem.
1578  nsCOMPtr<nsIURI> uri;
1579  nsCOMPtr<sbIPropertyArray> propertyArray;
1580  TrackBatch::iterator const end = mTrackBatch.end();
1581  for (TrackBatch::iterator iter = mTrackBatch.begin();
1582  iter != end;
1583  ++iter) {
1584  if (!*iter) {
1585  continue;
1586  }
1587  iTunesTrack * const track = *iter;
1588  // This can be null if it's an update
1589  if (track->mSBGuid.IsEmpty()) {
1590  rv = track->GetTrackURI(GetOSType(),
1591  mIOService,
1592  miTunesLibSig,
1593  getter_AddRefs(uri));
1594  // If we can't get the URI then just skip it
1595  if (rv == NS_ERROR_NOT_AVAILABLE) {
1596  continue;
1597  }
1598  NS_ENSURE_SUCCESS(rv, rv);
1599 
1600  rv = track->GetPropertyArray(getter_AddRefs(propertyArray));
1601  NS_ENSURE_SUCCESS(rv, rv);
1602 
1603  rv = mLibrary->CreateMediaItem(uri,
1604  propertyArray,
1605  PR_FALSE,
1606  getter_AddRefs(mediaItem));
1607  NS_ENSURE_SUCCESS(rv, rv);
1608 
1609  rv = mediaItem->GetGuid(track->mSBGuid);
1610  NS_ENSURE_SUCCESS(rv, rv);
1611  mTrackIDMap.insert(TrackIDMap::value_type(track->mTrackID,
1612  track->mSBGuid));
1613  }
1614  if (!track->mSBGuid.IsEmpty()) {
1615  rv = miTunesDBServices.MapID(miTunesLibID,
1616  track->mTrackID,
1617  track->mSBGuid);
1618  NS_ENSURE_SUCCESS(rv, rv);
1619  }
1620  }
1621  return NS_OK;
1622 }
1623 
1624 inline
1625 void sbiTunesImporter::DestructiTunesTrack(iTunesTrack * aTrack) {
1626  delete aTrack;
1627 }
1628 
1629 nsresult sbiTunesImporter::ProcessTrackBatch() {
1630  nsresult rv;
1631 
1632  rv = ProcessUpdates();
1633  NS_ENSURE_SUCCESS(rv, rv);
1634 
1635  nsCOMPtr<nsIArray> newItems;
1636  TracksByID trackMap;
1637  rv = ProcessNewItems(trackMap,
1638  getter_AddRefs(newItems));
1639  NS_ENSURE_SUCCESS(rv, rv);
1640 
1641  // Only process created items if any new items where added.
1642  if (newItems) {
1643  rv = ProcessCreatedItems(newItems, trackMap);
1644  NS_ENSURE_SUCCESS(rv, rv);
1645 
1646  std::for_each(mTrackBatch.begin(), mTrackBatch.end(), DestructiTunesTrack);
1647  }
1648 
1649  mTrackBatch.clear();
1650  return NS_OK;
1651 }
1652 
1653 sbiTunesImporter::iTunesTrack::iTunesTrack() {
1654  MOZ_COUNT_CTOR(iTunesTrack);
1655 }
1656 sbiTunesImporter::iTunesTrack::~iTunesTrack() {
1657  MOZ_COUNT_DTOR(iTunesTrack);
1658 }
1659 
1660 nsresult
1661 sbiTunesImporter::iTunesTrack::Initialize(sbIStringMap * aProperties) {
1662  NS_ENSURE_ARG_POINTER(aProperties);
1663 
1664  nsresult rv = aProperties->Get(NS_LITERAL_STRING("Track ID"), mTrackID);
1665  NS_ENSURE_SUCCESS(rv, rv);
1666 
1667  PRBool ok = mProperties.Init(32);
1668  NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
1669 
1670  NS_NAMED_LITERAL_STRING(location, "Location");
1671  nsString URI;
1672 
1673  rv = aProperties->Get(location, URI);
1674  NS_ENSURE_SUCCESS(rv, rv);
1675 
1676  rv = mProperties.Put(location, URI);
1677  NS_ENSURE_SUCCESS(rv, rv);
1678 
1679  for (unsigned int index = 0; index < NS_ARRAY_LENGTH(gPropertyMap); ++index) {
1680  PropertyMap const & propertyMapEntry = gPropertyMap[index];
1681  nsString value;
1682  rv = aProperties->Get(NS_ConvertASCIItoUTF16(propertyMapEntry.ITProperty),
1683  value);
1684  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "sbIStringMap::Get failed");
1685  if (!value.IsVoid()) {
1686  if (propertyMapEntry.mConversion) {
1687  value = propertyMapEntry.mConversion(value);
1688  }
1689  mProperties.Put(NS_ConvertASCIItoUTF16(propertyMapEntry.SBProperty),
1690  value);
1691  }
1692  }
1693 
1694  mProperties.Put(NS_LITERAL_STRING(SB_PROPERTY_CONTENTTYPE),
1695  GetContentType(aProperties));
1696 
1697  return NS_OK;
1698 }
1699 
1700 nsString
1701 sbiTunesImporter::iTunesTrack::GetContentType(sbIStringMap * aProperties)
1702 {
1703  nsresult rv;
1704  nsString result;
1705 
1706  nsString podcastValue;
1707  rv = aProperties->Get(NS_LITERAL_STRING("Podcast"), podcastValue);
1708  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "sbIStringMap::Get failed");
1709 
1710  if (NS_SUCCEEDED(rv) && podcastValue.EqualsLiteral("true")) {
1711  result = NS_LITERAL_STRING("podcast");
1712  }
1713  else {
1714  nsString hasVideo;
1715  rv = aProperties->Get(NS_LITERAL_STRING("Has Video"), hasVideo);
1716  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "sbIStringMap::Get failed");
1717 
1718  if (NS_SUCCEEDED(rv) && hasVideo.EqualsLiteral("true")) {
1719  result = NS_LITERAL_STRING("video");
1720  }
1721  else {
1722  result = NS_LITERAL_STRING("audio");
1723  }
1724  }
1725 
1726  return result;
1727 }
1728 
1729 static PLDHashOperator
1730 ConvertToPropertyArray(nsAString const & aKey,
1731  nsString aValue,
1732  void * aUserArg) {
1733  NS_ENSURE_TRUE(aUserArg, PL_DHASH_STOP);
1734 
1736  reinterpret_cast<sbIMutablePropertyArray*>(aUserArg);
1737  nsresult rv = array->AppendProperty(aKey, aValue);
1738  NS_ENSURE_SUCCESS(rv, ::PL_DHASH_STOP);
1739  return PL_DHASH_NEXT;
1740 }
1741 
1742 nsresult
1743 sbiTunesImporter::iTunesTrack::GetPropertyArray(
1744  sbIPropertyArray ** aPropertyArray) {
1745  NS_ENSURE_ARG_POINTER(aPropertyArray);
1746 
1747  nsresult rv;
1748  nsCOMPtr<sbIMutablePropertyArray> array =
1749  do_CreateInstance("@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1",
1750  &rv);
1751  NS_ENSURE_SUCCESS(rv, rv);
1752 
1753  mProperties.EnumerateRead(ConvertToPropertyArray, array.get());
1754  nsCOMPtr<sbIPropertyArray> propertyArray = do_QueryInterface(array);
1755  propertyArray.forget(aPropertyArray);
1756  return NS_OK;
1757 }
1758 
1760 nsresult
1761 sbiTunesImporter::iTunesTrack::GetTrackURI(
1762  sbiTunesImporter::OSType aOSType,
1763  nsIIOService * aIOService,
1764  sbiTunesSignature & aSignature,
1765  nsIURI ** aTrackURI) {
1766  NS_ENSURE_ARG_POINTER(aIOService);
1767  NS_ENSURE_ARG_POINTER(aTrackURI);
1768 
1769  if (mURI) {
1770  *aTrackURI = mURI.get();
1771  NS_ADDREF(*aTrackURI);
1772  return NS_OK;
1773  }
1774  nsString uri16;
1775  if (!mProperties.Get(NS_LITERAL_STRING("Location"), &uri16)) {
1776  return NS_ERROR_NOT_AVAILABLE;
1777  }
1778 
1779  if (uri16.IsEmpty()) {
1780  return NS_ERROR_NOT_AVAILABLE;
1781  }
1782  // Convert to UTF8
1783  nsCString uri = NS_ConvertUTF16toUTF8(uri16);
1784 
1785  nsCString adjustedURI;
1786 
1787  if (uri[uri.Length() - 1] == '/') {
1788  uri.Cut(uri.Length() -1, 1);
1789  }
1790 
1791  if (uri.Find("file://localhost//", CaseInsensitiveCompare) == 0) {
1792  adjustedURI.AssignLiteral("file://///");
1793  uri.Cut(0, 18);
1794  }
1795  else if (uri.Find("file://localhost/", CaseInsensitiveCompare) == 0) {
1796  adjustedURI.AssignLiteral("file:///");
1797  uri.Cut(0,17);
1798  }
1799  else {
1800  char const c = uri[0];
1801  if (uri.Length() > 3 &&
1802  ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) &&
1803  uri[1] == ':' &&
1804  uri[2] == '/') {
1805  adjustedURI = "file:///";
1806  uri.Cut(0, 3);
1807  }
1808  else {
1809  adjustedURI = "file:////";
1810  }
1811  }
1812 
1813  adjustedURI.Append(uri);
1814 
1815  // Convert to lower case on Windows since it's case-insensitive.
1816  if (aOSType == WINDOWS_OS) {
1817  ToLowerCase(adjustedURI);
1818  }
1819 
1820  nsString locationSig;
1821  locationSig.AssignLiteral("Location");
1822  locationSig.AppendLiteral(adjustedURI.BeginReading());
1823  // Add file location to iTunes library signature.
1824  nsresult rv = aSignature.Update(locationSig);
1825  NS_ENSURE_SUCCESS(rv, rv);
1826 
1827  // Get the track URI.
1828  rv = aIOService->NewURI(adjustedURI, nsnull, nsnull, getter_AddRefs(mURI));
1829  NS_ENSURE_SUCCESS(rv, rv);
1830 
1831  *aTrackURI = mURI.get();
1832  NS_ADDREF(*aTrackURI);
1833 
1834  return NS_OK;
1835 }
1836 
1837 /* void onDataAvailable (in nsIRequest aRequest, in nsISupports aContext, in nsIInputStream aInputStream, in unsigned long aOffset, in unsigned long aCount); */
1838 NS_IMETHODIMP
1839 sbiTunesImporter::OnProgress(PRInt64 aBytesResad)
1840 {
1841  mBytesRead = aBytesResad;
1842  return NS_OK;
1843 }
#define TRACE(args)
static nsresult GetItemsByProperty(sbIMediaList *aMediaList, nsAString const &aPropertyName, nsAString const &aValue, nsCOMArray< sbIMediaItem > &aMediaItems)
#define SB_PROPERTY_TOTALDISCS
return NS_OK
Interface for importing external libraries.
nsString ConvertDuration(nsAString const &aDuration)
#define SB_PROPERTY_MEDIALISTNAME
#define LOG(args)
#define SB_PROPERTY_PLAYCOUNT
nsCString GetCharPref(const char *aKey, const nsCString &aDefault)
Definition: sbPrefBranch.h:148
nsresult sbOpenInputStream(nsAString const &aPath, nsIInputStream **aStream)
Definition: sbFileUtils.cpp:47
#define SB_PROPERTY_SAMPLERATE
nsCOMPtr< nsIArray > mProperties
dataSBHighestRatedArtists SBProperties rating
Definition: tuner2.js:867
menuItem id
Definition: FeedWriter.js:971
onPageChanged aValue
Definition: FeedWriter.js:1395
nsCOMPtr< sbIMutablePropertyArray > mChangedProperties
inArray array
nsCOMPtr< sbIPropertyArray > mProperties
#define SB_PROPERTY_ALBUMARTISTNAME
Generic interface for exposing long running jobs to the UI.
#define SB_PROPERTY_RATING
static PRBool IsSongbirdFolder(sbIStringMap *aProperties)
ValueConversion mConversion
const NS_ERROR_ABORT
nsresult StoreSignature(nsAString const &aID, nsAString const &aSignature)
#define SB_MUTABLEPROPERTYARRAY_CONTRACTID
nsresult Update(nsAString const &aStringData)
#define NS_LOCALFILE_CONTRACTID
static nsresult GetContentURI(nsIURI *aURI, nsIURI **_retval, nsIIOService *aIOService=nsnull)
Return a library content URI for the URI specified by aURI. A library content URI is a specially form...
#define SB_PROPERTY_LASTPLAYTIME
#define SB_PROPERTY_TOTALTRACKS
A brief description of the contents of this interface.
nsresult GetSBIDFromITID(nsAString const &aiTunesLibID, nsAString const &aiiTunesID, nsAString &aSongbirdID)
nsresult RetrieveSignature(nsAString const &aID, nsAString &aSignature)
#define SB_PROPERTY_BITRATE
static nsresult FindPlaylistByName(sbILibrary *aLibrary, nsAString const &aPlaylistName, sbIMediaList **aMediaList)
#define SB_PROPERTY_CONTENTLENGTH
static nsresult StorePlaylistSignature(sbIMediaList *aMediaList)
char const * ITProperty
An interface to carry around arrays of nsIProperty instances Note that implementations of the interfa...
const unsigned long TYPE_LOCAL
static PLDHashOperator EnumReadFunc(nsAString const &aKey, nsString aValue, void *aUserArg)
_hideDatepicker duration
#define SB_PROPERTY_GENRE
#define SB_PROPERTY_CONTENTTYPE
NS_IMPL_ISUPPORTS2(sbiTunesImporter, sbILibraryImporter, sbIiTunesXMLParserListener) sbiTunesImporter
static PLDHashOperator ConvertToPropertyArray(nsAString const &aKey, nsString aValue, void *aUserArg)
#define SB_PROPERTY_DURATION
NS_DECL_ISUPPORTS NS_DECL_SBIJOBPROGRESS static NS_DECL_SBIJOBCANCELABLE sbiTunesImporterJob * New()
#define SB_PROPERTY_DISCNUMBER
NS_DECL_ISUPPORTS static NS_DECL_SBIALBUMARTLISTENER sbiTunesImporterAlbumArtListener * New()
char const * SBProperty
static sbiTunesImporterStatus * New(sbiTunesImporterJob *aJobProgress)
nsresult GetSignature(nsAString &aSignature)
NS_DECL_ISUPPORTS NS_DECL_SBIITUNESXMLPARSER NS_DECL_NSISAXCONTENTHANDLER static NS_DECL_NSISAXERRORHANDLER sbiTunesXMLParser * New()
void nsCString_Split(const nsACString &aString, const nsACString &aDelimiter, nsTArray< nsCString > &aSubStringArray)
nsString ConvertRating(nsAString const &aRating)
sbiTunesImporterEnumeratePropertiesData(sbIPropertyArray *aProperties, nsresult *rv)
Media library abstraction.
Definition: sbILibrary.idl:82
#define SB_PROPERTY_ARTISTNAME
nsString ConvertDateTime(nsAString const &aDateTime)
static nsresult ComputePlaylistSignature(sbiTunesSignature &aSignature, sbIMediaList *aMediaList)
#define SB_PROPERTY_BPM
Interface for listening to library importer events.
var uri
Definition: FeedWriter.js:1135
var prefs
Definition: FeedWriter.js:1169
countRef value
Definition: FeedWriter.js:1423
#define SB_PROPERTY_ALBUMNAME
#define SB_PROPERTY_COMPOSERNAME
PRInt64 nsString_ToInt64(const nsAString &str, nsresult *rv)
static nsresult AddItemsToPlaylist(sbIMediaList *aMediaList, nsIMutableArray *aItems)
char const SB_ITUNES_LIBRARY_IMPORTER_PREF_PREFIX[]
#define SB_PROPERTY_YEAR
#define SB_PROPERTY_LASTSKIPTIME
nsresult GetMainLibrary(sbILibrary **aMainLibrary)
Interface that defines a single item of media in the system.
#define SB_PROPERTY_TRACKNAME
#define SB_PROPERTY_COMMENT
static nsresult IsPlaylistDirty(sbIMediaList *aMediaList, PRBool &aIsDirty)
function msg
nsString(* ValueConversion)(nsAString const &aValue)
#define SB_PROPERTY_CONTENTURL
observe data
Definition: FeedWriter.js:1329
An interface to carry around arrays of nsIProperty instances. Users of this interface should only QI ...
#define SB_PROPERTY_ITUNES_GUID
#define SB_PROPERTY_SKIPCOUNT
nsresult MapID(nsAString const &aiTunesLibID, nsAString const &aiTunesID, nsAString const &aSongbirdID)
PropertyMap gPropertyMap[]
char const SB_ITUNES_LIBRARY_IMPORT_PREF_PREFIX[]
var file
#define SB_PROPERTY_TRACKNUMBER