MetadataHandlerTaglib.cpp
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 /*******************************************************************************
26  *******************************************************************************
27  *
28  * Taglib metadata handler.
29  *
30  *******************************************************************************
31  ******************************************************************************/
32 
38 /*******************************************************************************
39  *
40  * Taglib metadata handler imported services.
41  *
42  ******************************************************************************/
43 
44 /* Local file imports. */
45 #include "MetadataHandlerTaglib.h"
46 #include "tagunion.h" /* a taglib import, but internal use only...
47  here until taglib2 where it won't be necessary*/
48 
49 /* Local module imports. */
50 #include "TaglibChannelFileIO.h"
51 
52 /* Songbird utility classes */
53 #include "sbStringUtils.h"
54 #include "sbMemoryUtils.h"
55 #include <sbILibraryUtils.h>
57 
58 /* Songbird interfaces */
59 #include "sbStandardProperties.h"
60 #include "sbIPropertyArray.h"
61 #include "sbPropertiesCID.h"
62 
63 /* Mozilla imports. */
64 #include <nsIURI.h>
65 #include <nsComponentManagerUtils.h>
66 #include <nsIFile.h>
67 #include <nsIMIMEService.h>
68 #include <nsIBinaryInputStream.h>
69 #include <nsIIOService.h>
70 #include <nsIProtocolHandler.h>
71 #include <nsIStandardURL.h>
72 #include <nsICharsetDetector.h>
73 #include <nsICharsetConverterManager.h>
74 #include <nsIUnicodeDecoder.h>
75 #include <nsIContentSniffer.h>
76 #include <nsNetUtil.h>
77 #include <nsServiceManagerUtils.h>
78 #include <nsStringGlue.h>
79 #include <prlog.h>
80 #include <prprf.h>
81 #include <nsUnicharUtils.h>
82 #include <nsMemory.h>
83 
84 /* TagLib imports */
85 #include <flacfile.h>
86 #include <oggflacfile.h>
87 #include <mpcfile.h>
88 #include <mpegfile.h>
89 #include <urllinkframe.h>
90 #include <mp4file.h>
91 #include <asffile.h>
92 #include <vorbisfile.h>
93 #include <uniquefileidentifierframe.h>
94 #include <textidentificationframe.h>
95 #include <tpropertymap.h>
96 
97 /* C++ std imports. */
98 #include <sstream>
99 
100 /* Windows Specific */
101 #if defined(XP_WIN)
102  #include <windows.h>
103 #endif
104 
105 #define WRITE_PROPERTY(tmp_result, SB_PROPERTY, taglibName) \
106  PR_BEGIN_MACRO \
107  tmp_result = mpMetadataPropertyArray->GetPropertyValue( \
108  NS_LITERAL_STRING(SB_PROPERTY), propertyValue); \
109  if (NS_SUCCEEDED(tmp_result)) { \
110  TagLib::String key = TagLib::String(taglibName, \
111  TagLib::String::UTF8); \
112  TagLib::String value = TagLib::String( \
113  NS_ConvertUTF16toUTF8(propertyValue).BeginReading(), \
114  TagLib::String::UTF8); \
115  properties->erase(key); \
116  if (!value.isEmpty()) { \
117  TagLib::StringList valueList = \
118  TagLib::StringList(value); \
119  properties->insert(key, valueList); \
120  } \
121  } \
122  PR_END_MACRO
123 #define GET_PROPERTY(taglibid) \
124  properties[TagLib::String(taglibid)].toString(", ")
125 #define TAGLIB1_PROPERTIES_WORKAROUND(pTag) \
126  PR_BEGIN_MACRO \
127  if (dynamic_cast<TagLib::APE::Tag*>(pTag)){ \
128  properties.merge(dynamic_cast<TagLib::APE::Tag*>(pTag) \
129  ->properties()); \
130  } \
131  if (dynamic_cast<TagLib::ASF::Tag*>(pTag)){ \
132  properties.merge(dynamic_cast<TagLib::ASF::Tag*>(pTag) \
133  ->properties()); \
134  } \
135  if (dynamic_cast<TagLib::ID3v1::Tag*>(pTag)){ \
136  properties.merge(dynamic_cast<TagLib::ID3v1::Tag*>(pTag)\
137  ->properties()); \
138  } \
139  if (dynamic_cast<TagLib::ID3v2::Tag*>(pTag)){ \
140  properties.merge(dynamic_cast<TagLib::ID3v2::Tag*>(pTag)\
141  ->properties()); \
142  } \
143  if (dynamic_cast<TagLib::MP4::Tag*>(pTag)){ \
144  properties.merge(dynamic_cast<TagLib::MP4::Tag*>(pTag) \
145  ->properties()); \
146  } \
147  if (dynamic_cast<TagLib::Ogg::XiphComment*>(pTag)){ \
148  properties.merge( \
149  dynamic_cast<TagLib::Ogg::XiphComment*>(pTag) \
150  ->properties()); \
151  } \
152  PR_END_MACRO
153 
154 // Property namespace for Gracenote properties
155 // Note that this must match those used in sbGracenoteDefines.h, so
156 // be sure to change those if you change these.
157 #define SB_GN_PROP_EXTENDEDDATA "http://gracenote.com/pos/1.0#extendedData"
158 #define SB_GN_PROP_TAGID "http://gracenote.com/pos/1.0#tagId"
159 
160 // Define image maximum sizes for the tags
161 #define MAX_MPEG_IMAGE_SIZE 16777216 // MPEG (ID3v2)
162 
163 /*******************************************************************************
164  *
165  * Taglib metadata handler logging services.
166  *
167  ******************************************************************************/
168 
169 #ifdef PR_LOGGING
170 static PRLogModuleInfo* gLog = nsnull;
171 #define LOG(args) PR_BEGIN_MACRO \
172  if (!gLog) { \
173  gLog = PR_NewLogModule("sbMetadataHandlerTaglib"); \
174  } \
175  PR_LOG(gLog, PR_LOG_DEBUG, args); \
176  PR_END_MACRO
177 #else
178 #define LOG(args) /* nothing */
179 #endif
180 
181 
182 // the minimum number of chracters to feed into the charset detector
183 #define GUESS_CHARSET_MIN_CHAR_COUNT 256
184 
185 PRLock* sbMetadataHandlerTaglib::sTaglibLock = nsnull;
186 
187 /*
188  *
189  * Taglib metadata handler nsISupports implementation.
190  *
191  ******************************************************************************/
192 
196  nsICharsetDetectionObserver)
197 
198 
199 /*******************************************************************************
200  *
201  * Taglib metadata handler sbIMetadataHandler implementation.
202  *
203  ******************************************************************************/
204 
205 NS_IMETHODIMP sbMetadataHandlerTaglib::GetContractID(nsACString &aContractID)
206 {
207  aContractID.AssignLiteral(SONGBIRD_METADATAHANDLERTAGLIB_CONTRACTID);
208  return NS_OK;
209 }
210 
228 NS_IMETHODIMP sbMetadataHandlerTaglib::Vote(
229  const nsAString &url,
230  PRInt32 *pVote)
231 {
232  nsString _url(url);
233  PRInt32 vote = 0;
234 
235  /* Convert the URL to lower case. */
236  ToLowerCase(_url);
237 
238  /* Check for supported files. */
239  if ( (_url.Find(".flac", PR_TRUE) != -1)
240  || (_url.Find(".mpc", PR_TRUE) != -1)
241  || (_url.Find(".mp3", PR_TRUE) != -1)
242  || (_url.Find(".m4a", PR_TRUE) != -1)
243  || (_url.Find(".m4p", PR_TRUE) != -1)
244  || (_url.Find(".m4v", PR_TRUE) != -1)
245  || (_url.Find(".m4r", PR_TRUE) != -1)
246  || (_url.Find(".aac", PR_TRUE) != -1)
247  || (_url.Find(".mp4", PR_TRUE) != -1)
248  || (_url.Find(".wv", PR_TRUE) != -1)
249  || (_url.Find(".spx", PR_TRUE) != -1)
250  || (_url.Find(".tta", PR_TRUE) != -1)
251  || (_url.Find(".oga", PR_TRUE) != -1)
252  || (_url.Find(".ogm", PR_TRUE) != -1)
253  || (_url.Find(".ogg", PR_TRUE) != -1)
254  || (_url.Find(".wma", PR_TRUE) != -1)
255  || (_url.Find(".wmv", PR_TRUE) != -1))
256  {
257  vote = 100;
258  }
259 
260  /* Check for unsupported files. */
261  else if ( (_url.Find(".avi", PR_TRUE) != -1)
262  || (_url.Find(".wav", PR_TRUE) != -1))
263  {
264  vote = -1;
265  }
266 
267  /* we can only handle things with mozilla protocol handlers */
268  if (vote >= 0) {
269  nsresult rv;
270  nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
271  NS_ENSURE_SUCCESS(rv, rv);
272  nsCOMPtr<nsIProtocolHandler> protocolHandler;
273  nsCString scheme;
274 
275  rv = ios->ExtractScheme(NS_ConvertUTF16toUTF8(_url), scheme);
276  if (NS_SUCCEEDED(rv)) {
277  rv = ios->GetProtocolHandler(scheme.BeginReading(),
278  getter_AddRefs(protocolHandler));
279  }
280  if (NS_FAILED(rv)) {
281  // can't deal with the url
282  LOG(("sbMetadataHandlerTaglib::Vote - can't handle scheme %s\n",
283  scheme.BeginReading()));
284  vote = -1;
285  }
286  }
287 
288  /* Return results. */
289  *pVote = vote;
290  LOG(("sbMetadataHandlerTaglib::Vote - voting to %d\n", vote));
291 
292  return (NS_OK);
293 }
294 
295 NS_IMETHODIMP
296 sbMetadataHandlerTaglib::GetRequiresMainThread(PRBool *_retval)
297 {
298  NS_ENSURE_ARG_POINTER(_retval);
299  NS_ENSURE_STATE(mpChannel);
300  nsresult rv;
301 
302  nsCOMPtr<nsIURI> uri;
303  rv = mpChannel->GetURI(getter_AddRefs(uri));
304  NS_ENSURE_SUCCESS(rv, rv);
305 
306  PRBool isFileURI = PR_FALSE;
307  rv = uri->SchemeIs( "file" , &isFileURI );
308  NS_ENSURE_SUCCESS(rv, rv);
309 
310  // Taglib uses the nsIChannel for all protocols
311  // other than file:// and cannot be run off
312  // of the main thread in these cases.
313 
314  *_retval = !isFileURI;
315  return NS_OK;
316 }
317 
318 
329 NS_IMETHODIMP sbMetadataHandlerTaglib::Read(
330  PRInt32 *pReadCount)
331 {
332  nsresult rv = NS_ERROR_FAILURE;
333  nsAutoLock lock(sTaglibLock);
334 
335  // Attempt to avoid crashes. This may only work on windows.
336  try {
337  rv = ReadInternal(pReadCount);
338  } catch(...) {
339  NS_ERROR("sbMetadataHandlerTaglib::Read caught an exception!");
340  rv = NS_ERROR_FAILURE;
341  }
342 
343  return rv;
344 }
345 
346 nsresult sbMetadataHandlerTaglib::ReadInternal(
347  PRInt32 *pReadCount)
348 {
349  nsCOMPtr<nsIStandardURL> pStandardURL;
350  nsCOMPtr<nsIURI> pURI;
351  nsCOMPtr<nsIFile> pFile;
352  nsCString urlSpec;
353  nsCString urlScheme;
354  nsAutoString filePath;
355  PRUint32 unsignedReadCount = 0;
356  PRInt32 readCount = 0;
357  nsresult result = NS_OK;
358 
359  // Starting a new operation, so clear the completion flag
360  mCompleted = PR_FALSE;
361 
362  /* Get the TagLib sbISeekableChannel file IO manager. */
363  mpTagLibChannelFileIOManager =
364  do_GetService
365  ("@songbirdnest.com/Songbird/sbTagLibChannelFileIOManager;1",
366  &result);
367 
368  /* Initialize the metadata values. */
369  if (NS_SUCCEEDED(result))
370  {
371  mpMetadataPropertyArray =
372  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &result);
373 
374  result = mpMetadataPropertyArray->SetStrict(PR_FALSE);
375  NS_ENSURE_SUCCESS(result, result);
376  }
377 
378  /* Get the channel URL info. */
379  if (!mpURL) {
380  result = NS_ERROR_NOT_INITIALIZED;
381  }
382  if (NS_SUCCEEDED(result))
383  result = mpURL->GetSpec(urlSpec);
384  if (NS_SUCCEEDED(result))
385  result = mpURL->GetScheme(urlScheme);
386 
387  LOG(("sbMetadataHandlerTaglib::ReadInternal - spec is %s\n",
388  urlSpec.BeginReading()));
389 
390  /* If the channel URL scheme is for a local file, try reading */
391  /* synchronously. If successful, metadata read operation will */
392  /* be completed. */
393  if (urlScheme.Equals(NS_LITERAL_CSTRING("file")))
394  {
395  /* Get the metadata local file path. */
396  if (NS_SUCCEEDED(result))
397  {
398  PRBool useSpec = PR_TRUE;
399  #if XP_UNIX && !XP_MACOSX
400  if (StringBeginsWith(urlSpec, NS_LITERAL_CSTRING("file://"))) {
401  nsCString path(Substring(urlSpec, NS_ARRAY_LENGTH("file://") - 1));
402  LOG(("looking at path %s\n", path.get()));
403  do { /* allow breaking out gracefully */
404  nsCOMPtr<nsILocalFile> localFile =
405  do_CreateInstance("@mozilla.org/file/local;1", &result);
406  if (NS_FAILED(result) || !localFile)
407  break;
408  nsCOMPtr<nsINetUtil> netUtil =
409  do_CreateInstance("@mozilla.org/network/util;1", &result);
410  if (NS_FAILED(result)) {
411  LOG(("failed to get netutil\n"));
412  break;
413  }
414  nsCString unescapedPath;
415  result = netUtil->UnescapeString(path, 0, unescapedPath);
416  if (NS_FAILED(result)) {
417  LOG(("failed to unescape path\n"));
418  break;
419  }
420  result = localFile->SetPersistentDescriptor(unescapedPath);
421  if (NS_FAILED(result)) {
422  LOG(("failed to set persistent descriptor %s\n", unescapedPath.get()));
423  break;
424  }
425  #if PR_LOGGING
426  result = localFile->GetPersistentDescriptor(path);
427  if (NS_SUCCEEDED(result)) {
428  LOG(("file path is %s\n", path.get()));
429  }
430  #endif
431  PRBool fileExists = PR_FALSE;
432  result = localFile->Exists(&fileExists);
433  if (NS_FAILED(result) || !fileExists) {
434  LOG(("file does not exist, falling back"));
435  break;
436  }
437  pFile = do_QueryInterface(localFile, &result);
438  if (NS_SUCCEEDED(result) && pFile)
439  useSpec = PR_FALSE;
440  } while (0);
441  }
442  #endif /* XP_UNIX && !XP_MACOSX */
443  if (useSpec)
444  result = mpFileProtocolHandler->GetFileFromURLSpec
445  (urlSpec,
446  getter_AddRefs(pFile));
447  }
448 
449  if (NS_SUCCEEDED(result)) {
450  #if XP_UNIX && !XP_MACOSX
451  result = pFile->GetNativePath(mMetadataPath);
452  #else
453  nsString metadataPathU16;
454  result = pFile->GetPath(metadataPathU16);
455  if (NS_SUCCEEDED(result)) {
456  CopyUTF16toUTF8(metadataPathU16, mMetadataPath);
457  }
458  #endif
459  }
460  LOG(("using metadata path %s", mMetadataPath.get()));
461 
462  /* Read the metadata. */
463  if (NS_SUCCEEDED(result)) {
464  result = ReadMetadata();
465  // If the read fails, that's it, nothing else to do
466  if (NS_FAILED(result)) {
467  CompleteRead();
468  }
469  }
470  }
471 
472  /* If metadata reading is not complete, start an */
473  /* asynchronous read with the metadata channel. */
474  if (NS_SUCCEEDED(result) && !mCompleted)
475  {
476  /* Create a metadata channel. */
477  mpSeekableChannel =
478  do_CreateInstance("@songbirdnest.com/Songbird/SeekableChannel;1",
479  &result);
480 
481  /* Add the metadata channel for use with the */
482  /* TagLib nsIChannel file I/O services. */
483  if (NS_SUCCEEDED(result))
484  {
485  /* Allocate a metadata channel ID */
486  /* and use it for the metadata path. */
487  PR_AtomicIncrement((PRInt32 *) &sNextChannelID);
488  mMetadataPath.AssignLiteral("metadata_channel://");
489  mMetadataPath.AppendInt(sNextChannelID);
490  mMetadataChannelID = mMetadataPath;
491 
492  /* Add the metadata channel. */
493  result = mpTagLibChannelFileIOManager->AddChannel
494  (mMetadataChannelID,
495  mpSeekableChannel);
496  }
497 
498  /* Open the metadata channel to start reading. */
499  if (NS_SUCCEEDED(result))
500  mpSeekableChannel->Open(mpChannel, this);
501 
502  /* Indicate that reading is being done asynchronously. */
503  if (NS_SUCCEEDED(result))
504  readCount = -1;
505  }
506 
507  /* If the read operation is complete, get the number of read tags. */
508  if (NS_SUCCEEDED(result) && mCompleted) {
509  result = mpMetadataPropertyArray->GetLength(&unsignedReadCount);
510  readCount = (PRInt32)unsignedReadCount; // (sigh)
511  }
512 
513  /* Complete read operation on error. */
514  if (!NS_SUCCEEDED(result))
515  {
516  CompleteRead();
517  readCount = 0;
518  }
519 
520  /* Return results. */
521  *pReadCount = readCount;
522 
523  return result;
524 }
525 
526 
542 NS_IMETHODIMP sbMetadataHandlerTaglib::Write(
543  PRInt32 *pWriteCount)
544 {
545  nsresult rv = NS_ERROR_FAILURE;
546  nsAutoLock lock(sTaglibLock);
547  // Attempt to avoid crashes. This may only work on windows.
548  try {
549  rv = WriteInternal(pWriteCount);
550  } catch(...) {
551  NS_ERROR("sbMetadataHandlerTaglib::Write caught an exception!");
552  }
553 
554  return rv;
555 }
556 
557 nsresult sbMetadataHandlerTaglib::WriteInternal(
558  PRInt32 *pWriteCount)
559 {
560  nsCOMPtr<nsIStandardURL> pStandardURL;
561  nsCOMPtr<nsIURI> pURI;
562  nsCOMPtr<nsIFile> pFile;
563  nsCString urlSpec;
564  nsCString urlScheme;
565  nsAutoString filePath;
566  nsresult result = NS_OK;
567  nsresult tmp_result;
568 
569  // Starting a new operation, so clear the completion flag
570  mCompleted = PR_FALSE;
571 
572  // Must ensure metadata is set before writing
573  NS_ENSURE_TRUE(mpMetadataPropertyArray, NS_ERROR_NOT_INITIALIZED);
574 
575  /* Get the TagLib sbISeekableChannel file IO manager. */
576  mpTagLibChannelFileIOManager =
577  do_GetService
578  ("@songbirdnest.com/Songbird/sbTagLibChannelFileIOManager;1",
579  &result);
580 
581  /* Get the channel URL info. */
582  NS_ENSURE_STATE(mpURL);
583  if (NS_SUCCEEDED(result))
584  result = mpURL->GetSpec(urlSpec);
585  if (NS_SUCCEEDED(result))
586  result = mpURL->GetScheme(urlScheme);
587 
588  if (!urlScheme.EqualsLiteral("file"))
589  {
590  LOG(("%s: can't write to scheme %s", __FUNCTION__, urlScheme.get()));
591  return NS_ERROR_NOT_IMPLEMENTED;
592  }
593 
594  /* Get the metadata local file path. */
595  if (NS_SUCCEEDED(result))
596  {
597  result = mpFileProtocolHandler->GetFileFromURLSpec(
598  urlSpec,
599  getter_AddRefs(pFile)
600  );
601  }
602 
603  if (NS_SUCCEEDED(result)) {
604  #if XP_UNIX && !XP_MACOSX
605  result = pFile->GetNativePath(mMetadataPath);
606  #else
607  nsCOMPtr<sbILibraryUtils> libUtils =
608  do_GetService("@songbirdnest.com/Songbird/library/Manager;1", &result);
609  NS_ENSURE_SUCCESS(result, result);
610  nsCOMPtr<nsIFile> canonicalFile;
611  result = libUtils->GetCanonicalPath(pFile,
612  getter_AddRefs(canonicalFile));
613  NS_ENSURE_SUCCESS(result, result);
614 
615  canonicalFile.forget(getter_AddRefs(pFile));
616 
617  nsString metadataPathU16;
618  result = pFile->GetPath(metadataPathU16);
619  if (NS_SUCCEEDED(result)) {
620  CopyUTF16toUTF8(metadataPathU16, mMetadataPath);
621  }
622  #endif
623  }
624 
625  nsCString fileExt;
626  result = mpURL->GetFileExtension(fileExt);
627  NS_ENSURE_SUCCESS(result, result);
628  ToLowerCase(fileExt);
629 
630  // Ensure the file is not a video file. (Ultimately we would like
631  // to support writing video metadata, but not for LZ.)
632  if (fileExt.Equals(NS_LITERAL_CSTRING("m4v")) ||
633  fileExt.Equals(NS_LITERAL_CSTRING("mp4")) ||
634  fileExt.Equals(NS_LITERAL_CSTRING("asf")) ||
635  fileExt.Equals(NS_LITERAL_CSTRING("wmv")) ||
636  fileExt.Equals(NS_LITERAL_CSTRING("mov")) ||
637  fileExt.Equals(NS_LITERAL_CSTRING("wm")) ||
638  fileExt.Equals(NS_LITERAL_CSTRING("ogx")) ||
639  fileExt.Equals(NS_LITERAL_CSTRING("ogm")) ||
640  fileExt.Equals(NS_LITERAL_CSTRING("ogv"))
641  ) {
642  return NS_OK; // don't write, don't warn.
643  // TODO: get rid of this
644  }
645 
646  /* WRITE the metadata. */
647  if (NS_SUCCEEDED(result)) {
648  // on windows, we leave this as an nsAString
649  // beginreading doesn't promise to return a null terminated string
650  // this is RFB
651 #if XP_WIN
652  NS_ConvertUTF8toUTF16 filePath(mMetadataPath);
653 #else
654  nsACString &filePath = mMetadataPath;
655 #endif
656 
657  TagLib::FileRef f(filePath.BeginReading());
658  NS_ENSURE_TRUE(f.file(), NS_ERROR_FAILURE);
659  NS_ENSURE_TRUE(f.file()->isOpen(), NS_ERROR_FAILURE);
660  NS_ENSURE_TRUE(f.file()->isValid(), NS_ERROR_FAILURE);
661 
662  nsAutoString propertyValue;
663 
664  // TODO: something better than looking at file extensions?!
665 
666  // TODO: write other files' metadata.
667  // TODO: Move all suff here to the WriteXXX methods, for better code
668  // overview.
669  if (fileExt.Equals(NS_LITERAL_CSTRING("mp3"))) {
670  LOG(("Writing MP3 Metadata"));
671  TagLib::MPEG::File* MPEGFile =
672  static_cast<TagLib::MPEG::File*>(f.file());
673 
674  // Write tags for all possible tag types, if they exist
675  if (NS_SUCCEEDED(result) && MPEGFile->APETag()) {
676  result = WriteAPE(MPEGFile->APETag());
677  }
678  if (NS_SUCCEEDED(result) && MPEGFile->ID3v1Tag()) {
679  result = WriteID3v1(MPEGFile->ID3v1Tag());
680  }
681  if (NS_SUCCEEDED(result) && MPEGFile->ID3v2Tag()) {
682  result = WriteID3v2(MPEGFile->ID3v2Tag());
683  }
684 
685  // Write Image Data
686  nsAutoString imageSpec;
687  tmp_result = mpMetadataPropertyArray->GetPropertyValue(
688  NS_LITERAL_STRING(SB_PROPERTY_PRIMARYIMAGEURL),
689  imageSpec
690  );
691  if (NS_SUCCEEDED(tmp_result)) {
692  PRInt32 imageType = METADATA_IMAGE_TYPE_FRONTCOVER;
693  WriteMP3Image(MPEGFile, imageType, imageSpec);
694  }
695 
696  // Look up the origins of this file.
697  /* bug 10933: description of a URL is not currently implemented in taglib
698  nsAutoString title;
699  result = mpMetadataPropertyArray->GetPropertyValue(
700  NS_LITERAL_STRING(SB_PROPERTY_ORIGINPAGETITLE),
701  title
702  );*/
703  nsAutoString url;
704  tmp_result = mpMetadataPropertyArray->GetPropertyValue(
705  NS_LITERAL_STRING(SB_PROPERTY_ORIGINPAGE),
706  url
707  );
708  // if we have an origin page
709  if (NS_SUCCEEDED(tmp_result)) {
710  if (MPEGFile->ID3v2Tag()) {
711  /*TagLib::String taglibTitle = TagLib::String(
712  NS_ConvertUTF16toUTF8(title).BeginReading(),
713  TagLib::String::UTF8
714  );*/
715 
716  TagLib::String taglibURL = TagLib::String(
717  NS_ConvertUTF16toUTF8(url).BeginReading(),
718  TagLib::String::UTF8
719  );
720 
721  // TODO: bug 10932 -- fix WCOP to be like this in TL
722  TagLib::ID3v2::Tag* tag = MPEGFile->ID3v2Tag();
723  if(taglibURL.isEmpty()) {
724  tag->removeFrames("WOAF");
725  }
726  else if(!tag->frameList("WOAF").isEmpty()) {
727  TagLib::ID3v2::UrlLinkFrame* frame =
728  static_cast<TagLib::ID3v2::UrlLinkFrame*>(
729  tag->frameList("WOAF").front());
730  frame->setUrl(taglibURL);
731  //bug 10933: frame->setText(taglibTitle);
732  }
733  else {
734  TagLib::ID3v2::UrlLinkFrame* frame =
735  new TagLib::ID3v2::UrlLinkFrame("WOAF");
736  tag->addFrame(frame);
737  frame->setUrl(taglibURL);
738  //bug 10933: frame->setText(taglibURL);
739  }
740  }
741  }
742 
743  { // Write Gracenote-specific frames, see bug 18136
744  nsresult tagResult;
745  nsresult extendedDataResult;
746  tagResult = mpMetadataPropertyArray->GetPropertyValue(
747  NS_LITERAL_STRING(SB_GN_PROP_TAGID), propertyValue);
748  extendedDataResult = mpMetadataPropertyArray->GetPropertyValue(
749  NS_LITERAL_STRING(SB_GN_PROP_EXTENDEDDATA), propertyValue);
750  if (NS_SUCCEEDED(tagResult) || NS_SUCCEEDED(extendedDataResult)) {
751  AddGracenoteMetadataMP3(MPEGFile);
752  }
753  }
754  } else if (fileExt.Equals(NS_LITERAL_CSTRING("ogg")) ||
755  fileExt.Equals(NS_LITERAL_CSTRING("oga"))) {
756  LOG(("Write OGG metadata"));
757  // Write ogg specific metadata / TODO: some ogg-expert please check why there is no Ogg::FLAC in here?
758  TagLib::Ogg::Vorbis::File* oggFile =
759  static_cast<TagLib::Ogg::Vorbis::File*>(f.file());
760 
761  // Write tags for all possible tag types
762  if (NS_SUCCEEDED(result) && oggFile->tag()){
763  result = WriteXiphComment(oggFile->tag());
764  }
765 
766  // Write Image Data
767  nsAutoString imageSpec;
768  tmp_result = mpMetadataPropertyArray->GetPropertyValue(
769  NS_LITERAL_STRING(SB_PROPERTY_PRIMARYIMAGEURL),
770  imageSpec
771  );
772  if (NS_SUCCEEDED(tmp_result)) {
773  PRInt32 imageType = METADATA_IMAGE_TYPE_FRONTCOVER;
774  WriteOGGImage(oggFile, imageType, imageSpec);
775  }
776 
777  { // Write Gracenote-specific frames, see bug 18136
778  nsresult tagResult;
779  nsresult extendedDataResult;
780  tagResult = mpMetadataPropertyArray->GetPropertyValue(
781  NS_LITERAL_STRING(SB_GN_PROP_TAGID), propertyValue);
782  extendedDataResult = mpMetadataPropertyArray->GetPropertyValue(
783  NS_LITERAL_STRING(SB_GN_PROP_EXTENDEDDATA), propertyValue);
784  if (NS_SUCCEEDED(tagResult) || NS_SUCCEEDED(extendedDataResult)) {
785  AddGracenoteMetadataXiph(oggFile);
786  }
787  }
788  } else if (fileExt.EqualsLiteral("mp4") ||
789  fileExt.EqualsLiteral("m4a") ||
790  fileExt.EqualsLiteral("m4v")) {
791  LOG(("Writing MPEG-4 metadata"));
792  // Write MP4 specific metadata
793  TagLib::MP4::File* mp4File = static_cast<TagLib::MP4::File*>(f.file());
794 
795  // Write advanced tags for all possible tag types
796  if (NS_SUCCEEDED(result) && mp4File->tag()){
797  result = WriteMP4(mp4File->tag());
798  }
799 
800  // Write Image Data
801  nsAutoString imageSpec;
802  tmp_result = mpMetadataPropertyArray->GetPropertyValue(
803  NS_LITERAL_STRING(SB_PROPERTY_PRIMARYIMAGEURL),
804  imageSpec
805  );
806  if (NS_SUCCEEDED(tmp_result)) {
807  PRInt32 imageType = METADATA_IMAGE_TYPE_FRONTCOVER;
808  WriteMP4Image(mp4File, imageType, imageSpec);
809  }
810  }
811 
812  // Attempt to save the metadata, if everything worked till here
813  if (NS_SUCCEEDED(result)){
814  if (f.save()) {
815  result = NS_OK;
816  } else {
817  LOG(("%s: failed to save!", __FUNCTION__));
818  result = NS_ERROR_FAILURE;
819  }
820  } else {
821  LOG(("%s: failed to update tags!", __FUNCTION__));
822  }
823  }
824 
825  // TODO need to set pWriteCount
826 
827  // Indicate that the operation is complete
828  mCompleted = PR_TRUE;
829 
830  return result;
831 }
832 
833 void sbMetadataHandlerTaglib::AddGracenoteMetadataMP3(
834  TagLib::MPEG::File* MPEGFile)
835 {
836  nsresult rv;
837  nsString propertyValue;
838 
839  rv = mpMetadataPropertyArray->GetPropertyValue(
840  NS_LITERAL_STRING(SB_GN_PROP_TAGID), propertyValue);
841  if (NS_SUCCEEDED(rv)) {
842  const TagLib::ByteVector UFID("UFID");
843  TagLib::ID3v2::Tag *tag2 = MPEGFile->ID3v2Tag(true);
844  NS_ASSERTION(tag2, "TagLib did not create id3v2 tag as asked!");
845 
846  TagLib::String owner("http://www.cddb.com/id3/taginfo1.html");
847  NS_LossyConvertUTF16toASCII propertyCValue(propertyValue);
848  TagLib::ByteVector identifier(propertyCValue.BeginReading(),
849  propertyCValue.Length());
850 
851  // erase old frames
852  const TagLib::ID3v2::FrameList& frames =
853  tag2->frameList(UFID);
854  TagLib::ID3v2::FrameList::ConstIterator it;
855  for (it = frames.begin(); it != frames.end(); ++it) {
856  NS_ASSERTION((*it)->frameID() == UFID,
857  "TagLib gave us the wrong frame!");
858  TagLib::ID3v2::UniqueFileIdentifierFrame* ufidFrame =
859  static_cast<TagLib::ID3v2::UniqueFileIdentifierFrame*>(*it);
860  if (!(ufidFrame->owner() == owner)) {
861  // this UFID frame is for something else
862  continue;
863  }
864  tag2->removeFrame(ufidFrame);
865  it = frames.begin();
866  }
867 
868  // add a new frame
869  TagLib::ID3v2::FrameFactory* factory =
870  TagLib::ID3v2::FrameFactory::instance();
871  TagLib::ID3v2::UniqueFileIdentifierFrame* ufidFrame =
872  static_cast<TagLib::ID3v2::UniqueFileIdentifierFrame*>(
873  factory->createFrame(UFID));
874  ufidFrame->setOwner(owner);
875  ufidFrame->setIdentifier(identifier);
876  tag2->addFrame(ufidFrame);
877  }
878 
879  rv = mpMetadataPropertyArray->GetPropertyValue(
880  NS_LITERAL_STRING(SB_GN_PROP_EXTENDEDDATA), propertyValue);
881  if (NS_SUCCEEDED(rv)) {
882  const TagLib::ByteVector TXXX("TXXX");
883  TagLib::ID3v2::Tag *tag2 = MPEGFile->ID3v2Tag(true);
884  NS_ASSERTION(tag2, "TagLib did not create id3v2 tag as asked!");
885 
886  TagLib::String description("GN_Ext_Data");
887  NS_LossyConvertUTF16toASCII propertyCValue(propertyValue);
888  TagLib::String text(propertyCValue.BeginReading());
889 
890  // erase old frames
891  const TagLib::ID3v2::FrameList& frames =
892  tag2->frameList(TXXX);
893  TagLib::ID3v2::FrameList::ConstIterator it;
894  for (it = frames.begin(); it != frames.end(); ++it) {
895  NS_ASSERTION((*it)->frameID() == TXXX,
896  "TagLib gave us the wrong frame!");
897  TagLib::ID3v2::UserTextIdentificationFrame* txxxFrame =
898  static_cast<TagLib::ID3v2::UserTextIdentificationFrame*>(*it);
899  if (!(txxxFrame->description() == description)) {
900  // this UFID frame is for something else
901  continue;
902  }
903  tag2->removeFrame(txxxFrame);
904  it = frames.begin();
905  }
906 
907  // add a new frame
908  TagLib::ID3v2::FrameFactory* factory =
909  TagLib::ID3v2::FrameFactory::instance();
910  TagLib::ID3v2::UserTextIdentificationFrame* txxxFrame =
911  static_cast<TagLib::ID3v2::UserTextIdentificationFrame*>(
912  factory->createFrame(TXXX));
913  txxxFrame->setDescription(description);
914  txxxFrame->setText(text);
915  tag2->addFrame(txxxFrame);
916  }
917 }
918 
919 void sbMetadataHandlerTaglib::AddGracenoteMetadataXiph(
920  TagLib::Ogg::Vorbis::File *oggFile)
921 {
922  nsresult rv;
923  nsString propertyValue;
924 
925  TagLib::Ogg::XiphComment *xiphComment = oggFile->tag();
926  NS_ASSERTION(xiphComment, "TagLib has no xiph comment!");
927  rv = mpMetadataPropertyArray->GetPropertyValue(
928  NS_LITERAL_STRING(SB_GN_PROP_TAGID), propertyValue);
929  if (NS_SUCCEEDED(rv)) {
930  TagLib::String value(NS_ConvertUTF16toUTF8(propertyValue).BeginReading(),
931  TagLib::String::UTF8);
932  xiphComment->addField("GracenoteFileID", value);
933  }
934  rv = mpMetadataPropertyArray->GetPropertyValue(
935  NS_LITERAL_STRING(SB_GN_PROP_EXTENDEDDATA), propertyValue);
936  if (NS_SUCCEEDED(rv)) {
937  TagLib::String value(NS_ConvertUTF16toUTF8(propertyValue).BeginReading(),
938  TagLib::String::UTF8);
939  xiphComment->addField("GracenoteExtData", value);
940  }
941 }
942 
956 NS_IMETHODIMP sbMetadataHandlerTaglib::GetImageData(
957  PRInt32 aType,
958  nsACString &aMimeType,
959  PRUint32 *aDataLen,
960  PRUint8 **aData)
961 {
962  nsresult rv;
963  NS_ENSURE_ARG_POINTER(aData);
964 
965  LOG(("sbMetadataHandlerTaglib::GetImageData\n"));
966 
967  // First check to see if we have cached art for
968  // the requested type. This is an optimization
969  // to avoid reading metadata twice, or otherwise
970  // locking taglib on the main thread.
971  sbAlbumArt *cachedArt = nsnull;
972  for (PRUint32 i=0; i < mCachedAlbumArt.Length(); i++) {
973  cachedArt = mCachedAlbumArt[i];
974  NS_ENSURE_TRUE(cachedArt, NS_ERROR_UNEXPECTED);
975  if (cachedArt->type == aType) {
976  LOG(("sbMetadataHandlerTaglib::GetImageData - found cached image\n"));
977 
978  aMimeType.Assign(cachedArt->mimeType);
979  *aDataLen = cachedArt->dataLen;
980  *aData = cachedArt->data;
981 
982  // The data now belongs to the caller, so we
983  // are not allowed to free it or use it again.
984  cachedArt->dataLen = 0;
985  cachedArt->data = nsnull;
986  mCachedAlbumArt.RemoveElementAt(i);
987 
988  return NS_OK;
989  }
990  }
991 
992  // If read has already been run, then we can assume
993  // that all images should have been in the cache.
994  // Since we didn't find any in the cache, just go
995  // ahead and return null.
996  if (mCompleted) {
997  *aDataLen = 0;
998  *aData = nsnull;
999  return NS_OK;
1000  }
1001 
1002  // Nothing was found in the cache, so open the target file
1003  // and read the data out manually.
1004 
1005  nsAutoLock lock(sTaglibLock);
1006  try {
1007  rv = GetImageDataInternal(aType, aMimeType, aDataLen, aData);
1008  } catch(...) {
1009  NS_ERROR("sbMetadataHandlerTaglib::GetImageData caught an exception!");
1010  rv = NS_ERROR_FAILURE;
1011  }
1012  return rv;
1013 }
1014 
1015 nsresult sbMetadataHandlerTaglib::GetImageDataInternal(
1016  PRInt32 aType,
1017  nsACString &aMimeType,
1018  PRUint32 *aDataLen,
1019  PRUint8 **aData)
1020 {
1021  nsCOMPtr<nsIFile> pFile;
1022  nsCString urlSpec;
1023  nsCString urlScheme;
1024  nsCString fileExt;
1025  PRBool isMP3;
1026  PRBool isM4A;
1027  PRBool isOGG;
1028  PRBool isFLAC;
1029  nsresult result = NS_OK;
1030 
1031  /* Get the channel URL info. */
1032  NS_ENSURE_STATE(mpURL);
1033  result = mpURL->GetSpec(urlSpec);
1034  NS_ENSURE_SUCCESS(result, result);
1035  result = mpURL->GetScheme(urlScheme);
1036  NS_ENSURE_SUCCESS(result, result);
1037 
1038  if (urlScheme.EqualsLiteral("file"))
1039  {
1040  /* Get the metadata file extension. */
1041  result = mpURL->GetFileExtension(fileExt);
1042  NS_ENSURE_SUCCESS(result, result);
1043  ToLowerCase(fileExt);
1044 
1045  isMP3 = fileExt.Equals(NS_LITERAL_CSTRING("mp3"));
1046  isM4A = fileExt.Equals(NS_LITERAL_CSTRING("m4a"));
1047  isOGG = fileExt.Equals(NS_LITERAL_CSTRING("ogg")) ||
1048  fileExt.Equals(NS_LITERAL_CSTRING("oga"));
1049  isFLAC = fileExt.Equals(NS_LITERAL_CSTRING("flac"));
1050  if (!isMP3 && !isM4A && !isOGG && !isFLAC) {
1051  return NS_ERROR_NOT_IMPLEMENTED;
1052  }
1053 
1054  /* Get the metadata local file path. */
1055  result = mpFileProtocolHandler->GetFileFromURLSpec
1056  (urlSpec,
1057  getter_AddRefs(pFile));
1058  NS_ENSURE_SUCCESS(result, result);
1059 
1060 #if XP_WIN
1061  nsString filePath;
1062  result = pFile->GetPath(filePath);
1063  NS_ENSURE_SUCCESS(result, result);
1064  CopyUTF16toUTF8(filePath, mMetadataPath);
1065 #else
1066  result = pFile->GetNativePath(mMetadataPath);
1067  NS_ENSURE_SUCCESS(result, result);
1068  nsCString filePath = mMetadataPath;
1069 #endif
1070 
1071  result = NS_ERROR_FILE_UNKNOWN_TYPE;
1072  if (isMP3) {
1073  nsAutoPtr<TagLib::MPEG::File> pTagFile;
1074  pTagFile = new TagLib::MPEG::File(filePath.BeginReading());
1075  NS_ENSURE_STATE(pTagFile);
1076 
1077  /* Read the metadata file. */
1078  if (pTagFile->ID3v2Tag()) {
1079  result = ReadImageID3v2(pTagFile->ID3v2Tag(), aType, aMimeType, aDataLen, aData);
1080  }
1081  } else if (isM4A) {
1082  nsAutoPtr<TagLib::MP4::File> pTagFile;
1083  pTagFile = new TagLib::MP4::File(filePath.BeginReading());
1084  NS_ENSURE_STATE(pTagFile);
1085 
1086  /* Read the metadata file. */
1087  if (pTagFile->tag()) {
1088  result = ReadImageITunes(
1089  static_cast<TagLib::MP4::Tag*>(pTagFile->tag()),
1090  aMimeType, aDataLen, aData);
1091  }
1092  } else if (isOGG) {
1093  nsAutoPtr<TagLib::Ogg::Vorbis::File> pTagFile;
1094  #if XP_WIN
1095  // XXX Mook: temporary hack to make tree build, reopening bug 16158
1096  pTagFile = new TagLib::Ogg::Vorbis::File(NS_ConvertUTF16toUTF8(filePath).BeginReading());
1097  #else
1098  pTagFile = new TagLib::Ogg::Vorbis::File(filePath.BeginReading());
1099  #endif
1100  NS_ENSURE_STATE(pTagFile);
1101 
1102  /* Read the metadata file. */
1103  if (pTagFile->tag()) {
1104  result = ReadImageOgg(
1105  static_cast<TagLib::Ogg::XiphComment*>(pTagFile->tag()),
1106  aType, aMimeType, aDataLen, aData);
1107  }
1108  } else if (isFLAC) {
1109  nsAutoPtr<TagLib::FLAC::File> pTagFile;
1110  pTagFile = new TagLib::FLAC::File(filePath.BeginReading());
1111  NS_ENSURE_STATE(pTagFile);
1112 
1113  if(pTagFile->xiphComment()) {
1114  /* Read the metadata file. */
1115  result = ReadImageFlac(pTagFile, aType, aMimeType, aDataLen, aData);
1116  }
1117  }
1118  } else {
1119  result = NS_ERROR_NOT_IMPLEMENTED;
1120  }
1121  return result;
1122 }
1123 
1124 NS_IMETHODIMP sbMetadataHandlerTaglib::SetImageData(
1125  PRInt32 aType,
1126  const nsAString &aURL)
1127 {
1128  nsresult rv;
1129  LOG(("sbMetadataHandlerTaglib::SetImageData\n"));
1130  nsAutoLock lock(sTaglibLock);
1131 
1132  try {
1133  rv = SetImageDataInternal(aType, aURL);
1134  } catch(...) {
1135  NS_ERROR("sbMetadataHandlerTaglib::SetImageData caught an exception!");
1136  rv = NS_ERROR_FAILURE;
1137  }
1138  return rv;
1139 }
1140 
1141 nsresult sbMetadataHandlerTaglib::SetImageDataInternal(
1142  PRInt32 aType,
1143  const nsAString &aURL)
1144 {
1145  nsCOMPtr<nsIFile> pFile;
1146  nsCString urlSpec;
1147  nsCString urlScheme;
1148  nsCString fileExt;
1149  nsresult result = NS_OK;
1150  PRBool isMP3;
1151  PRBool isOGG;
1152  PRBool isMP4;
1153 
1154  NS_ENSURE_STATE(mpURL);
1155 
1156  LOG(("%s(%s)", __FUNCTION__, NS_ConvertUTF16toUTF8(aURL).get()));
1157  // TODO: write other files' metadata.
1158  // First check if we support this file
1159  result = mpURL->GetFileExtension(fileExt);
1160  NS_ENSURE_SUCCESS(result, result);
1161 
1162  ToLowerCase(fileExt);
1163  isMP3 = fileExt.EqualsLiteral("mp3");
1164  isOGG = fileExt.EqualsLiteral("ogg") ||
1165  fileExt.EqualsLiteral("oga");
1166  isMP4 = fileExt.EqualsLiteral("mp4") ||
1167  fileExt.EqualsLiteral("m4a");
1168  if (!isMP3 && !isOGG && !isMP4) {
1169  LOG(("%s: file format not supported", __FUNCTION__));
1170  return NS_ERROR_NOT_IMPLEMENTED;
1171  }
1172 
1173  result = mpURL->GetSpec(urlSpec);
1174  NS_ENSURE_SUCCESS(result, result);
1175  result = mpURL->GetScheme(urlScheme);
1176  NS_ENSURE_SUCCESS(result, result);
1177 
1178  if (urlScheme.EqualsLiteral("file"))
1179  {
1180  /* Get the metadata local file path. */
1181  result = mpFileProtocolHandler->GetFileFromURLSpec
1182  (urlSpec,
1183  getter_AddRefs(pFile));
1184  NS_ENSURE_SUCCESS(result, result);
1185 
1186 #if XP_WIN
1187  nsString filePath;
1188  result = pFile->GetPath(filePath);
1189  NS_ENSURE_SUCCESS(result, result);
1190  CopyUTF16toUTF8(filePath, mMetadataPath);
1191 #else
1192  result = pFile->GetNativePath(mMetadataPath);
1193  NS_ENSURE_SUCCESS(result, result);
1194  nsCString filePath = mMetadataPath;
1195 #endif
1196 
1197  /* Open and read the metadata file. */
1198  TagLib::FileRef f(filePath.BeginReading());
1199  NS_ENSURE_TRUE(f.file(), NS_ERROR_FAILURE);
1200  NS_ENSURE_TRUE(f.file()->isOpen(), NS_ERROR_FAILURE);
1201  NS_ENSURE_TRUE(f.file()->isValid(), NS_ERROR_FAILURE);
1202 
1203  /* Get the metadata file extension. */
1204  if (isMP3)
1205  result = WriteMP3Image(static_cast<TagLib::MPEG::File*>(f.file()),
1206  aType,
1207  aURL);
1208  else if (isOGG)
1209  result = WriteOGGImage(static_cast<TagLib::Ogg::Vorbis::File*>(f.file()),
1210  aType,
1211  aURL);
1212  else if (isMP4)
1213  result = WriteMP4Image(static_cast<TagLib::MP4::File*>(f.file()),
1214  aType,
1215  aURL);
1216 
1217  // WriteSetImageDataInternal does not write the metadata to file
1218  if (NS_SUCCEEDED(result)) {
1219  // Attempt to save the metadata
1220  if (f.save()) {
1221  result = NS_OK;
1222  } else {
1223  LOG(("%s: Failed to save", __FUNCTION__));
1224  result = NS_ERROR_FAILURE;
1225  }
1226  }
1227  } else {
1228  LOG(("%s: scheme not supported", __FUNCTION__));
1229  result = NS_ERROR_NOT_IMPLEMENTED;
1230  }
1231 
1232  return result;
1233 }
1234 
1242 nsresult sbMetadataHandlerTaglib::RemoveAllImagesMP3(
1243  TagLib::MPEG::File* aMPEGFile,
1244  PRInt32 imageType)
1245 {
1246  if (aMPEGFile->ID3v2Tag()) {
1247  TagLib::ID3v2::FrameList frameList= aMPEGFile->ID3v2Tag()->frameList("APIC");
1248  if (!frameList.isEmpty()){
1249  std::list<TagLib::ID3v2::Frame*>::const_iterator iter = frameList.begin();
1250  while (iter != frameList.end()) {
1251  TagLib::ID3v2::AttachedPictureFrame *frame =
1252  static_cast<TagLib::ID3v2::AttachedPictureFrame *>( *iter );
1253  std::list<TagLib::ID3v2::Frame*>::const_iterator nextIter = iter;
1254  nextIter++;
1255  if (frame && frame->type() == imageType){
1256  // Remove and free the memory for this frame
1257  aMPEGFile->ID3v2Tag()->removeFrame(*iter, true);
1258  }
1259  iter = nextIter;
1260  }
1261  }
1262  }
1263 
1264  return NS_OK;
1265 }
1266 
1274 nsresult sbMetadataHandlerTaglib::RemoveAllImagesOGG(
1275  TagLib::Ogg::Vorbis::File* aOGGFile,
1276  PRInt32 imageType)
1277 {
1278  if (aOGGFile->tag()) {
1279  StringList s_artworkList = aOGGFile->tag()->fieldListMap()["METADATA_BLOCK_PICTURE"];
1280  if(!s_artworkList.isEmpty()){
1281  for (StringList::Iterator it = s_artworkList.begin();
1282  it != s_artworkList.end();
1283  ++it)
1284  {
1285  TagLib::FLAC::Picture *picture = new TagLib::FLAC::Picture();
1286  String encodedData = *it;
1287  if (encodedData.isNull())
1288  {
1289  break;
1290  }
1291  std::string decodedData = base64_decode(encodedData.to8Bit());
1292  if (decodedData.empty())
1293  break;
1294  ByteVector bv;
1295  bv.setData(decodedData.data(), decodedData.size());
1296  if (!picture->parse(bv))
1297  {
1298  delete picture;
1299  break;
1300  }
1301 
1302  if (picture->type() == imageType) {
1303  LOG(("erasing artwork"));
1304  aOGGFile->tag()->removeField("METADATA_BLOCK_PICTURE", *it);
1305  }
1306  delete picture;
1307  }
1308  }
1309  }
1310 
1311  return NS_OK;
1312 }
1313 
1323 nsresult sbMetadataHandlerTaglib::ReadImageFile(const nsAString &imageSpec,
1324  PRUint8* &imageData,
1325  PRUint32 &imageDataSize,
1326  nsCString &imageMimeType)
1327 {
1328  nsresult rv;
1329  nsCOMPtr<nsIFile> imageFile;
1330  nsCOMPtr<nsIURI> imageURI;
1331  PRBool isResource;
1332  nsCString cImageSpec = NS_LossyConvertUTF16toASCII(imageSpec);
1333 
1334  { // Scope for unlock
1335  nsAutoUnlock unlock(sTaglibLock);
1336 
1337  nsCOMPtr<nsIIOService> ioservice =
1338  do_ProxiedGetService("@mozilla.org/network/io-service;1", &rv);
1339  NS_ENSURE_SUCCESS(rv, rv);
1340 
1341  // Convert the imageSpec to an nsIURI
1342  rv = ioservice->NewURI(cImageSpec, nsnull, nsnull,
1343  getter_AddRefs(imageURI));
1344  NS_ENSURE_SUCCESS(rv, rv);
1345  }
1346 
1347  rv = imageURI->SchemeIs("resource", &isResource);
1348  NS_ENSURE_SUCCESS(rv, rv);
1349 
1350  if (isResource) {
1351  rv = mpResourceProtocolHandler->ResolveURI(imageURI, cImageSpec);
1352  NS_ENSURE_SUCCESS(rv, rv);
1353  }
1354 
1355  // open the image file
1356  rv = mpFileProtocolHandler->GetFileFromURLSpec(cImageSpec,
1357  getter_AddRefs(imageFile)
1358  );
1359  NS_ENSURE_SUCCESS(rv, rv);
1360 
1361  // Get the mime type.
1362  nsCOMPtr<nsIMIMEService> mimeService =
1363  do_GetService("@mozilla.org/mime;1", &rv);
1364  NS_ENSURE_SUCCESS(rv, rv);
1365  rv = mimeService->GetTypeFromFile(imageFile, imageMimeType);
1366  NS_ENSURE_SUCCESS(rv, rv);
1367 
1368  // Load the actual data.
1369  nsCOMPtr<nsIFileInputStream> inputStream =
1370  do_CreateInstance("@mozilla.org/network/file-input-stream;1", &rv);
1371  NS_ENSURE_SUCCESS(rv, rv);
1372 
1373  rv = inputStream->Init(imageFile, 0x01, 0600, 0);
1374  NS_ENSURE_SUCCESS(rv, rv);
1375 
1376  nsCOMPtr<nsIBinaryInputStream> stream =
1377  do_CreateInstance("@mozilla.org/binaryinputstream;1", &rv);
1378  NS_ENSURE_SUCCESS(rv, rv);
1379 
1380  rv = stream->SetInputStream(inputStream);
1381  NS_ENSURE_SUCCESS(rv, rv);
1382 
1383  // First length,
1384  rv = inputStream->Available(&imageDataSize);
1385  NS_ENSURE_SUCCESS(rv, rv);
1386 
1387  // then the data itself
1388  rv = stream->ReadByteArray(imageDataSize, &imageData);
1389  NS_ENSURE_SUCCESS(rv, rv);
1390 
1391  return rv;
1392 }
1393 
1403 nsresult sbMetadataHandlerTaglib::WriteMP3Image(TagLib::MPEG::File* aMPEGFile,
1404  PRInt32 imageType,
1405  const nsAString &imageSpec)
1406 {
1407  nsresult rv;
1408 
1409  if (!aMPEGFile->ID3v2Tag()) {
1410  // Not ID3v2 tag then abort
1411  return NS_ERROR_FAILURE;
1412  }
1413 
1414  if (imageSpec.Length() <= 0) {
1415  // No image provided so just remove existing ones.
1416  rv = RemoveAllImagesMP3(aMPEGFile, imageType);
1417  } else {
1418  PRUint8* imageData;
1419  PRUint32 imageDataSize = 0;
1420  nsCString imageMimeType;
1421 
1422  // Read the image file contents
1423  rv = ReadImageFile(imageSpec, imageData, imageDataSize, imageMimeType);
1424  NS_ENSURE_SUCCESS(rv, rv);
1425 
1426  // We need to check a few things to ensure we are not adding an image that
1427  // does not meet the requirements of the ID3v2 tag format.
1428  // MaxSize = 16Mb | 16777216b
1429  // type >= 0x00 | <= 0x14
1430  if (imageDataSize > MAX_MPEG_IMAGE_SIZE)
1431  return NS_ERROR_FAILURE;
1432 
1433  if (imageType < METADATA_IMAGE_TYPE_OTHER ||
1434  imageType > METADATA_IMAGE_TYPE_FRONTCOVER)
1435  return NS_ERROR_FAILURE;
1436 
1437  // Create the picture frame, and set mimetype, image type (e.g. front cover)
1438  // and then fill in the data.
1439  TagLib::ID3v2::AttachedPictureFrame *pic =
1440  new TagLib::ID3v2::AttachedPictureFrame;
1441  pic->setMimeType(TagLib::String(imageMimeType.BeginReading(),
1442  TagLib::String::UTF8));
1443  pic->setType(TagLib::ID3v2::AttachedPictureFrame::Type(imageType));
1444  pic->setPicture(TagLib::ByteVector((const char *)imageData, imageDataSize));
1445 
1446  // First we have to remove any other existing frames of the same type
1447  rv = RemoveAllImagesMP3(aMPEGFile, imageType);
1448  NS_ENSURE_SUCCESS(rv, rv);
1449 
1450  // Add the frame to the file.
1451  aMPEGFile->ID3v2Tag()->addFrame(pic);
1452  }
1453 
1454  return rv;
1455 }
1456 
1466 nsresult sbMetadataHandlerTaglib::WriteOGGImage(
1467  TagLib::Ogg::Vorbis::File* aOGGFile,
1468  PRInt32 imageType,
1469  const nsAString &imageSpec)
1470 {
1471  nsresult rv;
1472 
1473  if (!aOGGFile->tag()) {
1474  return NS_ERROR_FAILURE;
1475  }
1476 
1477  if (imageSpec.Length() <= 0) {
1478  // No image provided so just remove existing ones.
1479  rv = RemoveAllImagesOGG(aOGGFile, imageType);
1480  } else {
1481  PRUint8* imageData;
1482  PRUint32 imageDataSize = 0;
1483  nsCString imageMimeType;
1484 
1485  // Read the image file contents
1486  rv = ReadImageFile(imageSpec, imageData, imageDataSize, imageMimeType);
1487  NS_ENSURE_SUCCESS(rv, rv);
1488 
1489  // Create the picture frame, and set mimetype, image type (e.g. front cover)
1490  // and then fill in the data.
1491  LOG(("WriteOGGImage():: Creating new FLAC::Picture"));
1492  TagLib::FLAC::Picture *pic = new TagLib::FLAC::Picture;
1493  pic->setMimeType(TagLib::String(imageMimeType.BeginReading(),
1494  TagLib::String::UTF8));
1495  pic->setType(TagLib::FLAC::Picture::Type(imageType));
1496  pic->setData(TagLib::ByteVector((const char *)imageData, imageDataSize));
1497 
1498  // First we have to remove any other existing frames of the same type
1499  LOG(("WriteOGGImage():: Removing all images from OGG file"));
1500  rv = RemoveAllImagesOGG(aOGGFile, imageType);
1501  NS_ENSURE_SUCCESS(rv, rv);
1502 
1503  // Add the frame to the file
1504  LOG(("WriteOGGImage():: Setting the artwork"));
1505  ByteVector bv = pic->render();
1506  std::string encodedData = base64_encode((const unsigned char *)bv.data(), bv.size());
1507  bv = ByteVector(encodedData.data(), encodedData.length());
1508  aOGGFile->tag()->addField("METADATA_BLOCK_PICTURE", bv.data());
1509  }
1510 
1511  return rv;
1512 }
1513 
1521 nsresult sbMetadataHandlerTaglib::WriteMP4Image(
1522  TagLib::MP4::File* aMP4File,
1523  PRInt32 imageType,
1524  const nsAString &imageSpec)
1525 {
1526  LOG(("%s: setting to image %s",
1527  __FUNCTION__,
1528  NS_ConvertUTF16toUTF8(imageSpec).get()));
1529  nsresult rv;
1530 
1531  NS_ENSURE_TRUE(aMP4File->tag(), NS_ERROR_FAILURE);
1532  NS_ENSURE_TRUE(imageType == METADATA_IMAGE_TYPE_FRONTCOVER,
1533  NS_ERROR_NOT_IMPLEMENTED);
1534 
1535  TagLib::ByteVector data;
1536 
1537  if (imageSpec.IsEmpty()) {
1538  // No image provided so just remove existing ones.
1539  LOG(("%s: clearing image", __FUNCTION__));
1540  data = TagLib::ByteVector::null;
1541  } else {
1542  PRUint8* imageData;
1543  PRUint32 imageDataSize = 0;
1544  nsCString imageMimeType;
1545 
1546  // Read the image file contents
1547  rv = ReadImageFile(imageSpec, imageData, imageDataSize, imageMimeType);
1548  NS_ENSURE_SUCCESS(rv, rv);
1549 
1550  LOG(("%s: setting image to %u bytes", __FUNCTION__, imageDataSize));
1551  data.setData((const char*)imageData, imageDataSize);
1552  }
1553 
1554  TagLib::MP4::Tag* tag = static_cast<TagLib::MP4::Tag*>(aMP4File->tag());
1555 
1556  MP4::CoverArtList coverArtList;
1558  coverArtList.append(MP4::CoverArt(MP4::CoverArt::JPEG, data));
1559  tag->itemListMap()["covr"] = coverArtList;
1560 
1561  tag->save();
1562 
1563  return NS_OK;
1564 }
1565 
1574 nsresult sbMetadataHandlerTaglib::ReadImageID3v2(TagLib::ID3v2::Tag *aTag,
1575  PRInt32 aType,
1576  nsACString &aMimeType,
1577  PRUint32 *aDataLen,
1578  PRUint8 **aData)
1579 {
1580  NS_ENSURE_ARG_POINTER(aTag);
1581  NS_ENSURE_ARG_POINTER(aData);
1582  nsresult rv = NS_OK;
1583 
1584  if (!aTag) {
1585  // Not ID3v2 tag then abort
1586  return NS_ERROR_FAILURE;
1587  }
1588 
1589  /*
1590  * Extract the requested image from the metadata
1591  */
1592  TagLib::ID3v2::FrameList frameList = aTag->frameList("APIC");
1593  if (!frameList.isEmpty()){
1594  TagLib::ID3v2::AttachedPictureFrame *p = nsnull;
1595  for (TagLib::uint frameIndex = 0;
1596  frameIndex < frameList.size();
1597  frameIndex++) {
1598  p = static_cast<TagLib::ID3v2::AttachedPictureFrame *>(frameList[frameIndex]);
1599  if (p->type() == aType && p->picture().size() > 0) {
1600  // Store the size of the data
1601  *aDataLen = p->picture().size();
1602  // Store the mimeType acquired from the image data
1603  // these can sometimes be in a format like "PNG"
1604  aMimeType.Assign(p->mimeType().toCString(), p->mimeType().length());
1605 
1606  // Copy the data over to a mozilla memory chunk so we don't break
1607  // Things :).
1608  *aData = static_cast<PRUint8 *>(nsMemory::Clone(p->picture().data(),
1609  *aDataLen));
1610  NS_ENSURE_TRUE(*aData, NS_ERROR_OUT_OF_MEMORY);
1611  break;
1612  }
1613  }
1614  }
1615 
1616  return rv;
1617 }
1618 
1619 nsresult sbMetadataHandlerTaglib::ReadImageITunes(TagLib::MP4::Tag *aTag,
1620  nsACString &aMimeType,
1621  PRUint32 *aDataLen,
1622  PRUint8 **aData)
1623 {
1624  NS_ENSURE_ARG_POINTER(aTag);
1625  NS_ENSURE_ARG_POINTER(aData);
1626  NS_ENSURE_ARG_POINTER(aDataLen);
1627  nsCOMPtr<nsIThread> mainThread;
1628  nsresult rv = NS_OK;
1629 
1630  /*
1631  * Extract the requested image from the metadata
1632  */
1633  MP4::ItemListMap::Iterator itr = aTag->itemListMap().begin();
1634 
1635  if (aTag->itemListMap().contains("covr")) {
1636  MP4::CoverArtList coverArtList = aTag->itemListMap()["covr"].toCoverArtList();
1637 
1638  if (coverArtList.size() == 0) {
1639  return NS_OK;
1640  }
1641 
1642  MP4::CoverArt ca = coverArtList[0];
1643 
1644  *aDataLen = coverArtList[0].data().size();
1645 
1647  static_cast<PRUint8 *>(nsMemory::Clone(coverArtList[0].data().data(), *aDataLen));
1648  NS_ENSURE_TRUE(data, NS_ERROR_OUT_OF_MEMORY);
1649 
1650  { // Scope for unlock
1651 
1652  // Release the lock while we're using proxied services to avoid
1653  // deadlocking with the main thread trying to grab the taglib lock
1654  nsAutoUnlock unlock(sTaglibLock);
1655 
1656  nsCOMPtr<nsIContentSniffer> contentSniffer =
1657  do_ProxiedGetService("@mozilla.org/image/loader;1", &rv);
1658  NS_ENSURE_SUCCESS(rv, rv);
1659 
1660  rv = contentSniffer->GetMIMETypeFromContent(NULL, data.get(), *aDataLen,
1661  aMimeType);
1662  NS_ENSURE_SUCCESS(rv, rv);
1663  }
1664 
1665  *aData = data.forget();
1666  }
1667 
1668  return NS_OK;
1669 }
1670 
1671 nsresult sbMetadataHandlerTaglib::ReadImageOgg(TagLib::Ogg::XiphComment *aTag,
1672  PRInt32 aType,
1673  nsACString &aMimeType,
1674  PRUint32 *aDataLen,
1675  PRUint8 **aData)
1676 {
1677  NS_ENSURE_ARG_POINTER(aTag);
1678  NS_ENSURE_ARG_POINTER(aData);
1679  NS_ENSURE_ARG_POINTER(aDataLen);
1680  nsCOMPtr<nsIThread> mainThread;
1681 
1682  /*
1683  * Extract the requested image from the metadata
1684  */
1685  StringList artworkList = aTag->fieldListMap()["METADATA_BLOCK_PICTURE"];
1686  if(!artworkList.isEmpty()){
1687  for (StringList::Iterator it = artworkList.begin();
1688  it != artworkList.end();
1689  ++it)
1690  {
1691  TagLib::FLAC::Picture* picture = new TagLib::FLAC::Picture();
1692  String encodedData = *it;
1693  if (encodedData.isNull())
1694  {
1695  break;
1696  }
1697  std::string decodedData = base64_decode(encodedData.to8Bit());
1698  if (decodedData.empty())
1699  break;
1700  ByteVector bv;
1701  bv.setData(decodedData.data(), decodedData.size());
1702  if (!picture->parse(bv))
1703  {
1704  delete picture;
1705  break;
1706  }
1707  if (picture->type() == aType) {
1708  *aDataLen = picture->data().size();
1709  aMimeType.Assign(picture->mimeType().toCString());
1710  *aData = static_cast<PRUint8 *>(
1711  nsMemory::Clone(picture->data().data(), *aDataLen));
1712  NS_ENSURE_TRUE(*aData, NS_ERROR_OUT_OF_MEMORY);
1713  }
1714  delete picture;
1715  }
1716  }
1717 
1718  return NS_OK;
1719 }
1720 
1721 nsresult sbMetadataHandlerTaglib::ReadImageFlac(TagLib::FLAC::File *pTagFile,
1722  PRInt32 aType,
1723  nsACString &aMimeType,
1724  PRUint32 *aDataLen,
1725  PRUint8 **aData)
1726 {
1727  NS_ENSURE_ARG_POINTER(pTagFile);
1728  NS_ENSURE_ARG_POINTER(aData);
1729  NS_ENSURE_ARG_POINTER(aDataLen);
1730  nsCOMPtr<nsIThread> mainThread;
1731  TagLib::List<TagLib::FLAC::Picture*> artworkList;
1732 
1733  /*
1734  * Extract the requested image from the metadata
1735  */
1736 
1737  artworkList = pTagFile->pictureList();
1738  if(!artworkList.isEmpty()){
1739  for (
1740  TagLib::List<TagLib::FLAC::Picture*>::Iterator it = artworkList.begin();
1741  it != artworkList.end();
1742  ++it)
1743  {
1744  TagLib::FLAC::Picture* picture = *it;
1745  if (picture->type() == aType) {
1746  *aDataLen = picture->data().size();
1747  aMimeType.Assign(picture->mimeType().toCString());
1748  *aData = static_cast<PRUint8 *>(
1749  nsMemory::Clone(picture->data().data(), *aDataLen));
1750  NS_ENSURE_TRUE(*aData, NS_ERROR_OUT_OF_MEMORY);
1751  break; // We can not handle more than one image currently
1752  }
1753  }
1754  }
1755 
1756  return NS_OK;
1757 }
1770 NS_IMETHODIMP sbMetadataHandlerTaglib::OnChannelData(
1771  nsISupports *pChannel)
1772 {
1773  return (NS_OK);
1774 }
1775 
1776 
1782 NS_IMETHODIMP sbMetadataHandlerTaglib::Close()
1783 {
1784  /* Throw away cached album art */
1785  mCachedAlbumArt.Clear();
1786 
1787  /* If a metadata channel is being used, remove it from */
1788  /* use with the TagLib nsIChannel file I/O services. */
1789  if (!mMetadataChannelID.IsEmpty())
1790  {
1791  mpTagLibChannelFileIOManager->RemoveChannel(mMetadataChannelID);
1792  mMetadataChannelID.Truncate();
1793  }
1794 
1795  /* Close the metadata channel. */
1796  if (mpSeekableChannel)
1797  {
1798  mpSeekableChannel->Close();
1799  mpSeekableChannel = nsnull;
1800  }
1801 
1802  /* Complete read operation. */
1803  CompleteRead();
1804 
1805  return (NS_OK);
1806 }
1807 
1808 
1809 /*
1810  * Getters/setters
1811  */
1812 
1813 NS_IMETHODIMP sbMetadataHandlerTaglib::GetProps(
1814  sbIMutablePropertyArray **ppPropertyArray)
1815 {
1816  NS_ENSURE_ARG_POINTER(ppPropertyArray);
1817  NS_ENSURE_STATE(mpMetadataPropertyArray);
1818  NS_ADDREF(*ppPropertyArray = mpMetadataPropertyArray);
1819  return (NS_OK);
1820 }
1821 
1822 NS_IMETHODIMP sbMetadataHandlerTaglib::SetProps(
1823  sbIMutablePropertyArray *ppPropertyArray)
1824 {
1825  mpMetadataPropertyArray = ppPropertyArray;
1826  return (NS_OK);
1827 }
1828 
1829 NS_IMETHODIMP sbMetadataHandlerTaglib::GetCompleted(
1830  PRBool *pCompleted)
1831 {
1832  NS_ENSURE_ARG_POINTER(pCompleted);
1833  *pCompleted = mCompleted;
1834  return (NS_OK);
1835 }
1836 
1837 NS_IMETHODIMP sbMetadataHandlerTaglib::GetChannel(
1838  nsIChannel **ppChannel)
1839 {
1840  NS_ENSURE_ARG_POINTER(ppChannel);
1841  NS_IF_ADDREF(*ppChannel = mpChannel);
1842  return (NS_OK);
1843 }
1844 
1845 NS_IMETHODIMP sbMetadataHandlerTaglib::SetChannel(
1846  nsIChannel *pChannel)
1847 {
1848  mpChannel = pChannel;
1849  if (!mpChannel) {
1850  mpURL = nsnull;
1851  } else {
1852  /* Get the channel URL info. */
1853  nsCOMPtr<nsIURI> pURI;
1854  nsresult result = NS_OK;
1855  result = mpChannel->GetURI(getter_AddRefs(pURI));
1856  NS_ENSURE_SUCCESS(result, result);
1857  mpURL = do_QueryInterface(pURI, &result);
1858  NS_ENSURE_SUCCESS(result, result);
1859  }
1860 
1861  return (NS_OK);
1862 }
1863 
1864 
1865 /*******************************************************************************
1866  *
1867  * Taglib metadata handler sbISeekableChannel implementation.
1868  *
1869  ******************************************************************************/
1870 
1883 NS_IMETHODIMP sbMetadataHandlerTaglib::OnChannelDataAvailable(
1884  sbISeekableChannel *pChannel)
1885 {
1886  PRBool channelCompleted;
1887  nsresult result = NS_OK;
1888 
1889  /* Do nothing if the metadata reading is complete. */
1890  if (mCompleted)
1891  return (result);
1892 
1893  /* Process channel data and complete processing on any exception. */
1894  try
1895  {
1896  /* Clear channel restart condition. */
1897  mMetadataChannelRestart = PR_FALSE;
1898 
1899  /* Read the metadata. */
1900  {
1901  nsAutoLock lock(sTaglibLock);
1902  ReadMetadata();
1903  }
1904 
1905  /* Check for metadata channel completion. */
1906  if (!mCompleted)
1907  {
1908  result = mpSeekableChannel->GetCompleted(&channelCompleted);
1909  if (NS_SUCCEEDED(result) && channelCompleted)
1910  CompleteRead();
1911  }
1912  }
1913  catch(...)
1914  {
1915  printf("1: OnChannelDataAvailable exception\n");
1916  CompleteRead();
1917  }
1918 
1919  return (result);
1920 }
1921 
1922 
1923 /*******************************************************************************
1924  *
1925  * Public taglib metadata handler services.
1926  *
1927  ******************************************************************************/
1928 
1929 /*
1930  * sbMetadataHandlerTaglib
1931  *
1932  * This method is the constructor for the taglib metadata handler class.
1933  */
1934 
1936 :
1937  mpTagLibChannelFileIOManager(nsnull),
1938  mpFileProtocolHandler(nsnull),
1939  mpResourceProtocolHandler(nsnull),
1940  mpMetadataPropertyArray(nsnull),
1941  mpChannel(nsnull),
1942  mpSeekableChannel(nsnull),
1943  mpURL(nsnull),
1944  mMetadataChannelRestart(PR_FALSE),
1945  mCompleted(PR_FALSE)
1946 {
1947 }
1948 
1949 
1950 /*
1951  * ~sbMetadataHandlerTaglib
1952  *
1953  * This method is the destructor for the taglib metadata handler class.
1954  */
1955 
1957 {
1958  /* Ensure the taglib metadata handler is closed. */
1959  Close();
1960 }
1961 
1962 
1963 /*
1964  * FactoryInit
1965  *
1966  * This function is called by the component factory to initialize the
1967  * component.
1968  */
1969 
1971 {
1972  nsresult rv;
1973 
1974  /* Get the file protocol handler now (since this is mostly threadsafe,
1975  whereas the IO service definitely isn't) */
1976  nsCOMPtr<nsIIOService> ioservice =
1977  do_GetService("@mozilla.org/network/io-service;1", &rv);
1978  NS_ENSURE_SUCCESS(rv, rv);
1979 
1980  nsCOMPtr<nsIProtocolHandler> fileHandler;
1981  rv = ioservice->GetProtocolHandler("file", getter_AddRefs(fileHandler));
1982  NS_ENSURE_SUCCESS(rv, rv);
1983  mpFileProtocolHandler = do_QueryInterface(fileHandler, &rv);
1984  NS_ENSURE_SUCCESS(rv, rv);
1985 
1986  nsCOMPtr<nsIProtocolHandler> resHandler;
1987  rv = ioservice->GetProtocolHandler("resource", getter_AddRefs(resHandler));
1988  NS_ENSURE_SUCCESS(rv, rv);
1989  mpResourceProtocolHandler = do_QueryInterface(resHandler, &rv);
1990  NS_ENSURE_SUCCESS(rv, rv);
1991 
1992  return (NS_OK);
1993 }
1994 
1995 /* static */
1997 {
1998  sbMetadataHandlerTaglib::sTaglibLock =
1999  nsAutoLock::NewLock("sbMetadataHandlerTaglib::sTaglibLock");
2000  NS_ENSURE_TRUE(sbMetadataHandlerTaglib::sTaglibLock, NS_ERROR_OUT_OF_MEMORY);
2001 
2002  return NS_OK;
2003 }
2004 
2005 /* static */
2007 {
2008  if (sbMetadataHandlerTaglib::sTaglibLock) {
2009  nsAutoLock::DestroyLock(sbMetadataHandlerTaglib::sTaglibLock);
2010  }
2011 }
2012 
2013 /*******************************************************************************
2014  *
2015  * Private taglib metadata handler class services.
2016  *
2017  ******************************************************************************/
2018 
2019 /*
2020  * Static field initializers.
2021  */
2022 
2023 PRUint32 sbMetadataHandlerTaglib::sNextChannelID = 0;
2024 
2025 
2026 /*******************************************************************************
2027  *
2028  * Private taglib metadata handler ID3v2 services.
2029  *
2030  ******************************************************************************/
2031 
2032 /*
2033  * ID3v2Map Map of ID3v2 tag names to Songbird metadata
2034  * names.
2035  */
2036 
2037 static const char *ID3v2Map[][2] =
2038 {
2039  { "TENC", SB_PROPERTY_SOFTWAREVENDOR },
2040  { "TIT3", SB_PROPERTY_SUBTITLE },
2041 // { "TMED", "mediatype" },
2042 // { "TOAL", "original_album" },
2043 // { "TOPE", "original_artist" },
2044 // { "TORY", "original_year" },
2045 // { "TPE2", "accompaniment" },
2046 // { "TPE4", "interpreter_remixer" },
2047  { "UFID", SB_PROPERTY_METADATAUUID },
2048 // { "USER", "terms_of_use" },
2049 // { "WCOM", "commercialinfo_url" },
2050 // { "WOAR", "artist_url" },
2051 // { "WOAS", "source_url" },
2052 // { "WORS", "netradio_url" },
2053 // { "WPAY", "payment_url" },
2054 // { "WPUB", "publisher_url" },
2055 // { "WXXX", "user_url" }
2056  { "XXXX", "junq" }
2057 };
2058 
2059 
2060 /*
2061  * ReadID3v2Tags
2062  *
2063  * --> pTag ID3v2 tag object.
2064  * --> aCharset The character set the tags are assumed to be in
2065  * Pass in null to do no conversion
2066  *
2067  * This function reads ID3v2 tags from the ID3v2 tag object specified by pTag.
2068  * The read tags are added to the set of metadata values.
2069  */
2070 
2071 void sbMetadataHandlerTaglib::ReadID3v2Tags(
2072  TagLib::ID3v2::Tag *pTag,
2073  const char *aCharset)
2074 {
2075  TagLib::ID3v2::FrameListMap frameListMap;
2076  int numMetadataEntries;
2077  int i;
2078 
2079  /* Do nothing if no tags are present. */
2080  if (!pTag) {
2081  return;
2082  }
2083 
2084  frameListMap = pTag->frameListMap();
2085 
2086  /* Add the metadata entries. */
2087  numMetadataEntries = sizeof(ID3v2Map) / sizeof(ID3v2Map[0]);
2088  for (i = 0; i < numMetadataEntries; i++) {
2089  TagLib::ID3v2::FrameList frameList = frameListMap[ID3v2Map[i][0]];
2090  if(!frameList.isEmpty()) {
2091  AddMetadataValue(ID3v2Map[i][1], frameList.front()->toString(), aCharset);
2092  }
2093  }
2094 
2095  // TODO: bug 10932 -- make WCOP like this in taglib
2096  TagLib::ID3v2::FrameList frameList = frameListMap["WOAF"];
2097  if (!frameList.isEmpty())
2098  {
2099  TagLib::ID3v2::UrlLinkFrame* woaf =
2100  static_cast<TagLib::ID3v2::UrlLinkFrame*>(frameList.front());
2101  TagLib::String taglibURL = woaf->url();
2102 
2103  AddMetadataValue(SB_PROPERTY_ORIGINPAGE, taglibURL, aCharset);
2104  /* bug 10933 -- UrlLinkFrame does not support setText appropriately
2105  TagLib::String taglibTitle = woaf->text();
2106  AddMetadataValue(SB_PROPERTY_ORIGINPAGETITLE, taglibTitle, aCharset);*/
2107  }
2108 
2109  // If this is a local file, cache common album art in order to speed
2110  // up any subsequent calls to GetImageData.
2111  nsCString urlScheme;
2112  nsresult result = mpURL->GetScheme(urlScheme);
2113  NS_ENSURE_SUCCESS(result,/*void*/);
2114 
2115  if (urlScheme.Equals(NS_LITERAL_CSTRING("file"))) {
2116  sbAlbumArt *art = new sbAlbumArt();
2117  NS_ENSURE_TRUE(art,/*void*/);
2118  result = ReadImageID3v2(pTag,
2120  art->mimeType, &(art->dataLen), &(art->data));
2121  NS_ENSURE_SUCCESS(result,/*void*/);
2123  nsAutoPtr<sbAlbumArt>* cacheSlot = mCachedAlbumArt.AppendElement();
2124  NS_ENSURE_TRUE(cacheSlot,/*void*/);
2125  *cacheSlot = art;
2126 
2127  art = new sbAlbumArt();
2128  NS_ENSURE_TRUE(art,/*void*/);
2129  result = ReadImageID3v2(pTag, sbIMetadataHandler::METADATA_IMAGE_TYPE_OTHER,
2130  art->mimeType, &(art->dataLen), &(art->data));
2131  NS_ENSURE_SUCCESS(result,/*void*/);
2133  cacheSlot = mCachedAlbumArt.AppendElement();
2134  NS_ENSURE_TRUE(cacheSlot,/*void*/);
2135  *cacheSlot = art;
2136  }
2137 
2138  return;
2139 }
2140 
2141 
2142 /*******************************************************************************
2143  *
2144  * Private taglib metadata handler APE services.
2145  *
2146  ******************************************************************************/
2147 
2148 /*
2149  * APEMap Map of APE tag names to Songbird metadata
2150  * names.
2151  */
2152 
2153 // see http://wiki.hydrogenaudio.org/index.php?title=APE_key
2154 static const char *APEMap[][2] =
2155 {
2156  { "Subtitle", SB_PROPERTY_SUBTITLE },
2157 // { "Debut album", "original_album" },
2158 // { "File", "source_url" },
2159 };
2160 
2161 
2162 /*
2163  * ReadAPETags
2164  *
2165  * --> pTag APE tag object.
2166  *
2167  * This function reads APE tags from the APE tag object specified by pTag.
2168  * The read tags are added to the set of metadata values.
2169  */
2170 
2171 void sbMetadataHandlerTaglib::ReadAPETags(
2172  TagLib::APE::Tag *pTag)
2173 {
2174  TagLib::APE::ItemListMap itemListMap;
2175  int numMetadataEntries;
2176  int i;
2177 
2178  /* Do nothing if no tags are present. */
2179  if (!pTag) {
2180  return;
2181  }
2182 
2183  /* Get the item list map. */
2184  itemListMap = pTag->itemListMap();
2185 
2186  /* Add the metadata entries. */
2187  numMetadataEntries = sizeof(APEMap) / sizeof(APEMap[0]);
2188  for (i = 0; i < numMetadataEntries; i++) {
2189  TagLib::APE::Item item = itemListMap[APEMap[i][0]];
2190  if (!item.isEmpty()) {
2191  AddMetadataValue(APEMap[i][1], item.toString(), nsnull);
2192  }
2193  }
2194 }
2195 
2196 
2197 /*******************************************************************************
2198  *
2199  * Private taglib metadata handler Xiph services.
2200  *
2201  ******************************************************************************/
2202 
2203 /*
2204  * ReadXiphTags
2205  *
2206  * --> pTag Xiph tag object.
2207  *
2208  * This function reads Xiph tags from the Xiph tag object specified by pTag.
2209  * The read tags are added to the set of metadata values.
2210  */
2211 
2212 void sbMetadataHandlerTaglib::ReadXiphTags(
2213  TagLib::Ogg::XiphComment *pTag)
2214 {
2215  nsresult result = NS_OK;
2216 
2217  // If this is a local file, cache common album art in order to speed
2218  // up any subsequent calls to GetImageData.
2219  PRBool isFileURI;
2220  result = mpURL->SchemeIs("file", &isFileURI);
2221  NS_ENSURE_SUCCESS(result, /* void */);
2222  if (isFileURI) {
2223  nsAutoPtr<sbAlbumArt> art(new sbAlbumArt());
2224  NS_ENSURE_TRUE(art, /* void */);
2225  result = ReadImageOgg(
2226  pTag,
2228  art->mimeType, &(art->dataLen), &(art->data));
2229  NS_ENSURE_SUCCESS(result, /* void */);
2231  nsAutoPtr<sbAlbumArt>* cacheSlot = mCachedAlbumArt.AppendElement();
2232  NS_ENSURE_TRUE(cacheSlot, /* void */);
2233  *cacheSlot = art;
2234 
2235  art = new sbAlbumArt();
2236  NS_ENSURE_TRUE(art, /* void */);
2237  result = ReadImageOgg(
2238  pTag,
2240  art->mimeType, &(art->dataLen), &(art->data));
2241  NS_ENSURE_SUCCESS(result, /* void */);
2243  cacheSlot = mCachedAlbumArt.AppendElement();
2244  NS_ENSURE_TRUE(cacheSlot, /* void */);
2245  *cacheSlot = art;
2246  }
2247 }
2248 
2249 /*******************************************************************************
2250  *
2251  * Private taglib metadata handler services.
2252  *
2253  ******************************************************************************/
2254 
2255  /*
2256  * CheckChannelRestart
2257  *
2258  * Determine if the channel is being restarted, or if
2259  * it has failed in some way.
2260  */
2261 
2262  nsresult sbMetadataHandlerTaglib::CheckChannelRestart()
2263  {
2264  nsresult result = NS_OK;
2265 
2266  if (!mMetadataChannelID.IsEmpty())
2267  {
2268  result =
2269  mpTagLibChannelFileIOManager->GetChannelRestart(mMetadataChannelID,
2270  &mMetadataChannelRestart);
2271  NS_ENSURE_SUCCESS(result, result);
2272  if (!mMetadataChannelRestart) {
2273  PRUint64 size;
2274  result = mpTagLibChannelFileIOManager->GetChannelSize(mMetadataChannelID,
2275  &size);
2276  NS_ENSURE_SUCCESS(result, result);
2277 
2278  // we don't consider empty files to be valid because no data
2279  // could have been read. Taglib considers files valid by default
2280  // so we can't actually ask it for anything useful (that still
2281  // manages to separate the "read error" and "validly no tags" cases)
2282  if (size <= 0) {
2283  result = NS_ERROR_FAILURE;
2284  }
2285  }
2286  }
2287 
2288  return (result);
2289  }
2290 
2291 /*
2292  * ReadMetadata
2293  *
2294  * This function attempts to read the metadata. If the metadata is available,
2295  * this function extracts the metadata values and sets the metadata reading
2296  * complete condition to true.
2297  */
2298 
2299 nsresult sbMetadataHandlerTaglib::ReadMetadata()
2300 {
2301  nsCString fileExt;
2302  PRBool isValid = PR_FALSE;
2303  PRBool decodedFileExt = PR_FALSE;
2304  nsresult result = NS_OK;
2305 
2306  /* Get the metadata file extension. */
2307  result = mpURL->GetFileExtension(fileExt);
2308  if (NS_SUCCEEDED(result))
2309  ToLowerCase(fileExt);
2310 
2311  #if PR_LOGGING
2312  {
2313  nsCString spec;
2314  result = mpURL->GetSpec(spec);
2315  if (NS_SUCCEEDED(result)) {
2316  LOG(("sbMetadataHandlerTaglib:: Reading metadata from %s\n", spec.get()));
2317  }
2318  }
2319  #endif /* PR_LOGGING */
2320 
2321  /* Read the metadata using the file extension */
2322  /* to determine the metadata format. */
2323  if (NS_SUCCEEDED(result))
2324  {
2325  decodedFileExt = PR_TRUE;
2326  if (fileExt.Equals(NS_LITERAL_CSTRING("flac"))) {
2327  isValid = ReadFLACFile();
2328  } else if (fileExt.Equals(NS_LITERAL_CSTRING("mpc"))) {
2329  isValid = ReadMPCFile();
2330  } else if (fileExt.Equals(NS_LITERAL_CSTRING("mp3"))) {
2331  isValid = ReadMPEGFile();
2332  } else if (fileExt.Equals(NS_LITERAL_CSTRING("m4a")) ||
2333  fileExt.Equals(NS_LITERAL_CSTRING("m4r")) ||
2334  fileExt.Equals(NS_LITERAL_CSTRING("aac")) ||
2335  fileExt.Equals(NS_LITERAL_CSTRING("m4p"))) {
2336  isValid = ReadMP4File();
2337  } else if (fileExt.Equals(NS_LITERAL_CSTRING("m4v")) ||
2338  fileExt.Equals(NS_LITERAL_CSTRING("mp4"))) {
2339  isValid = ReadMP4File();
2340  // XXX Mook not always true for mp4, but good enough for now
2341  AddMetadataValue(SB_PROPERTY_CONTENTTYPE, NS_LITERAL_STRING("video"));
2342  } else if (fileExt.Equals(NS_LITERAL_CSTRING("ogg")) ||
2343  fileExt.Equals(NS_LITERAL_CSTRING("oga"))) {
2344  isValid = ReadOGAFile();
2345  } else if (fileExt.Equals(NS_LITERAL_CSTRING("ogv")) ||
2346  fileExt.Equals(NS_LITERAL_CSTRING("ogm")) ||
2347  fileExt.Equals(NS_LITERAL_CSTRING("ogx"))) {
2348  isValid = ReadOGGFile();
2349  // XXX Mook this is not always true for ogx, but we need something for now
2350  AddMetadataValue(SB_PROPERTY_CONTENTTYPE, NS_LITERAL_STRING("video"));
2351  } else if (fileExt.Equals(NS_LITERAL_CSTRING("wma"))) {
2352  isValid = ReadASFFile();
2353  } else if (fileExt.Equals(NS_LITERAL_CSTRING("wmv"))) {
2354  isValid = ReadASFFile();
2355 
2356  // Always set WMV files as video.
2357  AddMetadataValue(SB_PROPERTY_CONTENTTYPE, NS_LITERAL_STRING("video"));
2358  } else {
2359  decodedFileExt = PR_FALSE;
2360  }
2361  }
2362 
2363  /* If the file extension was not decoded, try */
2364  /* reading the metadata in different formats. */
2365  if ( NS_SUCCEEDED(result)
2366  && !decodedFileExt
2367  && !isValid
2368  && !mMetadataChannelRestart)
2369  {
2370  isValid = ReadMPEGFile();
2371  }
2372 
2373  /* Check if the metadata reading is complete. */
2374  if (isValid && !mMetadataChannelRestart)
2375  CompleteRead();
2376 
2377  /* Complete metadata reading upon error. */
2378  if (!NS_SUCCEEDED(result))
2379  CompleteRead();
2380 
2381  /* If we weren't able to read, make sure we return a failure code */
2382  if (!isValid) {
2383  result = NS_ERROR_FAILURE;
2384  }
2385 
2386  return (result);
2387 }
2388 
2389 
2390 /*
2391  * CompleteRead
2392  *
2393  * This function completes a metadata read operation.
2394  */
2395 
2396 void sbMetadataHandlerTaglib::CompleteRead()
2397 {
2398  /* Indicate that the metadata read operation has completed. */
2399  mCompleted = PR_TRUE;
2400 }
2401 
2402 /*
2403  * AddSeparatedNumbers
2404  *
2405  * --> value Value read from taglib
2406  * --> baseProperty Nightingale property for the base value
2407  * --> countProperty Nightingale property for the count value
2408  *
2409  * This function adds an value consisting of a base value and an optional
2410  * count. Example: "1/2" -> baseProperty will become 1, countProperty 2.
2411  */
2412 
2413 nsresult sbMetadataHandlerTaglib::AddSeparatedNumbers(
2415  const char *baseProperty,
2416  const char *countProperty)
2417 {
2418  TagLib::String SEP = TagLib::String("/", TagLib::String::UTF8);
2419 
2420  TagLib::StringList val = value.split(SEP);
2421  if (!val.isEmpty()){
2422  AddMetadataValue(baseProperty, (PRUint64) val[0].toInt());
2423  if (val.size() > 1){
2424  AddMetadataValue(countProperty, (PRUint64) val[1].toInt());
2425  }
2426  }
2427 
2428  return NS_OK;
2429 }
2430 
2431 
2432 /*
2433  * ReadFile
2434  *
2435  * --> pTagFile File from which to read.
2436  * --> aCharset The character encoding to use for reading
2437  * May be empty string (to mean no conversion)
2438  * On Windows, may also be "CP_ACP"
2439  *
2440  * <-- True if file has valid metadata.
2441  *
2442  * This function reads a base set of metadata from the file specified by
2443  * pTagFile.
2444  */
2445 
2446 PRBool sbMetadataHandlerTaglib::ReadFile(
2447  TagLib::File *pTagFile,
2448  const char *aCharset)
2449 {
2450  TagLib::Tag *pTag;
2451  TagLib::AudioProperties *pAudioProperties;
2452 
2453  /* We want to be sure we have a legit file ref. */
2454  if (!pTagFile || !pTagFile->isValid()) {
2455  return false; // not valid!
2456  }
2457 
2458  pTag = pTagFile->tag();
2459  if (pTag) {
2460  TagLib::PropertyMap properties = pTag->properties();
2461 
2462  // We need to emulate virtual here, as taglib can't mark them as virtual
2463  // for now. The following stuff can be deleted once taglib2 is used.
2464  if (dynamic_cast<TagLib::TagUnion*>(pTag)){
2465  TagLib::TagUnion* tagUnion = dynamic_cast<TagLib::TagUnion*>(pTag);
2466  if (tagUnion->tag(2)){
2467  TAGLIB1_PROPERTIES_WORKAROUND(tagUnion->tag(2));
2468  }
2469  if (tagUnion->tag(1)){
2470  TAGLIB1_PROPERTIES_WORKAROUND(tagUnion->tag(1));
2471  }
2472  if (tagUnion->tag(0)){
2473  TAGLIB1_PROPERTIES_WORKAROUND(tagUnion->tag(0));
2474  }
2475  } else {
2477  }
2478 
2479  // Default tags
2480  AddMetadataValue(SB_PROPERTY_TRACKNAME, pTag->title(), aCharset);
2481  AddMetadataValue(SB_PROPERTY_ARTISTNAME, pTag->artist(), aCharset);
2482  AddMetadataValue(SB_PROPERTY_ALBUMNAME, pTag->album(), aCharset);
2483  AddMetadataValue(SB_PROPERTY_COMMENT, pTag->comment(), aCharset);
2484  AddMetadataValue(SB_PROPERTY_GENRE, pTag->genre(), aCharset);
2485  AddMetadataValue(SB_PROPERTY_YEAR, (PRUint64)pTag->year());
2486 
2487  AddMetadataValue(SB_PROPERTY_ALBUMARTISTNAME,
2488  GET_PROPERTY("ALBUMARTIST"), aCharset);
2489  AddMetadataValue(SB_PROPERTY_LYRICS,
2490  GET_PROPERTY("LYRICS"), aCharset);
2491 // Disabling producer: it's not exposed anywhere in the UI anyway.
2492 // AddMetadataValue(SB_PROPERTY_PRODUCERNAME, pTag->producer(), aCharset);
2493  AddMetadataValue(SB_PROPERTY_COMPOSERNAME,
2494  GET_PROPERTY("COMPOSER"), aCharset);
2495  AddMetadataValue(SB_PROPERTY_CONDUCTORNAME,
2496  GET_PROPERTY("CONDUCTOR"), aCharset);
2497  AddMetadataValue(SB_PROPERTY_LYRICISTNAME,
2498  GET_PROPERTY("LYRICIST"), aCharset);
2499  AddMetadataValue(SB_PROPERTY_RECORDLABELNAME,
2500  GET_PROPERTY("PUBLISHER"), aCharset);
2501  AddMetadataValue(SB_PROPERTY_LANGUAGE,
2502  GET_PROPERTY("LANGUAGE"), aCharset);
2503  AddMetadataValue(SB_PROPERTY_KEY,
2504  GET_PROPERTY("INITIALKEY"), aCharset);
2505  AddMetadataValue(SB_PROPERTY_COPYRIGHT,
2506  GET_PROPERTY("COPYRIGHT"), aCharset);
2507  AddMetadataValue(SB_PROPERTY_COPYRIGHTURL,
2508  GET_PROPERTY("COPYRIGHTURL"), aCharset);
2509 
2510  TagLib::String bpm_value = GET_PROPERTY("BPM");
2511  AddMetadataValue(SB_PROPERTY_BPM, (PRUint64) bpm_value.toInt());
2512 
2513  AddMetadataValue(SB_PROPERTY_CONTENTTYPE, NS_LITERAL_STRING("audio"));
2514 
2515  AddSeparatedNumbers(GET_PROPERTY("DISCNUMBER"), SB_PROPERTY_DISCNUMBER,
2517  AddSeparatedNumbers(GET_PROPERTY("TRACKNUMBER"), SB_PROPERTY_TRACKNUMBER,
2519 
2520  AddMetadataValue(SB_PROPERTY_RATING, GET_PROPERTY("NIGHTINGALE-RATING"), aCharset);
2521 
2522  // Disabled, as we have no write support for this, see WriteID3v2 for details.
2523  // AddMetadataValue(SB_PROPERTY_ISPARTOFCOMPILATION, pTag->isCompilation());
2524  }
2525 
2526  pAudioProperties = pTagFile->audioProperties();
2527  if (pAudioProperties)
2528  {
2529  AddMetadataValue(SB_PROPERTY_BITRATE, (PRUint64)pAudioProperties->bitrate());
2530  AddMetadataValue(SB_PROPERTY_SAMPLERATE, (PRUint64)pAudioProperties->sampleRate());
2531  AddMetadataValue(SB_PROPERTY_DURATION,
2532  (PRUint64)pAudioProperties->length() * 1000000);
2533  AddMetadataValue(SB_PROPERTY_CHANNELS, (PRUint64)pAudioProperties->channels());
2534  }
2535 
2536  return true; // file was valid
2537 }
2538 
2539 /*
2540  * RunCharsetDetector
2541  *
2542  * Run the given nsICharsetDetector. Results will be available in
2543  * mLastConfidence and mLastCharset.
2544  */
2545 
2546 nsresult sbMetadataHandlerTaglib::RunCharsetDetector(
2547  nsICharsetDetector* aDetector,
2549 {
2550  NS_ENSURE_ARG_POINTER(aDetector);
2551  nsresult rv = NS_OK;
2552 
2553  mLastConfidence = eNoAnswerYet;
2554 
2555  nsCOMPtr<nsICharsetDetectionObserver> observer =
2556  do_QueryInterface( NS_ISUPPORTS_CAST(nsICharsetDetectionObserver*, this) );
2557  NS_ASSERTION(observer, "Um! We're supposed to be implementing this...");
2558 
2559  rv = aDetector->Init(observer);
2560  if (NS_SUCCEEDED(rv)) {
2561  PRBool isDone;
2562  // artificially inflate the buffer by repeating it a lot; this does
2563  // in fact help with the detection
2564  const PRUint32 chunkSize = aContent.size();
2565  std::string raw = aContent.toCString();
2566  PRUint32 currentSize = 0;
2567  while (currentSize < GUESS_CHARSET_MIN_CHAR_COUNT) {
2568  rv = aDetector->DoIt(raw.c_str(), chunkSize, &isDone);
2569  if (NS_FAILED(rv) || isDone) {
2570  break;
2571  }
2572  currentSize += chunkSize;
2573  }
2574  if (NS_SUCCEEDED(rv)) {
2575  rv = aDetector->Done();
2576  }
2577  }
2578  return rv;
2579 }
2580 
2581 /* nsICharsetDetectionObserver */
2583  const char *aCharset,
2584  nsDetectionConfident aConf)
2585 {
2586  mLastCharset.AssignLiteral(aCharset);
2587  mLastConfidence = aConf;
2588  return NS_OK;
2589 }
2590 
2591 inline void toMozString(TagLib::String aString, nsAString& aResult)
2592 {
2593  CopyUTF8toUTF16(nsDependentCString(aString.toCString(true)), aResult);
2594 }
2595 
2596 // TODO: needed with current taglib?
2597 void sbMetadataHandlerTaglib::ConvertCharset(
2598  TagLib::String aString,
2599  const char *aCharset,
2600  nsAString& aResult)
2601 {
2602  aResult.Truncate();
2603 
2604  // If UTF16 or ASCII, or we have no idea,
2605  // just leave the string as-is
2606  if (!aCharset || !*aCharset ||
2607  !strcmp("UTF-8", aCharset) ||
2608  !strcmp("us-ascii", aCharset))
2609 
2610  {
2611  LOG(("sbMetadataHandlerTaglib::ConvertCharset: not converting to \"%s\"",
2612  aCharset ? aCharset : "(null)"
2613  ));
2614 
2615  toMozString(aString, aResult);
2616  return;
2617  }
2618 
2619  std::string data = aString.toCString(false);
2620 #if XP_WIN
2621  if (strcmp("CP_ACP", aCharset) == 0) {
2622  // convert to CP_ACP
2623  int size = ::MultiByteToWideChar( CP_ACP,
2624  MB_ERR_INVALID_CHARS,
2625  data.c_str(),
2626  data.length(),
2627  nsnull,
2628  0 );
2629  if (size) {
2630  // convert to CP_ACP
2631  PRUnichar *wstr = reinterpret_cast< PRUnichar * >( NS_Alloc( (size + 1) * sizeof( PRUnichar ) ) );
2632  if (wstr) {
2633  int read = MultiByteToWideChar( CP_ACP,
2634  MB_ERR_INVALID_CHARS,
2635  data.c_str(),
2636  data.length(),
2637  wstr,
2638  size );
2639  NS_ASSERTION(size == read, "Win32 Current Codepage conversion failed.");
2640 
2641  aResult.Assign(wstr, size);
2642  NS_Free(wstr);
2643  return;
2644  }
2645  }
2646  }
2647 #endif
2648 
2649  // convert via Mozilla
2650 
2651  nsresult rv;
2652  nsCOMPtr<nsICharsetConverterManager> converterManager =
2653  do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
2654  NS_ENSURE_SUCCESS(rv, toMozString(aString, aResult));
2655 
2656  nsCOMPtr<nsIUnicodeDecoder> decoder;
2657  rv = converterManager->GetUnicodeDecoderRaw(aCharset, getter_AddRefs(decoder));
2658  NS_ENSURE_SUCCESS(rv, toMozString(aString, aResult));
2659 
2660  PRInt32 dataLen = data.length();
2661  PRInt32 size;
2662  rv = decoder->GetMaxLength(data.c_str(), dataLen, &size);
2663  NS_ENSURE_SUCCESS(rv, toMozString(aString, aResult));
2664 
2665  PRUnichar *wstr = reinterpret_cast< PRUnichar * >( NS_Alloc( (size + 1) * sizeof( PRUnichar ) ) );
2666  rv = decoder->Convert(data.c_str(), &dataLen, wstr, &size);
2667  if (NS_SUCCEEDED(rv)) {
2668  aResult.Assign(wstr, size);
2669  }
2670  NS_Free(wstr);
2671  NS_ENSURE_SUCCESS(rv, toMozString(aString, aResult));
2672 
2673  return;
2674 }
2675 
2676 
2677 /*
2678  * ReadFLACFile
2679  * <-- True if file has valid FLAC metadata.
2680  *
2681  * This function reads metadata from the FLAC file with the file path
2682  * specified by mMetadataPath.
2683  */
2684 
2685 PRBool sbMetadataHandlerTaglib::ReadFLACFile()
2686 {
2687  nsAutoPtr<TagLib::FLAC::File> pTagFile;
2688  PRBool isValid = PR_TRUE;
2689  nsresult result = NS_OK;
2690 
2691  /* Get the file path in the proper format for the platform. */
2692  #if XP_WIN
2693  NS_ConvertUTF8toUTF16 filePath(mMetadataPath);
2694  #else
2695  nsACString &filePath = mMetadataPath;
2696  #endif
2697 
2698  /* Open and read the metadata file. */
2699  pTagFile = new TagLib::FLAC::File(filePath.BeginReading());
2700  if (!pTagFile)
2701  result = NS_ERROR_OUT_OF_MEMORY;
2702  if (!pTagFile->isOpen())
2703  result = NS_ERROR_INVALID_ARG;
2704  if (NS_SUCCEEDED(result))
2705  result = CheckChannelRestart();
2706 
2707  /* Read the base file metadata. */
2708  if (NS_SUCCEEDED(result) && isValid)
2709  isValid = ReadFile(pTagFile);
2710 
2711  /* Read the Xiph comment metadata. */
2712  if (NS_SUCCEEDED(result) && isValid)
2713  ReadXiphTags(pTagFile->xiphComment());
2714 
2715  /* FLAC has an additional album cover handler */
2716  // If this is a local file, cache common album art in order to speed
2717  // up any subsequent calls to GetImageData.
2718  PRBool isFileURI;
2719  result = mpURL->SchemeIs("file", &isFileURI);
2720  NS_ENSURE_SUCCESS(result, PR_FALSE);
2721  if (isFileURI) {
2722  // We clear the cache from the XiphComment, to ensure FLAC is first
2723  mCachedAlbumArt.Clear();
2724  nsAutoPtr<sbAlbumArt> art(new sbAlbumArt());
2725  NS_ENSURE_TRUE(art, PR_FALSE);
2726  result = ReadImageFlac(
2727  pTagFile,
2729  art->mimeType, &(art->dataLen), &(art->data));
2730  NS_ENSURE_SUCCESS(result, PR_FALSE);
2732  nsAutoPtr<sbAlbumArt>* cacheSlot = mCachedAlbumArt.AppendElement();
2733  NS_ENSURE_TRUE(cacheSlot, PR_FALSE);
2734  *cacheSlot = art;
2735 
2736  art = new sbAlbumArt();
2737  NS_ENSURE_TRUE(art, PR_FALSE);
2738  result = ReadImageFlac(
2739  pTagFile,
2741  art->mimeType, &(art->dataLen), &(art->data));
2742  NS_ENSURE_SUCCESS(result, PR_FALSE);
2744  cacheSlot = mCachedAlbumArt.AppendElement();
2745  NS_ENSURE_TRUE(cacheSlot, PR_FALSE);
2746  *cacheSlot = art;
2747  }
2748 
2749  /* File is invalid on any error. */
2750  if (NS_FAILED(result))
2751  isValid = PR_FALSE;
2752 
2753  return (isValid);
2754 }
2755 
2756 
2757 /*
2758  * ReadMPCFile
2759  * <-- True if file has valid MPC metadata.
2760  *
2761  * This function reads metadata from the MPC file with the file path specified
2762  * by mMetadataPath.
2763  */
2764 
2765 PRBool sbMetadataHandlerTaglib::ReadMPCFile()
2766 {
2767  nsAutoPtr<TagLib::MPC::File> pTagFile;
2768  PRBool isValid = PR_TRUE;
2769  nsresult result = NS_OK;
2770 
2771  /* Get the file path in the proper format for the platform. */
2772  #if XP_WIN
2773  NS_ConvertUTF8toUTF16 filePath(mMetadataPath);
2774  #else
2775  nsACString &filePath = mMetadataPath;
2776  #endif
2777 
2778  pTagFile = new TagLib::MPC::File(filePath.BeginReading());
2779  if (!pTagFile)
2780  result = NS_ERROR_OUT_OF_MEMORY;
2781  if (!pTagFile->isOpen())
2782  result = NS_ERROR_INVALID_ARG;
2783  if (NS_SUCCEEDED(result))
2784  result = CheckChannelRestart();
2785 
2786  /* Read the base file metadata. */
2787  if (NS_SUCCEEDED(result) && isValid)
2788  isValid = ReadFile(pTagFile);
2789 
2790  /* Read the APE metadata */
2791  if (NS_SUCCEEDED(result) && isValid)
2792  ReadAPETags(pTagFile->APETag());
2793 
2794  /* File is invalid on any error. */
2795  if (NS_FAILED(result))
2796  isValid = PR_FALSE;
2797 
2798  return (isValid);
2799 }
2800 
2801 
2802 /*
2803  * ReadMPEGFile
2804  * <-- True if file has valid MPEG metadata.
2805  *
2806  * This function reads metadata from the MPEG file with the file path
2807  * specified by mMetadataPath.
2808  */
2809 
2810 PRBool sbMetadataHandlerTaglib::ReadMPEGFile()
2811 {
2812  nsAutoPtr<TagLib::MPEG::File> pTagFile;
2813  PRBool isValid = PR_TRUE;
2814  nsresult result = NS_OK;
2815 
2816  /* Get the file path in the proper format for the platform. */
2817  #if XP_WIN
2818  NS_ConvertUTF8toUTF16 filePath(mMetadataPath);
2819  #else
2820  nsACString &filePath = mMetadataPath;
2821  #endif
2822 
2823  pTagFile = new TagLib::MPEG::File(filePath.BeginReading());
2824  if (!pTagFile)
2825  result = NS_ERROR_OUT_OF_MEMORY;
2826  if (!pTagFile->isOpen())
2827  result = NS_ERROR_INVALID_ARG;
2828  if (NS_SUCCEEDED(result))
2829  result = CheckChannelRestart();
2830 
2831  /* Guess the charset */
2832  nsCString charset;
2833  if (NS_SUCCEEDED(result)) {
2834  // We take UTF-8 for now, as the new taglib should handle the encoding
2835  charset.AssignLiteral("UTF-8");
2836  }
2837 
2838  /* Read the base file metadata. */
2839  if (NS_SUCCEEDED(result) && isValid)
2840  isValid = ReadFile(pTagFile, charset.BeginReading());
2841 
2842  /* Read the ID3v2 metadata. */
2843  if (NS_SUCCEEDED(result) && isValid)
2844  ReadID3v2Tags(pTagFile->ID3v2Tag(), charset.BeginReading());
2845 
2846  /* Read the APE metadata */
2847  if (NS_SUCCEEDED(result) && isValid)
2848  ReadAPETags(pTagFile->APETag());
2849 
2850  /* File is invalid on any error. */
2851  if (NS_FAILED(result))
2852  isValid = PR_FALSE;
2853 
2854  return (isValid);
2855 }
2856 
2857 
2858 /*
2859  * ReadASFFile
2860  * <-- True if file has valid ASF metadata.
2861  *
2862  * This function reads metadata from the ASF file with the file path
2863  * specified by mMetadataPath.
2864  */
2865 
2866 PRBool sbMetadataHandlerTaglib::ReadASFFile()
2867 {
2868  nsAutoPtr<TagLib::ASF::File> pTagFile;
2869  PRBool isValid = PR_TRUE;
2870  nsresult result = NS_OK;
2871 
2872  /* Get the file path in the proper format for the platform. */
2873  #if XP_WIN
2874  NS_ConvertUTF8toUTF16 filePath(mMetadataPath);
2875  #else
2876  nsACString &filePath = mMetadataPath;
2877  #endif
2878 
2879  pTagFile = new TagLib::ASF::File(filePath.BeginReading());
2880  if (!pTagFile)
2881  result = NS_ERROR_OUT_OF_MEMORY;
2882  if (!pTagFile->isOpen())
2883  result = NS_ERROR_INVALID_ARG;
2884  if (NS_SUCCEEDED(result))
2885  result = CheckChannelRestart();
2886 
2887  /* Read the base file metadata. */
2888  if (NS_SUCCEEDED(result) && isValid)
2889  isValid = ReadFile(pTagFile, "");
2890 
2891  /* File is invalid on any error. */
2892  if (NS_FAILED(result))
2893  isValid = PR_FALSE;
2894 
2895  return (isValid);
2896 }
2897 
2898 /*
2899  * ReadMP4File
2900  * <-- True if file has valid MP4 metadata.
2901  *
2902  * This function reads metadata from the MP4 file with the file path specified
2903  * by mMetadataPath.
2904  */
2905 
2906 PRBool sbMetadataHandlerTaglib::ReadMP4File()
2907 {
2908  nsAutoPtr<TagLib::MP4::File> pTagFile;
2909  PRBool isValid = PR_TRUE;
2910  nsresult result = NS_OK;
2911 
2912  /* Get the file path in the proper format for the platform. */
2913  #if XP_WIN
2914  NS_ConvertUTF8toUTF16 filePath(mMetadataPath);
2915  #else
2916  nsACString &filePath = mMetadataPath;
2917  #endif
2918 
2919  pTagFile = new TagLib::MP4::File(filePath.BeginReading());
2920  if (!pTagFile)
2921  result = NS_ERROR_OUT_OF_MEMORY;
2922  if (!pTagFile->isOpen())
2923  result = NS_ERROR_INVALID_ARG;
2924  if (NS_SUCCEEDED(result))
2925  result = CheckChannelRestart();
2926 
2927  /* Read the base file metadata. */
2928  if (NS_SUCCEEDED(result))
2929  isValid = ReadFile(pTagFile);
2930 
2931  if (NS_SUCCEEDED(result) && isValid) {
2932  // If this is a local file, cache common album art in order to speed
2933  // up any subsequent calls to GetImageData.
2934  PRBool isFileURI;
2935  result = mpURL->SchemeIs("file", &isFileURI);
2936  NS_ENSURE_SUCCESS(result, PR_FALSE);
2937  if (isFileURI) {
2938  nsAutoPtr<sbAlbumArt> art(new sbAlbumArt());
2939  NS_ENSURE_TRUE(art, PR_FALSE);
2940  result = ReadImageITunes(
2941  static_cast<TagLib::MP4::Tag*>(pTagFile->tag()),
2942  art->mimeType, &(art->dataLen), &(art->data));
2943  NS_ENSURE_SUCCESS(result, PR_FALSE);
2945  nsAutoPtr<sbAlbumArt>* cacheSlot = mCachedAlbumArt.AppendElement();
2946  NS_ENSURE_TRUE(cacheSlot, PR_FALSE);
2947  *cacheSlot = art;
2948  }
2949  }
2950 
2951  // XXX Mook: We should be looking at whether the
2952  // moov/trak/mdia/minf/stbl/stsd/drms box exists
2953  // taglib m4a support doesn't support
2954  // exposing boxes to the outside world (nor does it understand DRM).
2955  /*
2956  pavel@salsita: TagLib does not publicize the atom tree logic,
2957  fortunately it supports searching in the file stream so let's
2958  incrementally find all required atoms.
2959  For now, ignore that there should be a serialization of tree structure.
2960  Just the fact that there are all these strings sequentially is good enough.
2961  Meanings for the atom names taken from http://www.mp4ra.org/atoms.html
2962 
2963  The toplevel "moov" atom must be iterated properly (i.e. by reading and
2964  summing up the offsets) instead of find()-ing because MP4::File regularly
2965  does not find the atom block when it's appended at the end of file
2966  (after the media stream). Perhaps a TagLib partial match logic bug?
2967  */
2968  if (NS_SUCCEEDED(result)) {
2969  static const TagLib::ByteVector DRM_ATOMS[] = {
2970  TagLib::ByteVector("moov"), // ISO container for metadata
2971  TagLib::ByteVector("trak"), // ISO individual track [+]
2972  TagLib::ByteVector("mdia"), // ISO media information on a track
2973  TagLib::ByteVector("minf"), // ISO media information container
2974  TagLib::ByteVector("stbl"), // ISO sample table box
2975  TagLib::ByteVector("stsd"), // ISO sample descriptions
2976  TagLib::ByteVector("drms"), // nonstandard, DRM box/atom
2977  TagLib::ByteVector("sinf"), // ISO, protection scheme information box/atom
2978  TagLib::ByteVector("schi"), // ISO, scheme information box
2979  TagLib::ByteVector("priv"), // nonstandard, DRM private key
2980  };
2981  // [+] there can be multiple tracks in one moov (and perhaps some other atoms
2982  // down the tree as well). But because we are searching linearly, the expected
2983  // next atom will be hit anyway (even if we pass more occurences of the upper
2984  // atoms in the scanning)
2985  static const size_t DRM_ATOM_COUNT = sizeof(DRM_ATOMS)/sizeof(TagLib::ByteVector);
2986  static const size_t ATOM_LEN_BYTES = 4;
2987 
2988  long lPos = 0; // byte offset into file
2989  long atomLen = 0;
2990  size_t idx = 0; // index into DRM_ATOMS
2991  pTagFile->seek(lPos,TagLib::File::Beginning);
2992  // find the toplevel atom incrementally
2993  while(pTagFile->tell() < pTagFile->length()) {
2994  // Format of atom is
2995  // [L=4B length][4B ASCII atom name][L - 8, atom content]
2996  atomLen = pTagFile->readBlock(ATOM_LEN_BYTES).toUInt();
2997  // Do not count passed atom header (length+name)
2998  // into further seeking and reading
2999  atomLen -= 2*ATOM_LEN_BYTES;
3000  if( pTagFile->readBlock(ATOM_LEN_BYTES) == DRM_ATOMS[idx] ) {
3001  // found desired root, break out to dig the tree
3002  idx++;
3003  break;
3004  }
3005  // jump to the next atom
3006  pTagFile->seek(atomLen,TagLib::File::Current);
3007  }
3008  if( (idx > 0) && // "moov" atom has been actually found
3009  // sanity check on the length
3010  (atomLen > 0) && (pTagFile->tell()+atomLen <= pTagFile->length())
3011  ) {
3012  // read up the remaining metadata block
3013  ByteVector moovBuffer = pTagFile->readBlock(atomLen);
3014  lPos = 0;
3015  while( lPos < atomLen ) {
3016  // start at last known position, return -1 when not found
3017  lPos = moovBuffer.find(DRM_ATOMS[idx++],lPos);
3018  if( lPos == -1 ) {
3019  break; // not found
3020  } else if( idx == DRM_ATOM_COUNT ) {
3021  // found the last atom
3022  result = AddMetadataValue(SB_PROPERTY_ISDRMPROTECTED, true);
3023  break;
3024  }
3025  }
3026  }
3027  }
3028 
3029  /* File is invalid on any error. */
3030  if (NS_FAILED(result))
3031  isValid = PR_FALSE;
3032 
3033  return (isValid);
3034 }
3035 
3036 
3037 /*
3038  * ReadOGGFile
3039  * <-- True if file has valid OGG metadata.
3040  *
3041  * This function reads metadata from the OGG file with the file path specified
3042  * by mMetadataPath.
3043  */
3044 
3045 PRBool sbMetadataHandlerTaglib::ReadOGGFile()
3046 {
3047  nsAutoPtr<TagLib::Vorbis::File> pTagFile;
3048  PRBool isValid = PR_TRUE;
3049  nsresult result = NS_OK;
3050 
3051  /* Get the file path in the proper format for the platform. */
3052  #if XP_WIN
3053  NS_ConvertUTF8toUTF16 filePath(mMetadataPath);
3054  #else
3055  nsACString &filePath = mMetadataPath;
3056  #endif
3057 
3058  pTagFile = new TagLib::Vorbis::File(filePath.BeginReading());
3059  if (!pTagFile)
3060  result = NS_ERROR_OUT_OF_MEMORY;
3061  if (!pTagFile->isOpen())
3062  result = NS_ERROR_INVALID_ARG;
3063  if (NS_SUCCEEDED(result))
3064  result = CheckChannelRestart();
3065 
3066  /* Read the base file metadata. */
3067  if (NS_SUCCEEDED(result))
3068  isValid = ReadFile(pTagFile);
3069 
3070  /* Read the Xiph metadata. */
3071  if (NS_SUCCEEDED(result) && isValid)
3072  ReadXiphTags(pTagFile->tag());
3073 
3074  /* File is invalid on any error. */
3075  if (NS_FAILED(result))
3076  isValid = PR_FALSE;
3077 
3078  return (isValid);
3079 }
3080 
3081 /*
3082  * ReadOGAFile
3083  * <-- True if file has valid OGG metadata.
3084  *
3085  * This function reads metadata from the OGG file with the file path specified
3086  * by mMetadataPath, and is special-cased to try ogg vorbis/ogg flac.
3087  */
3088 
3089 PRBool sbMetadataHandlerTaglib::ReadOGAFile()
3090 {
3091  nsAutoPtr<TagLib::Ogg::FLAC::File> pTagFile;
3092  PRBool isValid = PR_TRUE;
3093  nsresult result = NS_OK;
3094 
3095  /* Get the file path in the proper format for the platform. */
3096  #if XP_WIN
3097  NS_ConvertUTF8toUTF16 filePath(mMetadataPath);
3098  #else
3099  nsACString &filePath = mMetadataPath;
3100  #endif
3101 
3102  pTagFile = new TagLib::Ogg::FLAC::File(filePath.BeginReading());
3103  if (!pTagFile)
3104  result = NS_ERROR_OUT_OF_MEMORY;
3105  if (!pTagFile->isOpen())
3106  result = NS_ERROR_INVALID_ARG;
3107  if (NS_SUCCEEDED(result))
3108  result = CheckChannelRestart();
3109 
3110  /* Read the base file metadata. */
3111  if (NS_SUCCEEDED(result))
3112  isValid = ReadFile(pTagFile);
3113 
3114  // If we have an invalid Ogg FLAC file, this is probably Ogg Vorbis.
3115  // and if it isn't, we're just going to give up.
3116  // Switching the metadata handler to use FileRef for reading
3117  // would be a good idea, since that's how we write and this is
3118  // also fixed there.
3119  if (!isValid) {
3120  ReadOGGFile();
3121  }
3122 
3123  /* File is invalid on any error. */
3124  if (NS_FAILED(result))
3125  isValid = PR_FALSE;
3126 
3127  return (isValid);
3128 }
3129 
3130 /*
3131  * AddMetadataValue
3132  *
3133  * --> name Metadata name.
3134  * --> value Metadata value.
3135  *
3136  * This function adds the metadata value specified by value with the name
3137  * specified by name to the set of metadata values.
3138  */
3139 
3140 nsresult sbMetadataHandlerTaglib::AddMetadataValue(
3141  const char *name,
3142  TagLib::String value,
3143  const char *charset)
3144 {
3145  nsresult result = NS_OK;
3146 
3147  /* Add the metadata value. */
3148  nsAutoString strValue;
3149  ConvertCharset(value, charset, strValue);
3150  result = mpMetadataPropertyArray->AppendProperty
3151  (NS_ConvertASCIItoUTF16(name),
3152  strValue);
3153 
3154  return (result);
3155 }
3156 
3157 
3158 /*
3159  * AddMetadataValue
3160  *
3161  * --> name Metadata name.
3162  * --> value Metadata value.
3163  *
3164  * This function adds the metadata value specified by value with the name
3165  * specified by name to the set of metadata values.
3166  */
3167 
3168 nsresult sbMetadataHandlerTaglib::AddMetadataValue(
3169  const char *name,
3170  PRUint64 value)
3171 {
3172  nsresult result = NS_OK;
3173 
3174  // Zero indicates no value
3175  if (value == 0) {
3176  return result;
3177  }
3178 
3179  /* Convert the integer value into a string. */
3180  sbAutoString valueString(value);
3181 
3182  /* Add the metadata value. */
3183  result = mpMetadataPropertyArray->AppendProperty
3184  (NS_ConvertASCIItoUTF16(name),
3185  valueString);
3186 
3187  return (result);
3188 }
3189 
3190 /*
3191  * AddMetadataValue
3192  *
3193  * --> name Metadata name.
3194  * --> value Metadata value.
3195  *
3196  * This function adds the metadata value specified by value with the name
3197  * specified by name to the set of metadata values.
3198  */
3199 
3200 nsresult sbMetadataHandlerTaglib::AddMetadataValue(
3201  const char *name,
3202  bool value)
3203 {
3204  nsresult result = NS_OK;
3205 
3206  // If the property is false, we don't add it.
3207  if (!value) {
3208  return result;
3209  }
3210 
3211  /* Convert the boolean value into a string. */
3212  sbAutoString valueString(value);
3213 
3214  /* Add the metadata value. */
3215  result = mpMetadataPropertyArray->AppendProperty
3216  (NS_ConvertASCIItoUTF16(name),
3217  valueString);
3218 
3219  return (result);
3220 }
3221 
3222 nsresult sbMetadataHandlerTaglib::AddMetadataValue(
3223  const char *name,
3224  const nsAString &value)
3225 {
3226  nsresult result = NS_OK;
3227 
3228  if(value.IsEmpty()) {
3229  return (result);
3230  }
3231 
3232  result = mpMetadataPropertyArray->AppendProperty(NS_ConvertASCIItoUTF16(name),
3233  value);
3234  return (result);
3235 }
3236 
3237 // Note on the WriteXXX functions
3238 // These functions take care of writing Tag-Specific values. You should look
3239 // into them if you plan to add new tags or want to fix issues with specific
3240 // tags not handled directly with taglib EXCEPT COVERS (they are handled
3241 // above). TODO in far future once the PropertyMap supports binary stuff:
3242 // Move covers here, too?
3243 // To prevent the same investigations again:
3244 // mpMetadataPropertyArray->GetPropertyValue gives us ALL CHANGED TAGS,
3245 // including the ones we should delete (empty string). This means it is safe to
3246 // copy existing data and simply change the values we get from the
3247 // PropertyArray.
3248 
3249 /*
3250  * WriteBasic
3251  *
3252  * --> properties PropertyMap to write the metadata to
3253  * This function adds basic metadata to the given PropertyMap. Basic metadata
3254  * is defined as the values that have a unified key within TagLib's property
3255  * map for all tags supporting that specific value.
3256  */
3257 nsresult sbMetadataHandlerTaglib::WriteBasic(
3258  TagLib::PropertyMap *properties)
3259 {
3260  LOG(("Writing Tag-unspecific values"));
3261  nsAutoString propertyValue;
3262 
3263  // Basic stuff
3264  nsresult tmp_result;
3265  WRITE_PROPERTY(tmp_result, SB_PROPERTY_TRACKNAME, "TITLE");
3266  WRITE_PROPERTY(tmp_result, SB_PROPERTY_ARTISTNAME, "ARTIST");
3267  WRITE_PROPERTY(tmp_result, SB_PROPERTY_ALBUMNAME, "ALBUM");
3268  WRITE_PROPERTY(tmp_result, SB_PROPERTY_COMMENT, "COMMENT");
3269  WRITE_PROPERTY(tmp_result, SB_PROPERTY_GENRE, "GENRE");
3270  // Taglib uses "DATE" internally, so I guess "YEAR" is deprecated.
3271  WRITE_PROPERTY(tmp_result, SB_PROPERTY_YEAR, "DATE");
3272  if (NS_SUCCEEDED(tmp_result)) {
3273  TagLib::String key = TagLib::String("YEAR", TagLib::String::UTF8);
3274  properties->erase(key);
3275  }
3276 
3277  // Advanced stuff that might not be handled by all formats, but is handled
3278  // by TagLib
3279  WRITE_PROPERTY(tmp_result, SB_PROPERTY_ALBUMARTISTNAME, "ALBUMARTIST");
3280  WRITE_PROPERTY(tmp_result, SB_PROPERTY_LYRICS, "LYRICS");
3281  WRITE_PROPERTY(tmp_result, SB_PROPERTY_COMPOSERNAME, "COMPOSER");
3282  WRITE_PROPERTY(tmp_result, SB_PROPERTY_CONDUCTORNAME, "CONDUCTOR");
3283  WRITE_PROPERTY(tmp_result, SB_PROPERTY_LYRICISTNAME, "LYRICIST");
3284  WRITE_PROPERTY(tmp_result, SB_PROPERTY_RECORDLABELNAME, "PUBLISHER");
3285  WRITE_PROPERTY(tmp_result, SB_PROPERTY_LANGUAGE, "LANGUAGE");
3286  WRITE_PROPERTY(tmp_result, SB_PROPERTY_COPYRIGHT, "COPYRIGHT");
3287  WRITE_PROPERTY(tmp_result, SB_PROPERTY_COPYRIGHTURL, "COPYRIGHTURL");
3288  WRITE_PROPERTY(tmp_result, SB_PROPERTY_BPM, "BPM");
3289  WRITE_PROPERTY(tmp_result, SB_PROPERTY_KEY, "INITIALKEY");
3290 
3291  // The following is tag-specific due to us supporting the total number of
3292  // tracks/discs, listed here for better code overview:
3293  // WRITE_PROPERTY(tmp_result, SB_PROPERTY_TRACKNUMBER, "TRACKNUMBER");
3294  // WRITE_PROPERTY(tmp_result, SB_PROPERTY_DISCNUMBER, "DISCNUMBER");
3295 
3296  return NS_OK;
3297 }
3298 
3299 /*
3300  * WriteSeparatedNumbers
3301  *
3302  * --> properties PropertyMap to write the metadata to
3303  * --> target TagLib-Key in properties to write to
3304  * --> baseProperty Nightingale-Property of the base value
3305  * --> countProperty Nightingale-Property of the count value
3306  * This function adds a combined "base/count" entry to a property map,
3307  * as required for track- and discnumber. The function preserves existing
3308  * values in the tag if they haven't been changed.
3309  */
3310 nsresult sbMetadataHandlerTaglib::WriteSeparatedNumbers(
3311  TagLib::PropertyMap *properties,
3313  const nsAString &baseProperty,
3314  const nsAString &countProperty)
3315 {
3316  TagLib::StringList valueList;
3317  nsAutoString propertyValue;
3318  TagLib::String SEP = TagLib::String("/", TagLib::String::UTF8);
3319  TagLib::String DEFAULT = TagLib::String("0", TagLib::String::UTF8);
3320 
3321  bool changed = false;
3322  TagLib::String base = DEFAULT;
3323  TagLib::String count = DEFAULT;
3324 
3325  valueList = (*properties)[target];
3326  if (!valueList.isEmpty()){
3327  TagLib::StringList old = valueList[0].split(SEP);
3328  if (!old.isEmpty()){
3329  base = old[0];
3330  if (old.size() > 1){
3331  count = old[1];
3332  }
3333  }
3334  }
3335 
3336  nsresult tmp_result = mpMetadataPropertyArray->GetPropertyValue(
3337  baseProperty, propertyValue);
3338  if (NS_SUCCEEDED(tmp_result)) {
3339  changed = true;
3341  NS_ConvertUTF16toUTF8(propertyValue).BeginReading(),
3342  TagLib::String::UTF8);
3343  if (value.isEmpty()){
3344  base = DEFAULT;
3345  } else {
3346  base = value;
3347  }
3348  }
3349  tmp_result = mpMetadataPropertyArray->GetPropertyValue(
3350  countProperty, propertyValue);
3351  if (NS_SUCCEEDED(tmp_result)) {
3352  changed = true;
3354  NS_ConvertUTF16toUTF8(propertyValue).BeginReading(),
3355  TagLib::String::UTF8);
3356  if (value.isEmpty()){
3357  count = DEFAULT;
3358  } else {
3359  count = value;
3360  }
3361  }
3362 
3363  if (changed){
3364  if (count != DEFAULT){
3365  base += SEP;
3366  base += count;
3367  }
3368  properties->erase(target);
3369  if (base != DEFAULT){
3370  properties->insert(target, base);
3371  }
3372  }
3373 
3374  return NS_OK;
3375 }
3376 
3377 /*
3378  * WriteAPE
3379  *
3380  * --> tag Tag to write the metadata to
3381  * This function adds metadata to the given APE tag.
3382  */
3383 nsresult sbMetadataHandlerTaglib::WriteAPE(
3384  TagLib::APE::Tag *tag)
3385 {
3386  LOG(("Writing APE Tag"));
3387  nsresult result = NS_OK;
3388  nsAutoString propertyValue;
3389 
3390  TagLib::PropertyMap prop = tag->properties();
3391  TagLib::PropertyMap* properties = &prop;
3392 
3393  // General Tags
3394  result = WriteBasic(properties);
3395  if (!NS_SUCCEEDED(result)) {
3396  return result;
3397  }
3398 
3399  // Track- and discnumbers
3400  result = WriteSeparatedNumbers(properties,
3401  TagLib::String("TRACKNUMBER", TagLib::String::UTF8),
3402  NS_LITERAL_STRING(SB_PROPERTY_TRACKNUMBER),
3403  NS_LITERAL_STRING(SB_PROPERTY_TOTALTRACKS));
3404  if (!NS_SUCCEEDED(result)) {
3405  return result;
3406  }
3407  result = WriteSeparatedNumbers(properties,
3408  TagLib::String("DISCNUMBER", TagLib::String::UTF8),
3409  NS_LITERAL_STRING(SB_PROPERTY_DISCNUMBER),
3410  NS_LITERAL_STRING(SB_PROPERTY_TOTALDISCS));
3411  if (!NS_SUCCEEDED(result)) {
3412  return result;
3413  }
3414 
3415  // Additional APE Tags, TODO: discuss APE mapping in general.
3416  nsresult tmp_result;
3417  WRITE_PROPERTY(tmp_result, SB_PROPERTY_RATING, "NIGHTINGALE-RATING");
3418 
3419  tag->setProperties(prop);
3420 
3421  return result;
3422 }
3423 
3424 /*
3425  * WriteASF
3426  *
3427  * --> tag Tag to write the metadata to
3428  * This function adds metadata to the given ASF tag.
3429  */
3430 nsresult sbMetadataHandlerTaglib::WriteASF(
3431  TagLib::ASF::Tag *tag)
3432 {
3433  LOG(("Writing ASF Tag"));
3434  nsresult result = NS_OK;
3435  nsAutoString propertyValue;
3436 
3437  TagLib::PropertyMap prop = tag->properties();
3438  TagLib::PropertyMap* properties = &prop;
3439 
3440  // General Tags
3441  result = WriteBasic(properties);
3442  if (!NS_SUCCEEDED(result)) {
3443  return result;
3444  }
3445 
3446  // Track- and discnumbers
3447  nsresult tmp_result;
3448  WRITE_PROPERTY(tmp_result, SB_PROPERTY_TRACKNUMBER, "TRACKNUMBER");
3449  WRITE_PROPERTY(tmp_result, SB_PROPERTY_DISCNUMBER, "DISCNUMBER");
3450 
3451  // TODO: ASF did not yet recive the update to PropertyMap for non-default
3452  // tags. We probably should write the tags here using tag->removeItem() and
3453  // tag->setAttribute()
3454 
3455  tag->setProperties(prop);
3456 
3457  return result;
3458 }
3459 
3460 /*
3461  * WriteID3v1
3462  *
3463  * --> tag Tag to write the metadata to
3464  * This function adds metadata to the given ID3v1 tag..
3465  */
3466 nsresult sbMetadataHandlerTaglib::WriteID3v1(
3467  TagLib::ID3v1::Tag *tag)
3468 {
3469  LOG(("Writing ID3v1 Tag"));
3470  nsresult result = NS_OK;
3471  nsAutoString propertyValue;
3472 
3473  TagLib::PropertyMap prop = tag->properties();
3474  TagLib::PropertyMap* properties = &prop;
3475 
3476  // General Tags
3477  result = WriteBasic(properties);
3478  if (!NS_SUCCEEDED(result)) {
3479  return result;
3480  }
3481 
3482  // Track- and discnumbers
3483  nsresult tmp_result;
3484  WRITE_PROPERTY(tmp_result, SB_PROPERTY_TRACKNUMBER, "TRACKNUMBER");
3485  WRITE_PROPERTY(tmp_result, SB_PROPERTY_DISCNUMBER, "DISCNUMBER");
3486 
3487  tag->setProperties(prop);
3488 
3489  return result;
3490 }
3491 
3492 /*
3493  * WriteID3v2
3494  *
3495  * --> tag Tag to write the metadata to
3496  * This function adds metadata to the given ID3v2 tag.
3497  */
3498 nsresult sbMetadataHandlerTaglib::WriteID3v2(
3499  TagLib::ID3v2::Tag *tag)
3500 {
3501  LOG(("Writing ID3v2 Tag"));
3502  nsresult result = NS_OK;
3503 
3504  nsAutoString propertyValue;
3505 
3506  TagLib::PropertyMap prop = tag->properties();
3507  TagLib::PropertyMap* properties = &prop;
3508 
3509  // General Tags
3510  result = WriteBasic(properties);
3511  if (!NS_SUCCEEDED(result)) {
3512  return result;
3513  }
3514 
3515  // Track- and discnumbers
3516  result = WriteSeparatedNumbers(properties,
3517  TagLib::String("TRACKNUMBER", TagLib::String::UTF8),
3518  NS_LITERAL_STRING(SB_PROPERTY_TRACKNUMBER),
3519  NS_LITERAL_STRING(SB_PROPERTY_TOTALTRACKS));
3520  if (!NS_SUCCEEDED(result)) {
3521  return result;
3522  }
3523  result = WriteSeparatedNumbers(properties,
3524  TagLib::String("DISCNUMBER", TagLib::String::UTF8),
3525  NS_LITERAL_STRING(SB_PROPERTY_DISCNUMBER),
3526  NS_LITERAL_STRING(SB_PROPERTY_TOTALDISCS));
3527  if (!NS_SUCCEEDED(result)) {
3528  return result;
3529  }
3530 
3531  // Additional ID3v2 Tags
3532  nsresult tmp_result;
3533  // Future: use popularimeter instead of a custom tag?
3534  WRITE_PROPERTY(tmp_result, SB_PROPERTY_RATING, "NIGHTINGALE-RATING");
3535 
3536  // Disabled, as this is is for iTunes only, and NOT a standard:
3537  // Note that this is also disabled for all other Tags for now.
3538  // Also, we'd need a BOOLEAN version for this only ("0" = delete mapping)
3539  // WRITE_BOOLEAN_PROPERTY(tmp_result, SB_PROPERTY_ISPARTOFCOMPILATION,
3540  // "TCMP");
3541 
3542  tag->setProperties(prop);
3543 
3544  return result;
3545 }
3546 
3547 /*
3548  * WriteMP4
3549  *
3550  * --> tag Tag to write the metadata to
3551  * This function adds metadata to the given MP4 tag.
3552  */
3553 nsresult sbMetadataHandlerTaglib::WriteMP4(
3554  TagLib::MP4::Tag *tag)
3555 {
3556  LOG(("Writing MP4 Tag"));
3557  nsresult result = NS_OK;
3558  nsAutoString propertyValue;
3559 
3560  TagLib::PropertyMap prop = tag->properties();
3561  TagLib::PropertyMap* properties = &prop;
3562 
3563  // General Tags
3564  result = WriteBasic(properties);
3565  if (!NS_SUCCEEDED(result)) {
3566  return result;
3567  }
3568 
3569  // Track- and discnumbers
3570  nsresult tmp_result;
3571  WRITE_PROPERTY(tmp_result, SB_PROPERTY_TRACKNUMBER, "TRACKNUMBER");
3572  WRITE_PROPERTY(tmp_result, SB_PROPERTY_DISCNUMBER, "DISCNUMBER");
3573 
3574  // TODO: MP4 did not yet recive the update to PropertyMap for non-default
3575  // tags. We probably should write the tags here using tag->itemListMap()
3576 
3577  tag->setProperties(prop);
3578 
3579  return result;
3580 }
3581 
3582 /*
3583  * WriteXiphComment
3584  *
3585  * --> tag Tag to write the metadata to
3586  * This function adds metadata to the given XiphComment tag.
3587  */
3588 nsresult sbMetadataHandlerTaglib::WriteXiphComment(
3589  TagLib::Ogg::XiphComment *tag)
3590 {
3591  LOG(("Writing XiphComment"));
3592  nsresult result = NS_OK;
3593  nsAutoString propertyValue;
3594 
3595  TagLib::PropertyMap prop = tag->properties();
3596  TagLib::PropertyMap* properties = &prop;
3597 
3598  // General Tags
3599  result = WriteBasic(properties);
3600  if (!NS_SUCCEEDED(result)) {
3601  return result;
3602  }
3603 
3604  // Track- and discnumbers
3605  result = WriteSeparatedNumbers(properties,
3606  TagLib::String("TRACKNUMBER", TagLib::String::UTF8),
3607  NS_LITERAL_STRING(SB_PROPERTY_TRACKNUMBER),
3608  NS_LITERAL_STRING(SB_PROPERTY_TOTALTRACKS));
3609  if (!NS_SUCCEEDED(result)) {
3610  return result;
3611  }
3612  result = WriteSeparatedNumbers(properties,
3613  TagLib::String("DISCNUMBER", TagLib::String::UTF8),
3614  NS_LITERAL_STRING(SB_PROPERTY_DISCNUMBER),
3615  NS_LITERAL_STRING(SB_PROPERTY_TOTALDISCS));
3616  if (!NS_SUCCEEDED(result)) {
3617  return result;
3618  }
3619 
3620  // Additional APE Tags, TODO: discuss XiphComment mapping in general.
3621  nsresult tmp_result;
3622  WRITE_PROPERTY(tmp_result, SB_PROPERTY_RATING, "NIGHTINGALE-RATING");
3623 
3624  tag->setProperties(prop);
3625 
3626  return result;
3627 }
3628 
3629 /*
3630  * base64 encode/decode routines:
3631  * Copyright (C) 2004-2008 René Nyffenegger
3632  *
3633  * This source code is provided 'as-is', without any express or implied
3634  * warranty. In no event will the author be held liable for any damages
3635  * arising from the use of this software.
3636  *
3637  * Permission is granted to anyone to use this software for any purpose,
3638  * including commercial applications, and to alter it and redistribute it
3639  * freely, subject to the following restrictions:
3640  *
3641  * 1. The origin of this source code must not be misrepresented; you must not
3642  * claim that you wrote the original source code. If you use this source code
3643  * in a product, an acknowledgment in the product documentation would be
3644  * appreciated but is not required.
3645  *
3646  * 2. Altered source versions must be plainly marked as such, and must not be
3647  * misrepresented as being the original source code.
3648  *
3649  * 3. This notice may not be removed or altered from any source distribution.
3650  *
3651  * René Nyffenegger rene.nyffenegger@adp-gmbh.ch
3652 */
3653 
3654 static const std::string base64_chars =
3655  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3656  "abcdefghijklmnopqrstuvwxyz"
3657  "0123456789+/";
3658 
3659 std::string sbMetadataHandlerTaglib::base64_encode(unsigned char const* bytes_to_encode,
3660  unsigned int in_len) {
3661  std::string ret;
3662  int i = 0;
3663  int j = 0;
3664  unsigned char char_array_3[3];
3665  unsigned char char_array_4[4];
3666 
3667  while (in_len--) {
3668  char_array_3[i++] = *(bytes_to_encode++);
3669  if (i == 3) {
3670  char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
3671  char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
3672  char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
3673  char_array_4[3] = char_array_3[2] & 0x3f;
3674 
3675  for(i = 0; (i <4) ; i++)
3676  ret += base64_chars[char_array_4[i]];
3677  i = 0;
3678  }
3679  }
3680 
3681  if (i)
3682  {
3683  for(j = i; j < 3; j++)
3684  char_array_3[j] = '\0';
3685 
3686  char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
3687  char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
3688  char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
3689  char_array_4[3] = char_array_3[2] & 0x3f;
3690 
3691  for (j = 0; (j < i + 1); j++)
3692  ret += base64_chars[char_array_4[j]];
3693 
3694  while((i++ < 3))
3695  ret += '=';
3696 
3697  }
3698 
3699  return ret;
3700 
3701 }
3702 
3703 static const unsigned char base64lookup[256] = {
3704  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3705  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3706  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3707  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3708  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3709  0xff, 0xff, 0xff, 62, 0xff, 0xff, 0xff, 63,
3710  52, 53, 54, 55, 56, 57, 58, 59,
3711  60, 61, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3712  0xff, 0, 1, 2, 3, 4, 5, 6,
3713  7, 8, 9, 10, 11, 12, 13, 14,
3714  15, 16, 17, 18, 19, 20, 21, 22,
3715  23, 24, 25, 0xff, 0xff, 0xff, 0xff, 0xff,
3716  0xff, 26, 27, 28, 29, 30, 31, 32,
3717  33, 34, 35, 36, 37, 38, 39, 40,
3718  41, 42, 43, 44, 45, 46, 47, 48,
3719  49, 50, 51, 0xff, 0xff, 0xff, 0xff, 0xff,
3720  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3721  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3722  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3723  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3724  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3725  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3726  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3727  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3728  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3729  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3730  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3731  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3732  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3733  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3734  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3735  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
3736 };
3737 
3738 std::string sbMetadataHandlerTaglib::base64_decode(std::string const& encoded_string) {
3739  int i = 0;
3740  std::string ret;
3741 
3742  int in_len = encoded_string.size();
3743  if (in_len % 4 != 0) {
3744  // base-64 encoded data must always be a multiple of 4 bytes
3745  return ret;
3746  }
3747 
3748  while (in_len > 0)
3749  {
3750  unsigned char vals[4];
3751 
3752  vals[0] = base64lookup[(unsigned char)encoded_string[i]];
3753  vals[1] = base64lookup[(unsigned char)encoded_string[i+1]];
3754  vals[2] = base64lookup[(unsigned char)encoded_string[i+2]];
3755  vals[3] = base64lookup[(unsigned char)encoded_string[i+3]];
3756 
3757  if (in_len > 4)
3758  {
3759  /* Check that all input bytes are legal */
3760  if (vals[0] == 0xff || vals[1] == 0xff || vals[2] == 0xff ||
3761  vals[3] == 0xff)
3762  return std::string();
3763  }
3764  else {
3765  // Final chunk may have one or two padding '=' bytes
3766  if (vals[0] == 0xff || vals[1] == 0xff)
3767  return std::string();
3768  if (vals[2] == 0xff || vals[3] == 0xff)
3769  {
3770  if (encoded_string[i+3] != '=' ||
3771  (vals[2] == 0xff && encoded_string[i+2] != '='))
3772  return std::string();
3773 
3774  ret += vals[0]<<2 | vals[1]>>4;
3775  if (vals[2] != 0xff)
3776  ret += (vals[1]&0x0F)<<4 | vals[2]>>2;
3777  // Done!
3778  return ret;
3779  }
3780  }
3781 
3782  ret += vals[0]<<2 | vals[1]>>4;
3783  ret += (vals[1]&0x0F)<<4 | vals[2]>>2;
3784  ret += (vals[2]&0x03)<<6 | vals[3];
3785 
3786  in_len -= 4;
3787  i += 4;
3788  }
3789 
3790  return ret;
3791 }
#define SB_PROPERTY_TOTALDISCS
#define TAGLIB1_PROPERTIES_WORKAROUND(pTag)
return NS_OK
void toMozString(TagLib::String aString, nsAString &aResult)
static const char * APEMap[][2]
#define SB_PROPERTY_METADATAUUID
#define SB_PROPERTY_SAMPLERATE
#define SB_PROPERTY_LYRICS
#define SB_PROPERTY_CHANNELS
static const char * ID3v2Map[][2]
#define SB_PROPERTY_ALBUMARTISTNAME
#define SB_PROPERTY_RATING
#define SB_PROPERTY_ISDRMPROTECTED
NS_IMETHOD Notify(const char *aCharset, nsDetectionConfident aConf)
NS_IMPL_THREADSAFE_ISUPPORTS3(sbMetadataHandlerTaglib, sbIMetadataHandler, sbISeekableChannelListener, nsICharsetDetectionObserver) NS_IMETHODIMP sbMetadataHandlerTaglib
#define SB_PROPERTY_CONDUCTORNAME
#define SB_MUTABLEPROPERTYARRAY_CONTRACTID
#define SB_PROPERTY_LYRICISTNAME
#define MAX_MPEG_IMAGE_SIZE
#define SB_PROPERTY_TOTALTRACKS
static const unsigned char base64lookup[256]
A seekable wrapper for an nsIChannel.
#define SB_PROPERTY_BITRATE
#define WRITE_PROPERTY(tmp_result, SB_PROPERTY, taglibName)
#define SONGBIRD_METADATAHANDLERTAGLIB_CONTRACTID
const PRUint32 METADATA_IMAGE_TYPE_OTHER
Constant for the type of image in the metadata, these are pulled from the taglib/attachedpictureframe...
An interface to carry around arrays of nsIProperty instances Note that implementations of the interfa...
static void ModuleDestructor(nsIModule *aSelf)
#define SB_PROPERTY_ORIGINPAGE
PRInt32 read()
Start the read operation.
var count
Definition: test_bug7406.js:32
static nsresult ModuleConstructor(nsIModule *aSelf)
const PRUint32 METADATA_IMAGE_TYPE_FRONTCOVER
const sbCreateProxiedComponent do_ProxiedGetService(const nsCID &aCID, nsresult *error=0)
#define SB_PROPERTY_GENRE
#define SB_PROPERTY_CONTENTTYPE
const nsIChannel
_updateTextAndScrollDataForFrame aContent
this _dialogInput val(dateText)
#define SB_PROPERTY_SOFTWAREVENDOR
#define SB_PROPERTY_KEY
#define SB_PROPERTY_DURATION
An object capable of manipulating the metadata tags for a media file.
#define SB_PROPERTY_COPYRIGHT
#define SB_PROPERTY_DISCNUMBER
#define SB_GN_PROP_EXTENDEDDATA
Songbird MetadataHandlerTaglib Component Definition.
#define SB_PROPERTY_ARTISTNAME
T * get() const
return null
Definition: FeedWriter.js:1143
A listener interface for sbISeekableChannel.
BogusChannel prototype owner
return ret
#define LOG(args)
#define SB_PROPERTY_BPM
Tag * tag(int index) const
#define GET_PROPERTY(taglibid)
#define SB_PROPERTY_RECORDLABELNAME
var uri
Definition: FeedWriter.js:1135
function url(spec)
countRef value
Definition: FeedWriter.js:1423
#define SB_PROPERTY_ALBUMNAME
#define SB_PROPERTY_LANGUAGE
#define GUESS_CHARSET_MIN_CHAR_COUNT
#define SB_PROPERTY_COMPOSERNAME
#define SB_PROPERTY_YEAR
#define SB_PROPERTY_TRACKNAME
var ios
Definition: head_feeds.js:5
#define SB_GN_PROP_TAGID
#define SB_PROPERTY_COMMENT
PRInt32 vote(in AString aUrl)
Vote to be the handler returned for the given url.
dataSBGenres SBProperties tag
Definition: tuner2.js:871
#define SB_PROPERTY_COPYRIGHTURL
observe data
Definition: FeedWriter.js:1329
#define SB_PROPERTY_SUBTITLE
_getSelectedPageStyle s i
#define SB_PROPERTY_PRIMARYIMAGEURL
let observer
static const std::string base64_chars
_updateTextAndScrollDataForFrame aData
converter charset
#define SB_PROPERTY_TRACKNUMBER
NS_DECL_ISUPPORTS NS_DECL_SBIMETADATAHANDLER NS_DECL_SBISEEKABLECHANNELLISTENER sbMetadataHandlerTaglib()