/// <summary>Creates the specified header.</summary> /// <param name="header">The header.</param> /// <param name="flags">The flags.</param> /// <param name="description">The description.</param> /// <param name="type">The type.</param> /// <param name="mimeType">Type of the MIME.</param> /// <param name="imageData">The image data.</param> /// <returns></returns> /// <exception cref="NotSupportedException"></exception> public static ID3v2APICFrame Create(ID3v2Header header, ID3v2FrameFlags flags, string description, ID3v2PictureType type, string mimeType, byte[] imageData) { ID3v2EncodingType encoding = ID3v2Encoding.Select(header, description + mimeType); // header, encoding[1], mimeType+0, pitureType[1], description+0, data byte[] descriptionBytes = ID3v2Encoding.GetBytes(encoding, description, true); byte[] mimeTypeBytes = ID3v2Encoding.GetBytes(encoding, mimeType, true); int contentSize = descriptionBytes.Length + mimeTypeBytes.Length + 1 + 1 + imageData.Length; var frameHeader = ID3v2FrameHeader.Create(header, "APIC", flags, contentSize); using (var ms = new MemoryStream()) { var writer = new DataWriter(ms); writer.Write(frameHeader.Data); writer.Write((byte)encoding); writer.Write(mimeTypeBytes); writer.Write((byte)type); writer.Write(descriptionBytes); writer.Write(imageData); if (frameHeader.HeaderSize + contentSize != ms.Position) { throw new Exception(); } return(new ID3v2APICFrame(new ID3v2Frame(header, ms.ToArray()))); } }
/// <summary> /// Creates a <see cref="ID3v2FrameFlags"/> instance from the specified <see cref="ID3v2d3FrameFlags"/>. /// </summary> /// <param name="flags"></param> /// <returns></returns> public static ID3v2FrameFlags FromID3v2d3(ID3v2d3FrameFlags flags) { var result = new ID3v2FrameFlags(); result.Compression = (flags & ID3v2d3FrameFlags.Compression) != 0; result.Encryption = (flags & ID3v2d3FrameFlags.Encryption) != 0; result.FileAlterPreservation = (flags & ID3v2d3FrameFlags.FileAlterPreservation) != 0; result.GroupingIdentity = (flags & ID3v2d3FrameFlags.GroupingIdentity) != 0; result.ReadOnly = (flags & ID3v2d3FrameFlags.ReadOnly) != 0; result.TagAlterPreservation = (flags & ID3v2d3FrameFlags.TagAlterPreservation) != 0; return(result); }
/// <summary> /// Creates a <see cref="ID3v2FrameFlags"/> instance from the specified <see cref="ID3v2d4FrameFlags"/>. /// </summary> /// <param name="flags"></param> /// <returns></returns> public static ID3v2FrameFlags FromID3v2d4(ID3v2d4FrameFlags flags) { var result = new ID3v2FrameFlags(); result.Compression = (flags & ID3v2d4FrameFlags.Compression) != 0; result.DataLengthIndicator = (flags & ID3v2d4FrameFlags.DataLengthIndicator) != 0; result.Encryption = (flags & ID3v2d4FrameFlags.Encryption) != 0; result.FileAlterPreservation = (flags & ID3v2d4FrameFlags.FileAlterPreservation) != 0; result.GroupingIdentity = (flags & ID3v2d4FrameFlags.GroupingIdentity) != 0; result.ReadOnly = (flags & ID3v2d4FrameFlags.ReadOnly) != 0; result.TagAlterPreservation = (flags & ID3v2d4FrameFlags.TagAlterPreservation) != 0; result.Unsynchronisation = (flags & ID3v2d4FrameFlags.Unsynchronisation) != 0; return(result); }
/// <summary> /// Creates a new header. /// </summary> /// <param name="header">The tag header.</param> /// <param name="flags">The flags.</param> /// <param name="description">The description.</param> /// <param name="type">The type.</param> /// <param name="image">The image.</param> /// <param name="imageFormat">The image format.</param> /// <param name="quality">The quality.</param> /// <returns></returns> /// <exception cref="ArgumentOutOfRangeException"></exception> public static ID3v2APICFrame Create(ID3v2Header header, ID3v2FrameFlags flags, string description, ID3v2PictureType type, SkiaSharp.SKImage image, SkiaSharp.SKEncodedImageFormat imageFormat = SkiaSharp.SKEncodedImageFormat.Jpeg, int quality = 99) { var data = image.Encode(imageFormat, quality); string mimeType; switch (imageFormat) { case SkiaSharp.SKEncodedImageFormat.Jpeg: mimeType = MimeTypes.FromExtension(".jpg"); break; case SkiaSharp.SKEncodedImageFormat.Png: mimeType = MimeTypes.FromExtension(".png"); break; default: throw new ArgumentOutOfRangeException(string.Format("ImageFormat {0} not suppoerted!", imageFormat)); } return(Create(header, flags, description, type, mimeType, data.ToArray())); }
/// <summary>Creates a new ID3v2TextFrame.</summary> /// <param name="header">The header.</param> /// <param name="flags">The flags.</param> /// <param name="id">The identifier.</param> /// <param name="text">The text.</param> /// <returns></returns> /// <exception cref="NotSupportedException"></exception> public static ID3v2TextFrame Create(ID3v2Header header, ID3v2FrameFlags flags, string id, string text) { var encoding = ID3v2Encoding.Select(header, text); // header, encoding[1], name+0 var textBytes = ID3v2Encoding.GetBytes(encoding, text, true); var contentSize = 1 + textBytes.Length; var frameHeader = ID3v2FrameHeader.Create(header, id, flags, contentSize); using var ms = new MemoryStream(); var writer = new DataWriter(ms); writer.Write(frameHeader.Data); writer.Write((byte)encoding); writer.Write(textBytes); return(new ID3v2TextFrame(new ID3v2Frame(header, ms.ToArray()))); }
/// <summary>Creates a new header.</summary> /// <param name="header">The tag header.</param> /// <param name="flags">The flags.</param> /// <param name="data">The data.</param> /// <returns></returns> /// <exception cref="NotSupportedException">Unsupported Header Version.</exception> public static ID3v2XSLTFrame Create(ID3v2Header header, ID3v2FrameFlags flags, byte[] data) { switch (header.Version) { case 3: case 4: break; default: throw new NotSupportedException("Unsupported Header Version"); } var frameHeader = ID3v2FrameHeader.Create(header, "XSLT", flags, data.Length); using var ms = new MemoryStream(); var writer = new DataWriter(ms); writer.Write(frameHeader.Data); writer.Write(data); return(new ID3v2XSLTFrame(new ID3v2Frame(header, ms.ToArray()))); }
/// <summary>Creates a new header.</summary> /// <param name="header">The tag header.</param> /// <param name="flags">The flags.</param> /// <param name="name">The name.</param> /// <param name="value">The value.</param> /// <returns></returns> /// <exception cref="NotSupportedException"></exception> public static ID3v2TXXXFrame Create(ID3v2Header header, ID3v2FrameFlags flags, string name, string value) { var encoding = ID3v2Encoding.Select(header, name + value); // header, encoding[1], name+0, value+0 var nameBytes = ID3v2Encoding.GetBytes(encoding, name, true); var valueBytes = ID3v2Encoding.GetBytes(encoding, value, true); var contentSize = nameBytes.Length + valueBytes.Length + 1; var frameHeader = ID3v2FrameHeader.Create(header, "TXXX", flags, contentSize); using var ms = new MemoryStream(); var writer = new DataWriter(ms); writer.Write(frameHeader.Data); writer.Write((byte)encoding); writer.Write(nameBytes); writer.Write(valueBytes); return(new ID3v2TXXXFrame(new ID3v2Frame(header, ms.ToArray()))); }
private static Dictionary <ID3Tag, object> ParseID3v2(byte[] data) { // TODO: add support for ID3v2.2.0 shorter frame IDs Dictionary <ID3Tag, object> tags = new Dictionary <ID3Tag, object>(); if (data.Length < 21) // there must be at least one header (10 bytes) containing one frame (at least 10 byte header + 1 byte) { return(tags); } using (MemoryStream stream = new MemoryStream(data)) { byte[] header = new byte[10]; if (stream.Read(header, 0, header.Length) != header.Length) { throw new MetaParseException("Unable to read full ID3v2 tag header"); } if (header[0] != 'I' || header[1] != 'D' || header[2] != '3') { return(tags); } byte versionMajor = header[3]; byte versionMinor = header[4]; string version = $"ID3v2.{versionMajor}.{versionMinor}"; if (versionMajor != 3 && versionMajor != 4) { throw new NotImplementedException($"Only ID3v2.3.0 and ID3v2.4.0 are supported. Detected version: {version}"); } tags[ID3Tag.Version] = version; ID3v2HeaderFlags flags = (ID3v2HeaderFlags)header[5]; int size = BitConverter.ToInt32(header.Skip(6).Take(4).Reverse().ToArray(), 0); if (flags.HasFlag(ID3v2HeaderFlags.ExtendedHeader)) { byte[] extendedHeader = new byte[10]; if (stream.Read(extendedHeader, 0, extendedHeader.Length) != extendedHeader.Length) { throw new MetaParseException("Unable to read full ID3v2 extended header"); } int extendedSize = BitConverter.ToInt32(extendedHeader.Take(4).Reverse().ToArray(), 0); ID3v2ExtendedFlags extendedFlags = (ID3v2ExtendedFlags)((extendedHeader[4] << 8) | extendedHeader[5]); int sizeOfPadding = BitConverter.ToInt32(extendedHeader.Skip(6).Take(4).Reverse().ToArray(), 0); Trace.WriteLine($"[{tags[ID3Tag.Version]}] Ignoring extended header"); stream.Position += extendedSize; } while (stream.Position < size - 10) { byte[] frameHeader = new byte[10]; if (stream.Read(frameHeader, 0, frameHeader.Length) != frameHeader.Length) { throw new MetaParseException("Unable to read full ID3v2 frame header"); } string frameID = Encoding.ASCII.GetString(frameHeader, 0, 4); if (frameID == "\0\0\0\0") { break; // TODO: done? } int frameSize = BitConverter.ToInt32(frameHeader.Skip(4).Take(4).Reverse().ToArray(), 0); ID3v2FrameFlags frameFlags = (ID3v2FrameFlags)((frameHeader[8] << 8) | frameHeader[9]); switch (frameID) { case "TALB": tags[ID3Tag.Album] = ParseTextualFrame(stream, frameSize); break; case "TIT2": tags[ID3Tag.Title] = ParseTextualFrame(stream, frameSize); break; case "TPE2": tags[ID3Tag.Artist] = ParseTextualFrame(stream, frameSize); break; case "TYER": tags[ID3Tag.Year] = Int32.Parse(ParseTextualFrame(stream, frameSize)); break; case "TRCK": string track = ParseTextualFrame(stream, frameSize); if (track.Contains('/')) { tags[ID3Tag.Track] = Int32.Parse(track.Split('/')[0]); // "<current>/<total>" } else { tags[ID3Tag.Track] = Int32.Parse(track); } break; default: Trace.WriteLine($"[{tags[ID3Tag.Version]}] Ignoring frame ID: \"{frameID}\""); stream.Position += frameSize; break; } } } return(tags); }