lofty-rs icon indicating copy to clipboard operation
lofty-rs copied to clipboard

Support writing ID3v2 version 3.

Open sagudev opened this issue 3 years ago • 10 comments

Currently all ID3v2 tags are upgraded to version 4, but this is not always desired.

sagudev avatar Aug 11 '22 15:08 sagudev

Hm, not sure how to best go about this. Maybe ID3v2Tag::set_version?

Serial-ATA avatar Aug 11 '22 16:08 Serial-ATA

Maybe create something like ID3v2Tag::upgrade for 3 -> 4 and ID3v2Tag::downgrade for 4 -> 3 and on reading instead of force upgrading all to version 4 something like: 2 -> 3 3 -> 3 4 -> 4

sagudev avatar Aug 11 '22 17:08 sagudev

Allowing two different versions of the tag would complicate things quite a bit, even TagLib upgrades all tags to 2.4. TagLib lets you change the version with overloading, so maybe a new trait is the way to go, maybe ID3v2Writable?

Serial-ATA avatar Aug 11 '22 18:08 Serial-ATA

If I understand correctly this would meant that Lofty would store ID3v2.4 internally and than decide what version to use on saving? ID3v2.3 file would then on reading be upgraded (as it is now) to ID3v2.4 and on writing be downgraded back to ID3v2.3. Is transforming ID3v2.3 to version 4 lossless or lossy? Looking at Wikipedia it loos like more or less lossless with only few fields having some difficult mappings.

sagudev avatar Aug 12 '22 05:08 sagudev

The conversion isn't completely lossless, some frames have a 1:1 mapping and others have expanded in 2.4. The discarded frames aren't that important though IMO, I doubt most apps even look at them.

Serial-ATA avatar Aug 12 '22 06:08 Serial-ATA

I really would like for this to be implemented. Sadly the support for id3v2.4 is pretty bad and so I need to use v2.3. Lofty is a genius library, the only one that allows writing many different tag formats so easily so I would really like to use it.

FriederHannenheim avatar Feb 11 '23 18:02 FriederHannenheim

The only blocker here is how to best expose the option to downgrade the tag.

I may just end up going with the suggestion above:

Maybe create something like ID3v2Tag::upgrade for 3 -> 4 and ID3v2Tag::downgrade for 4 -> 3 and on reading instead of force upgrading all to version 4 something like: 2 -> 3 3 -> 3 4 -> 4

Serial-ATA avatar Feb 20 '23 01:02 Serial-ATA

Yeah I think that's a good solution

FriederHannenheim avatar Mar 15 '23 11:03 FriederHannenheim

I've been looking into this and I think it'd introduce way more complexity than needed. The current behavior of upgrading to version 4 will stay so there aren't a bunch of unnecessary version checks.

The best solution would probably be through a setting in WriteOptions once that gets implemented (#228). Probably use_id3v23.

There can be an almost entirely lossless v3 -> v4 conversion (with the exception of the RVAD and EQUA (?) frames). The couple of frames that can't be upgraded can be stored with FrameId::Outdated, and ignored on write unless allowed through WriteOptions::use_id3v23.

There's a list of ID3v2.4 frames that can be discarded when writing ID3v2.3:

   ASPI Audio seek point index [F:4.30]
   EQU2 Equalisation (2) [F:4.12]
   RVA2 Relative volume adjustment (2) [F:4.11]
   SEEK Seek frame [F:4.29]
   SIGN Signature frame [F:4.28]
   TDEN Encoding time [F:4.2.5]
   TDOR Original release time [F:4.2.5]
   TDRC Recording time [F:4.2.5]
   TDRL Release time [F:4.2.5]
   TDTG Tagging time [F:4.2.5]
   TIPL Involved people list [F:4.2.2]
   TMCL Musician credits list [F:4.2.2]
   TMOO Mood [F:4.2.3]
   TPRO Produced notice [F:4.2.4]
   TSOA Album sort order [F:4.2.5]
   TSOP Performer sort order [F:4.2.5]
   TSOT Title sort order [F:4.2.5]
   TSST Set subtitle [F:4.2.1]

Writing an ID3v2.4 tag as ID3v2.3 would be as "simple" as:

  1. Filtering out the above IDs
  2. Splitting dates into multiple frames (Will be much easier after #233)
    1. TDOR has its year split into TORY (Original release year)
    2. TDRC is split into three components: Year, month/day, and hour/minute, going into TYER (yyyy), TDAT (DDMM), and TIME (HHMM) respectively.
  3. TCON needs to be split and any number (<= 255) or the strings "RX" or "CR" need to be wrapped in parentheses. (Trivial after #286)
  4. All frames encoded TextEncoding::{UTF8, UTF16BE} need to be changed to TextEncoding::UTF16
  5. FrameFlags need to be written with their ID3v2.3 mappings: https://github.com/Serial-ATA/lofty-rs/blob/cc338a3bc5298ff6af6c377ca8cce500f068962d/src/id3/v2/frame/header.rs#L100-L128

Serial-ATA avatar Jul 11 '23 02:07 Serial-ATA

Everything's in place for this to be worked on now. I'll probably work on this after the next release.

Serial-ATA avatar May 10 '24 17:05 Serial-ATA