Zeugma440 / atldotnet

Fully managed, portable and easy-to-use C# library to read and edit audio data and metadata (tags) from various audio formats, playlists and CUE sheets
MIT License
442 stars 60 forks source link

Fails to work with OPUS files with cover embedded not using atldotnet #208

Closed fsobolev closed 12 months ago

fsobolev commented 1 year ago

The problem

atldotnet fails to read embedded covers in OPUS files and doesn't write any changes to such files. If a file doesn't contain cover or if a cover was embedded using atldotnet, then it works fine.

Environment

Details

Error when trying to save a file:

The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.
   at System.Convert.FromBase64_ComputeResultLength(Char* inputPtr, Int32 inputLength)
   at System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength)
   at System.Convert.FromBase64CharArray(Char[] inArray, Int32 offset, Int32 length)
   at ATL.AudioData.IO.VorbisTag.SetPictureItem(Stream Source, String tagId, Int32 size, ReadTagParams readTagParams)
   at ATL.AudioData.IO.VorbisTag.read(Stream source, ReadTagParams readTagParams)
   at ATL.AudioData.IO.Ogg.readCommentPacket(BufferedBinaryReader source, Int32 contentType, VorbisTag tag, ReadTagParams readTagParams)
   at ATL.AudioData.IO.Ogg.getInfo(BufferedBinaryReader source, FileInfo info, ReadTagParams readTagParams)
   at ATL.AudioData.IO.Ogg.Read(Stream source, ReadTagParams readTagParams)
   at ATL.AudioData.AudioDataManager.read(Stream source, ReadTagParams readTagParams)
   at ATL.AudioData.AudioDataManager.ReadFromFile(Boolean readEmbeddedPictures, Boolean readAllMetaFrames)
The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.
   at System.Convert.FromBase64_ComputeResultLength(Char* inputPtr, Int32 inputLength)
   at System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength)
   at System.Convert.FromBase64CharArray(Char[] inArray, Int32 offset, Int32 length)
   at ATL.AudioData.IO.VorbisTag.SetPictureItem(Stream Source, String tagId, Int32 size, ReadTagParams readTagParams)
   at ATL.AudioData.IO.VorbisTag.read(Stream source, ReadTagParams readTagParams)
   at ATL.AudioData.IO.Ogg.readCommentPacket(BufferedBinaryReader source, Int32 contentType, VorbisTag tag, ReadTagParams readTagParams)
   at ATL.AudioData.IO.Ogg.getInfo(BufferedBinaryReader source, FileInfo info, ReadTagParams readTagParams)
   at ATL.AudioData.IO.Ogg.Read(Stream source, ReadTagParams readTagParams)
   at ATL.AudioData.AudioDataManager.read(Stream source, ReadTagParams readTagParams)
   at ATL.AudioData.AudioDataManager.ReadFromFile(Boolean readEmbeddedPictures, Boolean readAllMetaFrames)
Cannot access a closed file.
   at System.IO.FileStream.get_Length()
   at ATL.BufferedBinaryReader..ctor(Stream stream)
   at ATL.AudioData.IO.Ogg.Read(Stream source, ReadTagParams readTagParams)
   at ATL.AudioData.IO.Ogg.Write(Stream s, TagData tag, Action`1 writeProgress)
   at ATL.AudioData.AudioDataManager.UpdateTagInFile(TagData theTag, TagType tagType, ProgressManager writeProgress)

I also has a corrupted file with incomplete cover, in this case the error is different:

Stream should not be null and be at least 3 bytes long
   at ATL.PictureInfo.fromBinaryData(Stream stream, Int32 length, PIC_TYPE picType, TagType tagType, Object nativePicCode, Int32 position)
   at ATL.AudioData.IO.VorbisTag.ReadPicture(Stream s, ReadTagParams readTagParams)
   at ATL.AudioData.IO.VorbisTag.SetPictureItem(Stream Source, String tagId, Int32 size, ReadTagParams readTagParams)
   at ATL.AudioData.IO.VorbisTag.read(Stream source, ReadTagParams readTagParams)
   at ATL.AudioData.IO.Ogg.readCommentPacket(BufferedBinaryReader source, Int32 contentType, VorbisTag tag, ReadTagParams readTagParams)
   at ATL.AudioData.IO.Ogg.getInfo(BufferedBinaryReader source, FileInfo info, ReadTagParams readTagParams)
   at ATL.AudioData.IO.Ogg.Read(Stream source, ReadTagParams readTagParams)
   at ATL.AudioData.IO.Ogg.Write(Stream s, TagData tag, Action`1 writeProgress)
   at ATL.AudioData.AudioDataManager.UpdateTagInFile(TagData theTag, TagType tagType, ProgressManager writeProgress)

It would be nice to have an ability to force overwrite cover in case of corruption.

Let me know if I can provide additional information.

nlogozzo commented 1 year ago

Hi, just wanted to introduce myself too...I'm Nick and me and @fsobolev build projects under Nickvision, an open-source community.

We are currently working on Tagger, a music tagger for GNOME linux, in which we use ATL.NET. So through our development we've found this issue and other previously opened by Fyodor.

We are more than happy to help debug this issue and others day and night ;)


I wanted to attach this opus file that we had that first caused this issue for us in case it might help:
A.zip

Sorry for the zip, GitHub didn't like .opus 😆

Zeugma440 commented 1 year ago

Thanks for your feedback ! I'll let you know what I find.

@nlogozzo is the sample file for stracktrace 1 or stracktrace 2 ? I'd love to have a sample for both 😁

FYI, ATL has been implemented to be close to the specs, and is generally not good at handling corrupted/exotic files, especially on less popular formats. The more feedback like that one, the more robust the library will become 💪

nlogozzo commented 1 year ago

@nlogozzo is the sample file for stracktrace 1 or stracktrace 2 ? I'd love to have a sample for both 😁

I believe both. As we said the first stack trace is for when you call Save() on Track and the second is just on new Track.

FYI, ATL has been implemented to be close to the specs, and is generally not good at handling corrupted/exotic files, especially on less popular formats. The more feedback like that one, the more robust the library will become 💪

ATL is already pretty robust I must say. We had a file that had a corrupted header that taglib# could not read/write but ATL was able to manage it no problem!

fsobolev commented 1 year ago

is the sample file for stracktrace 1 or stracktrace 2 ? I'd love to have a sample for both

I believe both. As we said the first stack trace is for when you call Save() on Track and the second is just on new Track.

No, second stack trace is on Save() too, with that sample.

For stack trace 1, here's another sample: B.zip

This is an audio extracted from youtube video using yt-dlp.

Zeugma440 commented 12 months ago

That one should be good too. Both issues were read-related and have been fixed ✅

nlogozzo commented 12 months ago

Could you make a new release? I'll test it in Tagger asap...

Zeugma440 commented 12 months ago

Sure. I'm first waiting for the CI to complete, in case there are Sonar issues.

Zeugma440 commented 12 months ago

Available on today's v4.35 \o/

Please close the issue if you confirm the fix on your side~

nlogozzo commented 12 months ago

Fixed on my system!!

@fsobolev will close when he confirms the fix on his side as well :)

fsobolev commented 12 months ago

Seems to work good, that was fast, thanks! :)

Zeugma440 commented 12 months ago

You're welcome! By the way, I'm super glad you chose ATL for your Tagger app 😄

Don't hesitate to push other reports/suggestions~