예제 #1
0
        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)))));
        }
예제 #2
0
 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)));
 }
예제 #3
0
 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);
 }
예제 #4
0
 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);
 }
예제 #5
0
 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);
 }
예제 #6
0
 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()));
 }
예제 #7
0
        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);
        }
예제 #8
0
        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;
            }
        }
예제 #9
0
        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));
            }
        }
예제 #10
0
 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));
 }
예제 #11
0
 public void CreatePathNotFoundThrowsException()
 {
     Assert.Throws <FileNotFoundException>(() => CoverArtFactory.GetOrCreate("Foo"));
 }
예제 #12
0
 public void CreatePathNullThrowsException()
 {
     Assert.Throws <ArgumentNullException>(() => CoverArtFactory.GetOrCreate((string)null));
 }
예제 #13
0
 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;
                }
            }
        }