bool SFB::Audio::OggFLACMetadata::_WriteMetadata(CFErrorRef *error) { UInt8 buf [PATH_MAX]; if(!CFURLGetFileSystemRepresentation(mURL, false, buf, PATH_MAX)) return false; std::unique_ptr<TagLib::FileStream> stream(new TagLib::FileStream((const char *)buf)); if(!stream->isOpen()) { if(error) { SFB::CFString description = CFCopyLocalizedString(CFSTR("The file “%@” could not be opened for writing."), ""); SFB::CFString failureReason = CFCopyLocalizedString(CFSTR("Input/output error"), ""); SFB::CFString recoverySuggestion = CFCopyLocalizedString(CFSTR("The file may have been renamed, moved, deleted, or you may not have appropriate permissions."), ""); *error = CreateErrorForURL(Metadata::ErrorDomain, Metadata::InputOutputError, description, mURL, failureReason, recoverySuggestion); } return false; } TagLib::Ogg::FLAC::File file(stream.get(), false); if(!file.isValid()) { if(error) { SFB::CFString description = CFCopyLocalizedString(CFSTR("The file “%@” is not a valid Ogg file."), ""); SFB::CFString failureReason = CFCopyLocalizedString(CFSTR("Not an Ogg file"), ""); SFB::CFString recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), ""); *error = CreateErrorForURL(Metadata::ErrorDomain, Metadata::InputOutputError, description, mURL, failureReason, recoverySuggestion); } return false; } SetXiphCommentFromMetadata(*this, file.tag()); if(!file.save()) { if(error) { SFB::CFString description = CFCopyLocalizedString(CFSTR("The file “%@” is not a valid Ogg file."), ""); SFB::CFString failureReason = CFCopyLocalizedString(CFSTR("Unable to write metadata"), ""); SFB::CFString recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), ""); *error = CreateErrorForURL(Metadata::ErrorDomain, Metadata::InputOutputError, description, mURL, failureReason, recoverySuggestion); } return false; } return true; }
bool OggVorbisMetadata::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::Ogg::Vorbis::File file(stream, false); if(!file.isValid()) { if(error) { CFStringRef description = CFCopyLocalizedString(CFSTR("The file “%@” is not a valid Ogg Vorbis file."), ""); CFStringRef failureReason = CFCopyLocalizedString(CFSTR("Not an Ogg Vorbis 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.tag()); if(!file.save()) { if(error) { CFStringRef description = CFCopyLocalizedString(CFSTR("The file “%@” is not a valid Ogg Vorbis 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 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 OggSpeexMetadata::WriteMetadata(CFErrorRef *error) { UInt8 buf [PATH_MAX]; if(!CFURLGetFileSystemRepresentation(mURL, false, buf, PATH_MAX)) return false; // TODO: Use unique_ptr once the switch to C++11 STL is made std::auto_ptr<TagLib::FileStream> stream(new TagLib::FileStream(reinterpret_cast<const char *>(buf))); if(!stream->isOpen()) { if(error) { CFStringRef description = CFCopyLocalizedString(CFSTR("The file “%@” could not be opened for writing."), ""); CFStringRef failureReason = CFCopyLocalizedString(CFSTR("Input/output error"), ""); CFStringRef recoverySuggestion = CFCopyLocalizedString(CFSTR("The file may have been renamed, moved, deleted, or you may not have appropriate permissions."), ""); *error = CreateErrorForURL(AudioMetadataErrorDomain, AudioMetadataInputOutputError, description, mURL, failureReason, recoverySuggestion); CFRelease(description), description = nullptr; CFRelease(failureReason), failureReason = nullptr; CFRelease(recoverySuggestion), recoverySuggestion = nullptr; } return false; } TagLib::Ogg::Speex::File file(stream.get(), false); if(!file.isValid()) { if(error) { CFStringRef description = CFCopyLocalizedString(CFSTR("The file “%@” is not a valid Ogg Speex file."), ""); CFStringRef failureReason = CFCopyLocalizedString(CFSTR("Not an Ogg Speex 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.tag()); if(!file.save()) { if(error) { CFStringRef description = CFCopyLocalizedString(CFSTR("The file “%@” is not a valid Ogg Speex 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 OggSpeexMetadata::WriteMetadata(CFErrorRef *error) { UInt8 buf [PATH_MAX]; if(!CFURLGetFileSystemRepresentation(mURL, false, buf, PATH_MAX)) return false; TagLib::IOStream *stream = new TagLib::FileStream(reinterpret_cast<const char *>(buf)); TagLib::Ogg::Speex::File file(stream, false); if(!file.isValid()) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mURL); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” is not a valid Ogg Speex file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not an Ogg Speex file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioMetadataErrorDomain, AudioMetadataInputOutputError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } return false; } SetXiphCommentFromMetadata(*this, file.tag()); if(!file.save()) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mURL); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” is not a valid Ogg Speex file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Unable to write metadata"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioMetadataErrorDomain, AudioMetadataInputOutputError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } return false; } MergeChangedMetadataIntoMetadata(); return true; }