/** * Returns the content type corresponding to the supplied file extension. * The mapping is determined using Uniform Type Identifiers (UTIs). If * the file extension parameter is NULL, a CFString cannot be created * from the file extension parameter, there is no UTI corresponding to * the file extension, the UTI cannot supply a MIME type for the file * extension, or a Java string cannot be created, then NULL is returned; * otherwise the MIME type string is returned. */ JNIEXPORT jstring JNICALL Java_sun_nio_fs_UTIFileTypeDetector_probe0(JNIEnv* env, jobject ftd, jstring ext) { jstring result = NULL; CFStringRef extension = toCFString(env, ext); if (extension != NULL) { CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, extension, NULL); CFRelease(extension); if (uti != NULL) { CFStringRef mimeType = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType); CFRelease(uti); if (mimeType != NULL) { result = toJavaString(env, mimeType); CFRelease(mimeType); } } } return result; }
bool QMacPasteboardMimeQt3Any::canConvert(const QString &mime, QString flav) { loadMimeRegistry(); const int os_flav = UTGetOSTypeFromString(UTTypeCopyPreferredTagWithClass(QCFString(flav), kUTTagClassOSType)); if(mime_registry.contains(mime) && mime_registry[mime] == os_flav) return true; return false; }
QString QMacPasteboardMimeQt3Any::mimeFor(QString flav) { loadMimeRegistry(); const int os_flav = UTGetOSTypeFromString(UTTypeCopyPreferredTagWithClass(QCFString(flav), kUTTagClassOSType)); for(QMap<QString, int>::const_iterator it = mime_registry.constBegin(); it != mime_registry.constEnd(); ++it) { if(it.value() == os_flav) return QString::fromLatin1(it.key().toLatin1()); } return QString(); }
// Look up the (locale) display name and icon file associated with a UTI void wxMimeTypesManagerImpl::LoadDisplayDataForUti(const wxString& uti) { // Keys in to Info.plist const static wxCFStringRef docTypesKey( "CFBundleDocumentTypes" ); const static wxCFStringRef descKey( "CFBundleTypeName" ); const static wxCFStringRef iconKey( "CFBundleTypeIconFile" ); // The call for finding the preferred application for a UTI is LSCopyDefaultRoleHandlerForContentType // This returns an empty string on OS X 10.5 // Instead it is necessary to get the primary extension and use LSGetApplicationForInfo wxCFStringRef ext = UTTypeCopyPreferredTagWithClass( wxCFStringRef( uti ), kUTTagClassFilenameExtension ); // Look up the preferred application CFURLRef appUrl; OSStatus status = LSGetApplicationForInfo( kLSUnknownType, kLSUnknownCreator, ext, kLSRolesAll, NULL, &appUrl ); if( status != noErr ) return; // Create a bundle object for that application wxCFRef< CFBundleRef > bundle; bundle = wxCFRef< CFBundleRef >( CFBundleCreate( kCFAllocatorDefault, appUrl ) ); if( !bundle ) return; // Also get the open command while we have the bundle wxCFStringRef cfsAppPath(CFURLCopyFileSystemPath(appUrl, kCFURLPOSIXPathStyle)); m_utiMap[ uti ].application = cfsAppPath.AsString(); // Get all the document type data in this bundle CFTypeRef docTypeData; docTypeData = CFBundleGetValueForInfoDictionaryKey( bundle, docTypesKey ); if( !docTypeData ) return; // Find the document type entry that matches ext CFDictionaryRef docType; docType = GetDocTypeForExt( docTypeData, ext ); if( !docType ) return; // Get the display name for docType wxCFStringRef description = reinterpret_cast< CFStringRef >( CFDictionaryGetValue( docType, descKey ) ); wxCFRetain( description.get() ); m_utiMap[ uti ].description = description.AsString(); // Get the icon path for docType CFStringRef iconFile = reinterpret_cast< CFStringRef > ( CFDictionaryGetValue( docType, iconKey ) ); m_utiMap[ uti ].iconLoc.SetFileName( GetPathForIconFile( bundle, iconFile ) ); }
bool QMacPasteboard::hasOSType(int c_flavor) const { if (!paste) return false; sync(); ItemCount cnt = 0; if(PasteboardGetItemCount(paste, &cnt) || !cnt) return false; #ifdef DEBUG_PASTEBOARD qDebug("PasteBoard: hasOSType [%c%c%c%c]", (c_flavor>>24)&0xFF, (c_flavor>>16)&0xFF, (c_flavor>>8)&0xFF, (c_flavor>>0)&0xFF); #endif for(uint index = 1; index <= cnt; ++index) { PasteboardItemID id; if(PasteboardGetItemIdentifier(paste, index, &id) != noErr) return false; QCFType<CFArrayRef> types; if(PasteboardCopyItemFlavors(paste, id, &types ) != noErr) return false; const int type_count = CFArrayGetCount(types); for(int i = 0; i < type_count; ++i) { CFStringRef flavor = (CFStringRef)CFArrayGetValueAtIndex(types, i); const int os_flavor = UTGetOSTypeFromString(UTTypeCopyPreferredTagWithClass(flavor, kUTTagClassOSType)); if(os_flavor == c_flavor) { #ifdef DEBUG_PASTEBOARD qDebug(" - Found!"); #endif return true; } } } #ifdef DEBUG_PASTEBOARD qDebug(" - NotFound!"); #endif return false; }
static nglString GetMimeType(const nglString& extension) { CFStringRef mimeType = NULL; CFStringRef ext = extension.ToCFString(); CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, NULL); CFRelease(ext); if (!UTI) return nglString::Null; mimeType = UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType); if (!mimeType) // check for edge case { return nglString::Null; } CFRelease(UTI); nglString mime(mimeType); CFRelease(mimeType); return mime; }
bool FLACMetadata::WriteMetadata(CFErrorRef *error) { UInt8 buf [PATH_MAX]; if(!CFURLGetFileSystemRepresentation(mURL, false, buf, PATH_MAX)) return false; auto stream = new TagLib::FileStream(reinterpret_cast<const char *>(buf)); TagLib::FLAC::File file(stream, TagLib::ID3v2::FrameFactory::instance(), false); if(!file.isValid()) { if(error) { CFStringRef description = CFCopyLocalizedString(CFSTR("The file “%@” is not a valid FLAC file."), ""); CFStringRef failureReason = CFCopyLocalizedString(CFSTR("Not a FLAC file"), ""); CFStringRef recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), ""); *error = CreateErrorForURL(AudioMetadataErrorDomain, AudioMetadataInputOutputError, description, mURL, failureReason, recoverySuggestion); CFRelease(description), description = nullptr; CFRelease(failureReason), failureReason = nullptr; CFRelease(recoverySuggestion), recoverySuggestion = nullptr; } return false; } SetXiphCommentFromMetadata(*this, file.xiphComment(), false); // Remove existing cover art file.removePictures(); // Add album art for(auto attachedPicture : GetAttachedPictures()) { CGImageSourceRef imageSource = CGImageSourceCreateWithData(attachedPicture->GetData(), nullptr); if(nullptr == imageSource) { LOGGER_ERR("org.sbooth.AudioEngine.AudioMetadata.FLAC", "Skipping album art (unable to create image)"); continue; } TagLib::FLAC::Picture *picture = new TagLib::FLAC::Picture; picture->setData(TagLib::ByteVector((const char *)CFDataGetBytePtr(attachedPicture->GetData()), (TagLib::uint)CFDataGetLength(attachedPicture->GetData()))); picture->setType(static_cast<TagLib::FLAC::Picture::Type>(attachedPicture->GetType())); if(attachedPicture->GetDescription()) picture->setDescription(TagLib::StringFromCFString(attachedPicture->GetDescription())); // Convert the image's UTI into a MIME type CFStringRef mimeType = UTTypeCopyPreferredTagWithClass(CGImageSourceGetType(imageSource), kUTTagClassMIMEType); if(mimeType) { picture->setMimeType(TagLib::StringFromCFString(mimeType)); CFRelease(mimeType), mimeType = nullptr; } // Flesh out the height, width, and depth CFDictionaryRef imagePropertiesDictionary = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nullptr); if(imagePropertiesDictionary) { CFNumberRef imageWidth = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyPixelWidth); CFNumberRef imageHeight = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyPixelHeight); CFNumberRef imageDepth = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyDepth); int height, width, depth; // Ignore numeric conversion errors CFNumberGetValue(imageWidth, kCFNumberIntType, &width); CFNumberGetValue(imageHeight, kCFNumberIntType, &height); CFNumberGetValue(imageDepth, kCFNumberIntType, &depth); picture->setHeight(height); picture->setWidth(width); picture->setColorDepth(depth); CFRelease(imagePropertiesDictionary), imagePropertiesDictionary = nullptr; } file.addPicture(picture); CFRelease(imageSource), imageSource = nullptr; } if(!file.save()) { if(error) { CFStringRef description = CFCopyLocalizedString(CFSTR("The file “%@” is not a valid FLAC file."), ""); CFStringRef failureReason = CFCopyLocalizedString(CFSTR("Unable to write metadata"), ""); CFStringRef recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), ""); *error = CreateErrorForURL(AudioMetadataErrorDomain, AudioMetadataInputOutputError, description, mURL, failureReason, recoverySuggestion); CFRelease(description), description = nullptr; CFRelease(failureReason), failureReason = nullptr; CFRelease(recoverySuggestion), recoverySuggestion = nullptr; } return false; } MergeChangedMetadataIntoMetadata(); return true; }
bool SFB::Audio::SetID3v2TagFromMetadata(const Metadata& metadata, TagLib::ID3v2::Tag *tag, bool setAlbumArt) { if(nullptr == tag) return false; // Use UTF-8 as the default encoding (TagLib::ID3v2::FrameFactory::instance())->setDefaultTextEncoding(TagLib::String::UTF8); // Album title tag->setAlbum(TagLib::StringFromCFString(metadata.GetAlbumTitle())); // Artist tag->setArtist(TagLib::StringFromCFString(metadata.GetArtist())); // Composer tag->removeFrames("TCOM"); if(metadata.GetComposer()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TCOM", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(metadata.GetComposer())); tag->addFrame(frame); } // Genre tag->setGenre(TagLib::StringFromCFString(metadata.GetGenre())); // Date #if 1 int year = 0; if(metadata.GetReleaseDate()) year = CFStringGetIntValue(metadata.GetReleaseDate()); tag->setYear((TagLib::uint)year); #else // TODO: Parse the release date into components and set the frame appropriately tag->removeFrames("TDRC"); if(metadata.GetReleaseDate()) { /* The timestamp fields are based on a subset of ISO 8601. When being as precise as possible the format of a time string is yyyy-MM-ddTHH:mm:ss (year, "-", month, "-", day, "T", hour (out of 24), ":", minutes, ":", seconds), but the precision may be reduced by removing as many time indicators as wanted. Hence valid timestamps are yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm and yyyy-MM-ddTHH:mm:ss. All time stamps are UTC. For durations, use the slash character as described in 8601, and for multiple non- contiguous dates, use multiple strings, if allowed by the frame definition. */ // year = CFStringGetIntValue(metadata.GetReleaseDate()); auto frame = new TagLib::ID3v2::TextIdentificationFrame("TDRC", TagLib::String::Latin1); frame->setText(""); tag->addFrame(frame); } #endif // Comment tag->setComment(TagLib::StringFromCFString(metadata.GetComment())); // Album artist tag->removeFrames("TPE2"); if(metadata.GetAlbumArtist()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TPE2", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(metadata.GetAlbumArtist())); tag->addFrame(frame); } // Track title tag->setTitle(TagLib::StringFromCFString(metadata.GetTitle())); // BPM tag->removeFrames("TBPM"); if(metadata.GetBPM()) { SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%@"), metadata.GetBPM()); auto frame = new TagLib::ID3v2::TextIdentificationFrame("TBPM", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } // Rating tag->removeFrames("POPM"); CFNumberRef rating = metadata.GetRating(); if(rating) { TagLib::ID3v2::PopularimeterFrame *frame = new TagLib::ID3v2::PopularimeterFrame(); int i; if(CFNumberGetValue(rating, kCFNumberIntType, &i)) { frame->setRating(i); tag->addFrame(frame); } } // Track number and total tracks tag->removeFrames("TRCK"); CFNumberRef trackNumber = metadata.GetTrackNumber(); CFNumberRef trackTotal = metadata.GetTrackTotal(); if(trackNumber && trackTotal) { SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%@/%@"), trackNumber, trackTotal); auto frame = new TagLib::ID3v2::TextIdentificationFrame("TRCK", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } else if(trackNumber) { SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%@"), trackNumber); auto frame = new TagLib::ID3v2::TextIdentificationFrame("TRCK", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } else if(trackTotal) { SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("/%@"), trackTotal); auto frame = new TagLib::ID3v2::TextIdentificationFrame("TRCK", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } // Compilation // iTunes uses the TCMP frame for this, which isn't in the standard, but we'll use it for compatibility tag->removeFrames("TCMP"); if(metadata.GetCompilation()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TCMP", TagLib::String::Latin1); frame->setText(CFBooleanGetValue(metadata.GetCompilation()) ? "1" : "0"); tag->addFrame(frame); } // Disc number and total discs tag->removeFrames("TPOS"); CFNumberRef discNumber = metadata.GetDiscNumber(); CFNumberRef discTotal = metadata.GetDiscTotal(); if(discNumber && discTotal) { SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%@/%@"), discNumber, discTotal); auto frame = new TagLib::ID3v2::TextIdentificationFrame("TPOS", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } else if(discNumber) { SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%@"), discNumber); auto frame = new TagLib::ID3v2::TextIdentificationFrame("TPOS", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } else if(discTotal) { SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("/%@"), discTotal); auto frame = new TagLib::ID3v2::TextIdentificationFrame("TPOS", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } // Lyrics tag->removeFrames("USLT"); if(metadata.GetLyrics()) { auto frame = new TagLib::ID3v2::UnsynchronizedLyricsFrame(TagLib::String::UTF8); frame->setText(TagLib::StringFromCFString(metadata.GetLyrics())); tag->addFrame(frame); } tag->removeFrames("TSRC"); if(metadata.GetISRC()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TSRC", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(metadata.GetISRC())); tag->addFrame(frame); } // MusicBrainz auto musicBrainzReleaseIDFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "MusicBrainz Album Id"); if(nullptr != musicBrainzReleaseIDFrame) tag->removeFrame(musicBrainzReleaseIDFrame); CFStringRef musicBrainzReleaseID = metadata.GetMusicBrainzReleaseID(); if(musicBrainzReleaseID) { auto frame = new TagLib::ID3v2::UserTextIdentificationFrame(); frame->setDescription("MusicBrainz Album Id"); frame->setText(TagLib::StringFromCFString(musicBrainzReleaseID)); tag->addFrame(frame); } auto musicBrainzRecordingIDFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "MusicBrainz Track Id"); if(nullptr != musicBrainzRecordingIDFrame) tag->removeFrame(musicBrainzRecordingIDFrame); CFStringRef musicBrainzRecordingID = metadata.GetMusicBrainzRecordingID(); if(musicBrainzRecordingID) { auto frame = new TagLib::ID3v2::UserTextIdentificationFrame(); frame->setDescription("MusicBrainz Track Id"); frame->setText(TagLib::StringFromCFString(musicBrainzRecordingID)); tag->addFrame(frame); } // Sorting and grouping tag->removeFrames("TSOT"); if(metadata.GetTitleSortOrder()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TSOT", TagLib::String::UTF8); frame->setText(TagLib::StringFromCFString(metadata.GetTitleSortOrder())); tag->addFrame(frame); } tag->removeFrames("TSOA"); if(metadata.GetAlbumTitleSortOrder()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TSOA", TagLib::String::UTF8); frame->setText(TagLib::StringFromCFString(metadata.GetAlbumTitleSortOrder())); tag->addFrame(frame); } tag->removeFrames("TSOP"); if(metadata.GetArtistSortOrder()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TSOP", TagLib::String::UTF8); frame->setText(TagLib::StringFromCFString(metadata.GetArtistSortOrder())); tag->addFrame(frame); } tag->removeFrames("TSO2"); if(metadata.GetAlbumArtistSortOrder()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TSO2", TagLib::String::UTF8); frame->setText(TagLib::StringFromCFString(metadata.GetAlbumArtistSortOrder())); tag->addFrame(frame); } tag->removeFrames("TSOC"); if(metadata.GetComposerSortOrder()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TSOC", TagLib::String::UTF8); frame->setText(TagLib::StringFromCFString(metadata.GetComposerSortOrder())); tag->addFrame(frame); } tag->removeFrames("TIT1"); if(metadata.GetGrouping()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TIT1", TagLib::String::UTF8); frame->setText(TagLib::StringFromCFString(metadata.GetGrouping())); tag->addFrame(frame); } // ReplayGain CFNumberRef trackGain = metadata.GetReplayGainTrackGain(); CFNumberRef trackPeak = metadata.GetReplayGainTrackPeak(); CFNumberRef albumGain = metadata.GetReplayGainAlbumGain(); CFNumberRef albumPeak = metadata.GetReplayGainAlbumPeak(); // Write TXXX frames auto trackGainFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "replaygain_track_gain"); auto trackPeakFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "replaygain_track_peak"); auto albumGainFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "replaygain_album_gain"); auto albumPeakFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "replaygain_album_peak"); if(nullptr != trackGainFrame) tag->removeFrame(trackGainFrame); if(nullptr != trackPeakFrame) tag->removeFrame(trackPeakFrame); if(nullptr != albumGainFrame) tag->removeFrame(albumGainFrame); if(nullptr != albumPeakFrame) tag->removeFrame(albumPeakFrame); if(trackGain) { SFB::CFString str = CreateStringFromNumberWithFormat(trackGain, kCFNumberDoubleType, CFSTR("%+2.2f dB")); auto frame = new TagLib::ID3v2::UserTextIdentificationFrame(); frame->setDescription("replaygain_track_gain"); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } if(trackPeak) { SFB::CFString str = CreateStringFromNumberWithFormat(trackPeak, kCFNumberDoubleType, CFSTR("%1.8f dB")); auto frame = new TagLib::ID3v2::UserTextIdentificationFrame(); frame->setDescription("replaygain_track_peak"); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } if(albumGain) { SFB::CFString str = CreateStringFromNumberWithFormat(albumGain, kCFNumberDoubleType, CFSTR("%+2.2f dB")); auto frame = new TagLib::ID3v2::UserTextIdentificationFrame(); frame->setDescription("replaygain_album_gain"); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } if(albumPeak) { SFB::CFString str = CreateStringFromNumberWithFormat(albumPeak, kCFNumberDoubleType, CFSTR("%1.8f dB")); auto frame = new TagLib::ID3v2::UserTextIdentificationFrame(); frame->setDescription("replaygain_album_peak"); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } // Also write the RVA2 frames tag->removeFrames("RVA2"); if(trackGain) { auto relativeVolume = new TagLib::ID3v2::RelativeVolumeFrame(); float f; CFNumberGetValue(trackGain, kCFNumberFloatType, &f); relativeVolume->setIdentification(TagLib::String("track", TagLib::String::Latin1)); relativeVolume->setVolumeAdjustment(f, TagLib::ID3v2::RelativeVolumeFrame::MasterVolume); tag->addFrame(relativeVolume); } if(albumGain) { auto relativeVolume = new TagLib::ID3v2::RelativeVolumeFrame(); float f; CFNumberGetValue(albumGain, kCFNumberFloatType, &f); relativeVolume->setIdentification(TagLib::String("album", TagLib::String::Latin1)); relativeVolume->setVolumeAdjustment(f, TagLib::ID3v2::RelativeVolumeFrame::MasterVolume); tag->addFrame(relativeVolume); } // Album art if(setAlbumArt) { tag->removeFrames("APIC"); for(auto attachedPicture : metadata.GetAttachedPictures()) { SFB::CGImageSource imageSource = CGImageSourceCreateWithData(attachedPicture->GetData(), nullptr); if(!imageSource) continue; TagLib::ID3v2::AttachedPictureFrame *frame = new TagLib::ID3v2::AttachedPictureFrame; // Convert the image's UTI into a MIME type SFB::CFString mimeType = UTTypeCopyPreferredTagWithClass(CGImageSourceGetType(imageSource), kUTTagClassMIMEType); if(mimeType) frame->setMimeType(TagLib::StringFromCFString(mimeType)); frame->setPicture(TagLib::ByteVector((const char *)CFDataGetBytePtr(attachedPicture->GetData()), (TagLib::uint)CFDataGetLength(attachedPicture->GetData()))); frame->setType((TagLib::ID3v2::AttachedPictureFrame::Type)attachedPicture->GetType()); if(attachedPicture->GetDescription()) frame->setDescription(TagLib::StringFromCFString(attachedPicture->GetDescription())); tag->addFrame(frame); } } return true; }
/* * Given a mime-type, return the file extension if there is one * * Returns: 1 on success, 0 on failure */ int mime_get_os_ext_from_mimetype(char *mime_type, char *file_ext, int file_ext_len) { #ifdef _WINDOWS int ret; LPTSTR x, mime_type_lpt, file_ext_lpt; mime_type_lpt = utf8_to_lptstr(mime_type); if(mime_type_lpt){ if(file_ext){ file_ext_lpt = (LPTSTR) fs_get(file_ext_len * sizeof(TCHAR)); file_ext_lpt[0] = '\0'; } else file_ext_lpt = NULL; } ret = mswin_reg_mime_ext(mime_type_lpt, file_ext_lpt, (size_t) file_ext_len); /* convert answer back to UTF-8 */ if(ret && file_ext_lpt && file_ext){ char *u; u = lptstr_to_utf8(file_ext_lpt); if(u){ strncpy(file_ext, u, file_ext_len); file_ext[file_ext_len-1] = '\0'; fs_give((void **) &u); } } if(mime_type_lpt) fs_give((void **) &mime_type_lpt); if(file_ext_lpt) fs_give((void **) &file_ext_lpt); return ret; #elif OSX_TARGET if(!mime_os_specific_access()) return(0); #ifdef AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER CFStringRef mime_ref = NULL, type_id_ref = NULL, ext_ref = NULL; if(!mime_type || !*mime_type) return 0; /* This for if we built on OS X >= 10.3 but run on < 10.3 */ if(&UTTypeCreatePreferredIdentifierForTag == NULL) return 0; if((mime_ref = CFStringCreateWithCString(NULL, mime_type, kCFStringEncodingASCII)) == NULL) return 0; if((type_id_ref = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mime_ref, NULL)) == NULL) return 0; if((ext_ref = UTTypeCopyPreferredTagWithClass(type_id_ref, kUTTagClassFilenameExtension)) == NULL) return 0; if((CFStringGetCString(ext_ref, file_ext, (CFIndex)file_ext_len - 1, kCFStringEncodingASCII)) == false) return 0; file_ext[file_ext_len - 1] = '\0'; return 1; #else return 0; #endif /* AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER */ #else return 0; #endif /* OSX_TARGET */ }
bool SetAPETagFromMetadata(const AudioMetadata& metadata, TagLib::APE::Tag *tag, bool setAlbumArt) { if(nullptr == tag) return false; // Standard tags SetAPETag(tag, "ALBUM", metadata.GetAlbumTitle()); SetAPETag(tag, "ARTIST", metadata.GetArtist()); SetAPETag(tag, "ALBUMARTIST", metadata.GetAlbumArtist()); SetAPETag(tag, "COMPOSER", metadata.GetComposer()); SetAPETag(tag, "GENRE", metadata.GetGenre()); SetAPETag(tag, "DATE", metadata.GetReleaseDate()); SetAPETag(tag, "DESCRIPTION", metadata.GetComment()); SetAPETag(tag, "TITLE", metadata.GetTitle()); SetAPETagNumber(tag, "TRACKNUMBER", metadata.GetTrackNumber()); SetAPETagNumber(tag, "TRACKTOTAL", metadata.GetTrackTotal()); SetAPETagBoolean(tag, "COMPILATION", metadata.GetCompilation()); SetAPETagNumber(tag, "DISCNUMBER", metadata.GetDiscNumber()); SetAPETagNumber(tag, "DISCTOTAL", metadata.GetDiscTotal()); SetAPETagNumber(tag, "BPM", metadata.GetBPM()); SetAPETagNumber(tag, "RATING", metadata.GetRating()); SetAPETag(tag, "ISRC", metadata.GetISRC()); SetAPETag(tag, "MCN", metadata.GetMCN()); SetAPETag(tag, "TITLESORT", metadata.GetTitleSortOrder()); SetAPETag(tag, "ALBUMTITLESORT", metadata.GetAlbumTitleSortOrder()); SetAPETag(tag, "ARTISTSORT", metadata.GetArtistSortOrder()); SetAPETag(tag, "ALBUMARTISTSORT", metadata.GetAlbumArtistSortOrder()); SetAPETag(tag, "COMPOSERSORT", metadata.GetComposerSortOrder()); SetAPETag(tag, "GROUPING", metadata.GetGrouping()); // Additional metadata CFDictionaryRef additionalMetadata = metadata.GetAdditionalMetadata(); if(nullptr != additionalMetadata) { CFIndex count = CFDictionaryGetCount(additionalMetadata); const void * keys [count]; const void * values [count]; CFDictionaryGetKeysAndValues(additionalMetadata, reinterpret_cast<const void **>(keys), reinterpret_cast<const void **>(values)); for(CFIndex i = 0; i < count; ++i) { CFIndex keySize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(reinterpret_cast<CFStringRef>(keys[i])), kCFStringEncodingASCII); char key [keySize + 1]; if(!CFStringGetCString(reinterpret_cast<CFStringRef>(keys[i]), key, keySize + 1, kCFStringEncodingASCII)) { LOGGER_ERR("org.sbooth.AudioEngine", "CFStringGetCString failed"); continue; } SetAPETag(tag, key, reinterpret_cast<CFStringRef>(values[i])); } } // ReplayGain info SetAPETagDouble(tag, "REPLAYGAIN_REFERENCE_LOUDNESS", metadata.GetReplayGainReferenceLoudness(), CFSTR("%2.1f dB")); SetAPETagDouble(tag, "REPLAYGAIN_TRACK_GAIN", metadata.GetReplayGainTrackGain(), CFSTR("%+2.2f dB")); SetAPETagDouble(tag, "REPLAYGAIN_TRACK_PEAK", metadata.GetReplayGainTrackPeak(), CFSTR("%1.8f")); SetAPETagDouble(tag, "REPLAYGAIN_ALBUM_GAIN", metadata.GetReplayGainAlbumGain(), CFSTR("%+2.2f dB")); SetAPETagDouble(tag, "REPLAYGAIN_ALBUM_PEAK", metadata.GetReplayGainAlbumPeak(), CFSTR("%1.8f")); // Album art if(setAlbumArt) { tag->removeItem("Cover Art (Front)"); tag->removeItem("Cover Art (Back)"); #if 0 tag->removeItem("METADATA_BLOCK_PICTURE"); #endif for(auto attachedPicture : metadata.GetAttachedPictures()) { // APE can handle front and back covers natively if(AttachedPicture::Type::FrontCover == attachedPicture->GetType() || AttachedPicture::Type::FrontCover == attachedPicture->GetType()) { TagLib::ByteVector data; if(attachedPicture->GetDescription()) data.append(TagLib::StringFromCFString(attachedPicture->GetDescription()).data(TagLib::String::UTF8)); data.append('\0'); data.append(TagLib::ByteVector((const char *)CFDataGetBytePtr(attachedPicture->GetData()), (TagLib::uint)CFDataGetLength(attachedPicture->GetData()))); if(AttachedPicture::Type::FrontCover == attachedPicture->GetType()) tag->setData("Cover Art (Front)", data); else if(AttachedPicture::Type::BackCover == attachedPicture->GetType()) tag->setData("Cover Art (Back)", data); } #if 0 else { CGImageSourceRef imageSource = CGImageSourceCreateWithData(attachedPicture->GetData(), nullptr); if(nullptr == imageSource) return false; TagLib::FLAC::Picture picture; picture.setData(TagLib::ByteVector((const char *)CFDataGetBytePtr(attachedPicture->GetData()), (TagLib::uint)CFDataGetLength(attachedPicture->GetData()))); picture.setType(static_cast<TagLib::FLAC::Picture::Type>(attachedPicture->GetType())); if(attachedPicture->GetDescription()) picture.setDescription(TagLib::StringFromCFString(attachedPicture->GetDescription())); // Convert the image's UTI into a MIME type CFStringRef mimeType = UTTypeCopyPreferredTagWithClass(CGImageSourceGetType(imageSource), kUTTagClassMIMEType); if(mimeType) { picture.setMimeType(TagLib::StringFromCFString(mimeType)); CFRelease(mimeType), mimeType = nullptr; } // Flesh out the height, width, and depth CFDictionaryRef imagePropertiesDictionary = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nullptr); if(imagePropertiesDictionary) { CFNumberRef imageWidth = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyPixelWidth); CFNumberRef imageHeight = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyPixelHeight); CFNumberRef imageDepth = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyDepth); int height, width, depth; // Ignore numeric conversion errors CFNumberGetValue(imageWidth, kCFNumberIntType, &width); CFNumberGetValue(imageHeight, kCFNumberIntType, &height); CFNumberGetValue(imageDepth, kCFNumberIntType, &depth); picture.setHeight(height); picture.setWidth(width); picture.setColorDepth(depth); CFRelease(imagePropertiesDictionary), imagePropertiesDictionary = nullptr; } TagLib::ByteVector encodedBlock = TagLib::EncodeBase64(picture.render()); tag->addValue("METADATA_BLOCK_PICTURE", TagLib::String(encodedBlock, TagLib::String::UTF8), false); CFRelease(imageSource), imageSource = nullptr; } #endif } } return true; }