internal static ICoverArt FromBase64(ReadOnlySpan <byte> value) { // Use heap allocations for cover art > 256kB var byteCount = Base64.GetMaxDecodedFromUtf8Length(value.Length); var decodedValue = byteCount < 0x40000 ? stackalloc byte[byteCount] : new byte[byteCount]; Base64.DecodeFromUtf8(value, decodedValue, out _, out _); // If the image isn't a "Front Cover" or "Other", return null var imageType = BinaryPrimitives.ReadUInt32BigEndian(decodedValue); if (imageType != 3 && imageType != 0) { return(null); } var offset = 4; // Seek past the mime type and description offset += (int)BinaryPrimitives.ReadUInt32BigEndian(decodedValue.Slice(offset)) + 4; offset += (int)BinaryPrimitives.ReadUInt32BigEndian(decodedValue.Slice(offset)) + 4; // Seek past the width, height, color depth and type offset += 16; return(CoverArtFactory.GetOrCreate( decodedValue.Slice(offset + 4, (int)BinaryPrimitives.ReadUInt32BigEndian(decodedValue.Slice(offset))))); }
public void CreatePathInvalidThrowsException([NotNull] string fileName) { Assert.Throws <ImageInvalidException>(() => CoverArtFactory.GetOrCreate(Path.Combine( new DirectoryInfo(Directory.GetCurrentDirectory()).Parent?.Parent?.Parent?.Parent?.FullName, "TestFiles", "Invalid", fileName))); }
public void HasExpectedMimeType([NotNull] string fileName, [NotNull] string mimeType) { Assert.Equal(mimeType, CoverArtFactory.GetOrCreate( Path.Combine( new DirectoryInfo(Directory.GetCurrentDirectory()).Parent?.Parent?.Parent?.Parent?.FullName, "TestFiles", "Valid", fileName)) .MimeType); }
public void HasExpectedLossless([NotNull] string fileName, bool expectedLossless) { Assert.Equal(expectedLossless, CoverArtFactory.GetOrCreate( Path.Combine( new DirectoryInfo(Directory.GetCurrentDirectory()).Parent?.Parent?.Parent?.Parent?.FullName, "TestFiles", "Valid", fileName)) .Lossless); }
public void HasExpectedColorDepth([NotNull] string fileName, int expectedColorDepth) { Assert.Equal(expectedColorDepth, CoverArtFactory.GetOrCreate( Path.Combine( new DirectoryInfo(Directory.GetCurrentDirectory()).Parent?.Parent?.Parent?.Parent?.FullName, "TestFiles", "Valid", fileName)) .ColorDepth); }
public void GetDataReturnsExpectedValue([NotNull] string fileName, [NotNull] string expectedHash) { Assert.Equal(expectedHash, HashUtility.CalculateHash(CoverArtFactory.GetOrCreate( Path.Combine( new DirectoryInfo(Directory.GetCurrentDirectory()).Parent?.Parent?.Parent?.Parent?.FullName, "TestFiles", "Valid", fileName)) .Data.ToArray())); }
public void CreatesExpectedOutput( int index, string fileName, TestAudioMetadata metadata, string imageFileName, SettingDictionary settings, string[] validHashes) { var sourceDirectory = Path.Combine(PathUtility.GetTestFileRoot(), "Valid"); var path = Path.Combine("Output", "Save-AudioMetadata", "Valid", $"{index:000} - {fileName}"); Directory.CreateDirectory(Path.GetDirectoryName(path) !); File.Copy(Path.Combine(sourceDirectory, fileName), path, true); var audioFile = new TaggedAudioFile(path); _mapper.Map(metadata, audioFile.Metadata); if (!string.IsNullOrEmpty(imageFileName)) { audioFile.Metadata.CoverArt = CoverArtFactory.GetOrCreate(Path.Combine(sourceDirectory, imageFileName)); } using (var ps = PowerShell.Create()) { ps.Runspace = _moduleFixture.Runspace; ps.AddCommand("Save-AudioMetadata") .AddArgument(audioFile); foreach (var item in settings) { if (item.Value is bool boolValue) { if (boolValue) { ps.AddParameter(item.Key); } } else { ps.AddParameter(item.Key, item.Value); } } ps.Invoke(); } Assert.Contains(HashUtility.CalculateHash(audioFile.Path), validHashes); }
protected override unsafe void MetadataCallback(IntPtr handle, IntPtr metadataBlock, IntPtr userData) { // ReSharper disable once SwitchStatementMissingSomeCases switch ((MetadataType)Marshal.ReadInt32(metadataBlock)) { case MetadataType.VorbisComment: var vorbisComment = Marshal.PtrToStructure <VorbisCommentMetadataBlock>(metadataBlock).VorbisComment; for (var commentIndex = 0; commentIndex < vorbisComment.Count; commentIndex++) { var entry = Marshal.PtrToStructure <VorbisCommentEntry>(IntPtr.Add(vorbisComment.Comments, commentIndex * Marshal.SizeOf <VorbisCommentEntry>())); var commentBytes = new Span <byte>(entry.Entry.ToPointer(), (int)entry.Length); var delimiter = commentBytes.IndexOf((byte)0x3D); // '=' #if NETSTANDARD2_0 var keyBytes = commentBytes.Slice(0, delimiter); var valueBytes = commentBytes.Slice(delimiter + 1); AudioMetadata.Set( Encoding.ASCII.GetString( (byte *)Unsafe.AsPointer(ref MemoryMarshal.GetReference(keyBytes)), keyBytes.Length), Encoding.UTF8.GetString( (byte *)Unsafe.AsPointer(ref MemoryMarshal.GetReference(valueBytes)), valueBytes.Length)); #else AudioMetadata.Set( Encoding.ASCII.GetString(commentBytes.Slice(0, delimiter)), Encoding.UTF8.GetString(commentBytes.Slice(delimiter + 1))); #endif } break; case MetadataType.Picture: var picture = Marshal.PtrToStructure <PictureMetadataBlock>(metadataBlock).Picture; if (picture.Type == PictureType.CoverFront || picture.Type == PictureType.Other) { AudioMetadata.CoverArt = CoverArtFactory.GetOrCreate( new Span <byte>(picture.Data.ToPointer(), (int)picture.DataLength)); } break; } }
void ProcessPath(string path) { try { ICoverArt result; try { result = CoverArtFactory.GetOrCreate(path); } finally { ProcessLogMessages(); } WriteObject(result); } catch (AudioException e) { WriteError(new(e, e.GetType().Name, ErrorCategory.InvalidData, Path)); } }
public CoverAtom(ReadOnlySpan <byte> data) { // There could be more than one data atom. Ignore all but the first. Value = CoverArtFactory.GetOrCreate(data.Slice(24, (int)BinaryPrimitives.ReadUInt32BigEndian(data.Slice(8, 4)) - 16)); }
public void CreatePathNotFoundThrowsException() { Assert.Throws <FileNotFoundException>(() => CoverArtFactory.GetOrCreate("Foo")); }
public void CreatePathNullThrowsException() { Assert.Throws <ArgumentNullException>(() => CoverArtFactory.GetOrCreate((string)null)); }
public void CreateDataNullThrowsException() { Assert.Throws <ArgumentNullException>(() => CoverArtFactory.GetOrCreate((byte[])null)); }
internal TagModelToMetadataAdapter([NotNull] TagModel tagModel) { foreach (var frame in tagModel) { switch (frame) { case FrameText frameText: // ReSharper disable once SwitchStatementMissingSomeCases switch (frameText.FrameId) { case "TIT2": Title = frameText.Text; break; case "TPE1": Artist = frameText.Text; break; case "TALB": Album = frameText.Text; break; case "TPE2": AlbumArtist = frameText.Text; break; case "TCOM": Composer = frameText.Text; break; case "TCON": Genre = frameText.Text; break; // The TDAT frame contains the day and the month: case "TDAT": Day = frameText.Text.Substring(0, 2); Month = frameText.Text.Substring(2); break; case "TYER": Year = frameText.Text; break; // The TRCK frame contains the track number and (optionally) the track count: case "TRCK": var segments = frameText.Text.Split('/'); TrackNumber = segments[0]; if (segments.Length > 1) { TrackCount = segments[1]; } break; } break; case FrameFullText frameFullText: if (frameFullText.FrameId.Equals("COMM", StringComparison.Ordinal) && string.IsNullOrEmpty(frameFullText.Description)) { Comment = frameFullText.Text; } break; case FrameTextUserDef frameTextUserDef: // ReSharper disable once SwitchStatementMissingSomeCases switch (frameTextUserDef.Description) { case "REPLAYGAIN_TRACK_PEAK": TrackPeak = frameTextUserDef.Text; break; case "REPLAYGAIN_ALBUM_PEAK": AlbumPeak = frameTextUserDef.Text; break; case "REPLAYGAIN_TRACK_GAIN": #if NETSTANDARD2_0 TrackGain = frameTextUserDef.Text.Replace(" dB", string.Empty); #else TrackGain = frameTextUserDef.Text.Replace(" dB", string.Empty, StringComparison.OrdinalIgnoreCase); #endif break; case "REPLAYGAIN_ALBUM_GAIN": #if NETSTANDARD2_0 AlbumGain = frameTextUserDef.Text.Replace(" dB", string.Empty); #else AlbumGain = frameTextUserDef.Text.Replace(" dB", string.Empty, StringComparison.OrdinalIgnoreCase); #endif break; } break; case FramePicture framePicture: CoverArt = CoverArtFactory.GetOrCreate(framePicture.PictureData); break; } } }