From c2a60dd2ca5b5e9712976e66442154992af0dfac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Sch=C3=B6nherr?= Date: Tue, 21 Sep 2021 12:55:00 +0200 Subject: [PATCH] implement WriteTag wipeout and shrinking options --- src/Id3.Net/Id3/Id3Handler.cs | 2 +- src/Id3.Net/Id3/v1/Id3v1Handler.cs | 2 +- src/Id3.Net/Id3/v2/Id3v23Handler.cs | 47 ++++++++++++++++++++++++++--- src/Id3.Net/Mp3/Mp3.cs | 31 ++++++++++++------- src/Id3.Net/Mp3/WriteTagOptions.cs | 17 +++++++++++ 5 files changed, 82 insertions(+), 17 deletions(-) create mode 100644 src/Id3.Net/Mp3/WriteTagOptions.cs diff --git a/src/Id3.Net/Id3/Id3Handler.cs b/src/Id3.Net/Id3/Id3Handler.cs index dc39c1f..ffeb6ba 100644 --- a/src/Id3.Net/Id3/Id3Handler.cs +++ b/src/Id3.Net/Id3/Id3Handler.cs @@ -89,7 +89,7 @@ internal string GetFrameIdFromFrame(Id3Frame frame) internal abstract byte[] GetTagBytes(Stream stream); internal abstract bool HasTag(Stream stream); internal abstract Id3Tag ReadTag(Stream stream, out object additionalData); - internal abstract bool WriteTag(Stream stream, Id3Tag tag); + internal abstract bool WriteTag(Stream stream, Id3Tag tag, WriteTagOptions options); #endregion #region ID3 tag properties for the handler diff --git a/src/Id3.Net/Id3/v1/Id3v1Handler.cs b/src/Id3.Net/Id3/v1/Id3v1Handler.cs index 4141c91..741fc3e 100644 --- a/src/Id3.Net/Id3/v1/Id3v1Handler.cs +++ b/src/Id3.Net/Id3/v1/Id3v1Handler.cs @@ -98,7 +98,7 @@ internal override Id3Tag ReadTag(Stream stream, out object additionalData) return tag; } - internal override bool WriteTag(Stream stream, Id3Tag tag) + internal override bool WriteTag(Stream stream, Id3Tag tag, WriteTagOptions options) { Encoding encoding = TextEncodingHelper.GetDefaultEncoding(); diff --git a/src/Id3.Net/Id3/v2/Id3v23Handler.cs b/src/Id3.Net/Id3/v2/Id3v23Handler.cs index 40ecc24..6f0df29 100644 --- a/src/Id3.Net/Id3/v2/Id3v23Handler.cs +++ b/src/Id3.Net/Id3/v2/Id3v23Handler.cs @@ -1,4 +1,4 @@ -#region --- License & Copyright Notice --- + #region --- License & Copyright Notice --- /* Copyright (c) 2005-2019 Jeevan James All rights reserved. @@ -156,7 +156,7 @@ internal override Id3Tag ReadTag(Stream stream, out object additionalData) return tag; } - internal override bool WriteTag(Stream stream, Id3Tag tag) + internal override bool WriteTag(Stream stream, Id3Tag tag, WriteTagOptions options) { byte[] tagBytes = GetTagBytes(tag); int requiredTagSize = tagBytes.Length; @@ -165,7 +165,13 @@ internal override bool WriteTag(Stream stream, Id3Tag tag) int currentTagSize = GetTagSize(stream); if (requiredTagSize > currentTagSize) MakeSpaceForTag(stream, currentTagSize, requiredTagSize); - } else + else if (requiredTagSize < currentTagSize) + if (options.ShrinkFile && (currentTagSize - requiredTagSize) > options.ShrinkFileThreshold) + ShrinkSpaceForTag(stream, currentTagSize, requiredTagSize); + else if (options.WipeOut) + WipeOutRemainingSpace(stream, currentTagSize, requiredTagSize); + } + else MakeSpaceForTag(stream, 0, requiredTagSize); stream.Seek(0, SeekOrigin.Begin); @@ -310,6 +316,39 @@ private static void MakeSpaceForTag(Stream stream, int currentTagSize, int requi } } - private const int BufferSize = 8192; + private static void ShrinkSpaceForTag(Stream stream, int currentTagSize, int requiredTagSize) + { + if (currentTagSize < requiredTagSize) + return; + + int streamLength = (int)stream.Length; + var readPos = currentTagSize; + int writePos = requiredTagSize; + + var buffer = new byte[BufferSize]; + while (readPos < streamLength) + { + int bytesToRead = (readPos + BufferSize > streamLength) ? streamLength - readPos : BufferSize; + stream.Seek(readPos, SeekOrigin.Begin); + stream.Read(buffer, 0, bytesToRead); + stream.Seek(writePos, SeekOrigin.Begin); + stream.Write(buffer, 0, bytesToRead); + readPos += bytesToRead; + writePos += bytesToRead; + } + stream.SetLength(writePos); + } + + private static void WipeOutRemainingSpace(Stream stream, int currentTagSize, int requiredTagSize) + { + if (currentTagSize < requiredTagSize) + return; + + var remainingSize = currentTagSize - requiredTagSize; + stream.Seek(requiredTagSize, SeekOrigin.Begin); + stream.Write(new byte[remainingSize], 0, remainingSize); + } + + private const int BufferSize = 512 * 1024; // 8192 - memory is no big issue today, so reduce the seeking efforts } } \ No newline at end of file diff --git a/src/Id3.Net/Mp3/Mp3.cs b/src/Id3.Net/Mp3/Mp3.cs index 33133db..ca54b82 100644 --- a/src/Id3.Net/Mp3/Mp3.cs +++ b/src/Id3.Net/Mp3/Mp3.cs @@ -272,10 +272,26 @@ public bool HasTagOfVersion(Id3Version version) => #region Tag writing methods public bool UpdateTag(Id3Tag tag) { - return WriteTag(tag, WriteConflictAction.Replace); + return WriteTag(tag, new WriteTagOptions() { ConflictAction = WriteConflictAction.Replace }); } public bool WriteTag(Id3Tag tag, WriteConflictAction conflictAction = WriteConflictAction.NoAction) + { + return WriteTag(tag, new WriteTagOptions() { ConflictAction = conflictAction }); + } + + public bool WriteTag(Id3Tag tag, Id3Version version, WriteConflictAction conflictAction = WriteConflictAction.NoAction) + { + return WriteTag(tag, version, new WriteTagOptions() { ConflictAction = conflictAction }); + } + + public bool WriteTag(Id3Tag tag, Id3Version version, WriteTagOptions options) + { + tag.Version = version; + return WriteTag(tag, options); + } + + public bool WriteTag(Id3Tag tag, WriteTagOptions options) { if (tag == null) throw new ArgumentNullException(nameof(tag)); @@ -290,9 +306,9 @@ public bool WriteTag(Id3Tag tag, WriteConflictAction conflictAction = WriteConfl Id3Handler handler = familyHandler; if (handler.Version != tag.Version) { - if (conflictAction == WriteConflictAction.NoAction) + if (options.ConflictAction == WriteConflictAction.NoAction) return false; - if (conflictAction == WriteConflictAction.Replace) + if (options.ConflictAction == WriteConflictAction.Replace) { Id3Handler handlerCopy = handler; //TODO: Why did we need a copy of the handler? handlerCopy.DeleteTag(Stream); @@ -302,18 +318,11 @@ public bool WriteTag(Id3Tag tag, WriteConflictAction conflictAction = WriteConfl //Write the tag to the file. The handler will know how to overwrite itself. Id3Handler writeHandler = Id3Handler.GetHandler(tag.Version); - bool writeSuccessful = writeHandler.WriteTag(Stream, tag); + bool writeSuccessful = writeHandler.WriteTag(Stream, tag, options); if (writeSuccessful) InvalidateExistingHandlers(); return writeSuccessful; } - - public bool WriteTag(Id3Tag tag, Id3Version version, - WriteConflictAction conflictAction = WriteConflictAction.NoAction) - { - tag.Version = version; - return WriteTag(tag, conflictAction); - } #endregion #region Audio stream members diff --git a/src/Id3.Net/Mp3/WriteTagOptions.cs b/src/Id3.Net/Mp3/WriteTagOptions.cs new file mode 100644 index 0000000..35eeda7 --- /dev/null +++ b/src/Id3.Net/Mp3/WriteTagOptions.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Id3 +{ + public class WriteTagOptions + { + public WriteConflictAction ConflictAction { get; set; } = WriteConflictAction.NoAction; + + public bool WipeOut { get; set; } = true; + + public bool ShrinkFile { get; set; } = true; + + public int ShrinkFileThreshold { get; set; } = 0; + } +}