How to extract the content of a specific ID3 "User Defined Text" tag?

Started by FixEUser, May 19, 2024, 07:58:15 AM

Previous topic - Next topic

FixEUser

How can I extract the content of a specific ID3 "User Defined Text" tag?

Imagine you find this content in the tags of your mp3 file:

Album Artist Sort Order         : ABBA
User Defined Text               : (ARTISTS) ABBA
User Defined Text               : (BARCODE) 602498664636
User Defined Text               : (CATALOGNUMBER) 0602498664636
User Defined Text               : (MusicBrainz Album Artist Id) d87e52c5-bb8d-4da8-b941-9f4928627dc8
User Defined Text               : (MusicBrainz Album Id) 6b1d9e00-246f-4701-8f7f-57f7d2d998e6
User Defined Text               : (MusicBrainz Album Release Country) XE
User Defined Text               : (MusicBrainz Album Status) Official
User Defined Text               : (MusicBrainz Album Type) Album
User Defined Text               : (MusicBrainz Artist Id) d87e52c5-bb8d-4da8-b941-9f4928627dc8
User Defined Text               : (MusicBrainz Release Group Id) 1f78ea53-5e9c-46b6-8cd4-9d8a9c547a85
User Defined Text               : (MusicBrainz Release Track Id) 594c05ce-bbb5-45cd-b971-4e268d3b137b
User Defined Text               : (originalyear) 1974
User Defined Text               : (SCRIPT) Latn
Picture MIME Type               : image/jpeg

What is the most easy exiftool-syntax to get the "MusicBrainz Album Artist Id" only?

I learned, that we have the
-a -g0:4options to get something like this:
---- ID3:Copy4 ----
User Defined Text               : (MusicBrainz Album Artist Id) d87e52c5-bb8d-4da8-b941-9f4928627dc8
What exiftool-syntax would it be if the only way to get the content is using "ID3:Copy4"?

StarGeek

Quote from: FixEUser on May 19, 2024, 07:58:15 AMHow can I extract the content of a specific ID3 "User Defined Text" tag?

To get a specific one might take a user defined tag.

QuoteWhat exiftool-syntax would it be if the only way to get the content is using "ID3:Copy4"?
This won't work as you have specified an actual tag name. You could try ID3:Copy4:UserDefinedText but that assumes the item you are looking for is in the exact same location for every file, which seems unlikely.

With a user defined tag, you could look through all copies of that tag to find the correct one and return that.  But the problem is how to pass all copies of that tag name. I'm pretty sure I asked this question once, but can't remember the context, so it's hard to search for it.

I'll keep trying to figure it out, but it may require input from Phil.
* Did you read FAQ #3 and use the command listed there?
* Please use the Code button for exiftool code/output.
 
* Please include your OS, Exiftool version, and type of file you're processing (MP4, JPG, etc).

StarGeek

Ok, I got an idea and was able to edit the join_tags.config to make something that may work.

#------------------------------------------------------------------------------
# File:         join_tags_ID3.config
#
# Description:  ExifTool config file to generate new tags from ID3:UserDefinedText,
# based upon the original join_tags.config
#
# Revisions:    2020-11-16 - P. Harvey created
# 2024-05-19 - Bryan (StarGeek) Williams edited
#
#------------------------------------------------------------------------------

my $nameTag = 'UserDefinedText';

sub JoinTags($$)
{
    my ($val, $et) = @_;
    my $table = Image::ExifTool::GetTagTable('Image::ExifTool::Composite');
    my $i;
    for ($i=0; ;++$i) {
        my $suffix = $i ? " ($i)" : '';
my $temp = $et->GetValue("$nameTag$suffix") or last;
$temp=~m/^\(([^)]+)\)\s*(.*)/;
my $name = $1 or last;
        my $value = $2;
        last unless defined $value;
        my $tagInfo = Image::ExifTool::AddTagToTable($table, $name, {
            Name => Image::ExifTool::MakeTagName($name),
        });
        $et->FoundTag($tagInfo, $value);
    }
    return undef;
}

%Image::ExifTool::UserDefined = (
    'Image::ExifTool::Composite' => {
        JoinTagsID3 => {
            Desire => {
                0 => $nameTag,
                1 => $valueTag,
            },
            RawConv => \&JoinTags,
        },
    },
);

1; #end

This will look at the UserDefinedText tags, parse out the data between the parentheses (only if it is at the start of the tag data), create a tag name based upon that, and give that tag a value equal to the rest of the string, trimming leading spaces.

A couple of tests.
C:\>exiftool -config join_tags_ID3.config -G1 -a -s -UserDefinedText -Composite:all Y:\!temp\x\y\test1.mp3
[ID3v2_3]       UserDefinedText                 : (Rip date) 2008-06-22
[ID3v2_3]       UserDefinedText                 : (Source) CD (LP)
[ID3v2_3]       UserDefinedText                 : (Ripping tool) EAC
[ID3v2_3]       UserDefinedText                 : (Release type) Normal Release
[Composite]     DateTimeOriginal                : 2008:24:06
[Composite]     AudioBitrate                    : 206 kbps
[Composite]     Duration                        : 0:03:22 (approx)
[Composite]     Releasetype                     : Normal Release
[Composite]     Ripdate                         : 2008-06-22
[Composite]     Source                          : CD (LP)
[Composite]     Rippingtool                     : EAC

Here's one I thought would break it.  It has a tag with "([rating]) 3" and another with "(Rating) 0".  I thought the brackets might break this.
C:\>exiftool -config join_tags_ID3.config -G1 -a -s -UserDefinedText -composite:all Y:\!temp\x\y\test2.mp3
[ID3v2_3]       UserDefinedText                 : ([rating]) 3
[ID3v2_3]       UserDefinedText                 : (Release type) Normal release
[ID3v2_3]       UserDefinedText                 : (Rip date) 2007-09-14
[ID3v2_3]       UserDefinedText                 : (Ripping tool) EAC
[ID3v2_3]       UserDefinedText                 : (Source) CD
[ID3v2_3]       UserDefinedText                 : (Rating) 0
[Composite]     DateTimeOriginal                : 2007:11:09
[Composite]     AudioBitrate                    : 186 kbps
[Composite]     Duration                        : 0:03:45 (approx)
[Composite]     Rating                          : 0
[Composite]     Rating                          : 3
[Composite]     Releasetype                     : Normal release
[Composite]     Ripdate                         : 2007-09-14
[Composite]     Rippingtool                     : EAC
[Composite]     Source                          : CD

I do not know what will happen if there's a UserDefinedText tag that does not start with parentheses.  It might break in that case. If it does, send me a sample and I'll see if I can fix it.
* Did you read FAQ #3 and use the command listed there?
* Please use the Code button for exiftool code/output.
 
* Please include your OS, Exiftool version, and type of file you're processing (MP4, JPG, etc).

FixEUser

Thank you for your attempt.

There are at least 3 problems with your script:

1) It doesn't return any other value then without -config join_tags_ID3-config
2) It returns an error "join_tags_ID3.config did not return a true value at Image/ExifTool.pm line 9675."
3) For other readers: The config file needs to be named exactly the same as you use it in the command line:
exiftool -config join_tags_ID3.config -G1 -a -s -UserDefinedText Y:\temp\x\y\test.mp3

@StarGeek: I'm not sure if you understand my initial question:

I asked for a solution to get 1 (one) specific tag content, for example "MusicBrainz Album Artist Id".
The idea would be a (not yet working) syntax like
exiftool -config specificID3.config -G1 -a -s -UserDefinedText("MusicBrainz Album Artist Id") music.mp3and then get only this value:
d87e52c5-bb8d-4da8-b941-9f4928627dc8

How could I achieve that?

PS
Are you sure that
my $table = Image::ExifTool::GetTagTable('Image::ExifTool::Composite');
is the correct way to get the UserDefinedText content?

FixEUser

I think, I have found a workaround with a Windows batch file.
This may be a little bit more convenient for Windows users.

Write (copy & paste) this code in a batch file and name it like
ShowContentOfThisUndefinedText.cmd

ECHO OFF
@CLS
SETLOCAL enabledelayedexpansion
FOR /F "tokens=2 delims=)" %%M IN ('   
    exiftool.exe -a -UserDefinedText -ext mp3 -recurse . ^| findstr /c:%1 /i
    ') DO (   
          SET LeftTrimmedValue=%%M         
          ECHO !LeftTrimmedValue:~1!         
          )

This batch file assumes, that exiftool.exe is in the same directory as the batch.
Otherwise, you have to write the full path to your exiftool.exe like
C:\Path\To\My\exiftool.exe

Then copy the batch file to the root directory in which the mp3 files to be checked are stored.

Open a CMD window in this root directory.

Start the batch file and add the string you want to search for in the UndefinedText ID3 tag like this:
ShowContentOfThisUndefinedText "musicbrainz album artist id"and you get the wanted
d87e52c5-bb8d-4da8-b941-9f4928627dc8

Explanations for the batch code:
1) exiftool shows all the ID3 TXXX tags (aka "UserDefinedText") for all mp3 files recursively
2) the Windows command FINDSTR searches for all lines that contain the given string like "musicbrainz album artist id" case-IN-sensitive
3) to get only the tag content (neither "UserDefinedText" nor the tag name in brackets), only the second token after the closing bracket will be outputted
3) to left trim the found content, the first character will be stripped away

Remark:
This batch is only intended for MP3 files and the ID3 TXXX "UserDefinedText" tag.

StarGeek

Running the command on multiple files, I do see there is a problem where subsequent files will return an entry containing HASH(0x...) for previously defined tags, so I'll have to work on that.  But on single files, it works correctly.

Quote from: FixEUser on May 20, 2024, 04:48:34 AMThere are at least 3 problems with your script:

1) It doesn't return any other value then without -config join_tags_ID3-config

I'm not quite sure what you mean here. It still extracts all the rest of the data normally and as I said, extracting just the data you want will probably require a config file. Maybe Phil can figure out some way of extracting just the data for a specific copy of the tag without requiring it to be the exact same copy location each time, but I can't think of any way to do that.

Quote2) It returns an error "join_tags_ID3.config did not return a true value at Image/ExifTool.pm line 9675."

That sounds like you didn't save the complete config file.  Line 9675 in the code is checking for the the very last line of the config file
1; #end
Quote3) For other readers: The config file needs to be named exactly the same as you use it in the command line:
exiftool -config join_tags_ID3.config -G1 -a -s -UserDefinedText Y:\temp\x\y\test.mp3

I'm not sure what your point is. If you're using a config file with the -Config option, you have to use the same name you use when you saved it to disk.

Quote@StarGeek: I'm not sure if you understand my initial question:

I asked for a solution to get 1 (one) specific tag content, for example "MusicBrainz Album Artist Id".
The idea would be a (not yet working) syntax like
exiftool -config specificID3.config -G1 -a -s -UserDefinedText("MusicBrainz Album Artist Id") music.mp3and then get only this value:
d87e52c5-bb8d-4da8-b941-9f4928627dc8

How could I achieve that?

That's what my config file attempts to do. At least, once I fix the bug. It would take the UserDefinedText that contains "(MusicBrainz Album Artist Id)" and return the contents of that as a tag called MusicBrainzAlbumArtistId.

It works correctly on a single file, just not in batch ATM.
Example, the UserDefinedText that contains "(MusicBrainz TRM Id)" creates a tag called MusicBrainzTRMId which containts the value of "e5d018af-3a9d-4ed9-9593-a7b0b63c4579".
C:\>exiftool -config join_tags_ID3.config -G1 -a -s -MusicBrainzTRMId "Y:/Music/Music/Sorted/Albums/Journey/Greatest_Hits/01-Only_the_Young.mp3"
[Composite]     MusicBrainzTRMId                : e5d018af-3a9d-4ed9-9593-a7b0b63c4579

Listing all the "MusicBrainz" tags created
C:\>exiftool -config join_tags_ID3.config -G1 -a -s -UserDefinedText -*Brainz* "Y:/Music/Music/Sorted/Albums/Journey/Greatest_Hits/01-Only_the_Young.mp3"
[ID3v2_3]       UserDefinedText                 : (MusicBrainz TRM Id) e5d018af-3a9d-4ed9-9593-a7b0b63c4579
[ID3v2_3]       UserDefinedText                 : (MusicBrainz Artist Id) abd506e1-6f2b-4d6f-b937-92c267f6f88b
[ID3v2_3]       UserDefinedText                 : (MusicBrainz Album Id) 43ca7ac8-bd36-4468-bab4-004a9a34de66
[ID3v2_3]       UserDefinedText                 : (MusicBrainz Album Type) compilation
[ID3v2_3]       UserDefinedText                 : (MusicBrainz Album Status) official
[ID3v2_3]       UserDefinedText                 : (MusicBrainz Album Artist Id)
[ID3v2_3]       UserDefinedText                 : (MusicBrainz Album Release Country) US
[Composite]     MusicBrainzAlbumReleaseCountry  : US
[Composite]     MusicBrainzTRMId                : e5d018af-3a9d-4ed9-9593-a7b0b63c4579
[Composite]     MusicBrainzArtistId             : abd506e1-6f2b-4d6f-b937-92c267f6f88b
[Composite]     MusicBrainzAlbumId              : 43ca7ac8-bd36-4468-bab4-004a9a34de66
[Composite]     MusicBrainzAlbumType            : compilation
[Composite]     MusicBrainzAlbumStatus          : official
[Composite]     MusicBrainzAlbumArtistId        :

QuoteAre you sure that
my $table = Image::ExifTool::GetTagTable('Image::ExifTool::Composite');
is the correct way to get the UserDefinedText content?

This isn't getting the UserDefinedText. This is the part that creates the new tags.  The new tags get added to the Composite group.

The parts that get the UserDefinedText are
my $nameTag = 'UserDefinedText';and
            Desire => {
                0 => $nameTag,

Final edit: I haven't been able to fix the HASH problem when using it on multiple files, so I'm bugging Phil about fixing it.
* Did you read FAQ #3 and use the command listed there?
* Please use the Code button for exiftool code/output.
 
* Please include your OS, Exiftool version, and type of file you're processing (MP4, JPG, etc).

FixEUser

Thanks for your detailed answer and your efforts.

QuoteThat sounds like you didn't save the complete config file.  Line 9675 in the code is checking for the the very last line of the config file
You are right! My fault, the last line was missing for unknown reasons.

Please remember that I really would prefer the content of the specified tag itself:
e5d018af-3a9d-4ed9-9593-a7b0b63c4579
not the output of the entire line:
[Composite]    MusicBrainzTRMId                : e5d018af-3a9d-4ed9-9593-a7b0b63c4579

Otherwise, I would still have to use an additional batch to strip away the part before the : (colon) to get the needed part after the : (colon) and the space.

StarGeek

Use the -S (-veryShort) option or the -p (-printFormat) option.

I haven't been able to figure out how to make it work on more than one file at a time and am waiting to see if Phil can offer any insight, but I suspect the weather is very good in his area ATM and he's out birding.
* Did you read FAQ #3 and use the command listed there?
* Please use the Code button for exiftool code/output.
 
* Please include your OS, Exiftool version, and type of file you're processing (MP4, JPG, etc).

Phil Harvey

Quote from: StarGeek on May 20, 2024, 04:34:29 PMI suspect the weather is very good in his area ATM and he's out birding.

Correct.  :)  (It is the spring bird migration season now.)

I'm adding a patch to ExifTool 12.85 to do what StarGeek is trying to do in his config file, and generate named tags for the ID3 UserDefinedText tags.

- Phil

...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Phil Harvey

...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Phil Harvey

I'm tweaking this a bit, so some tag names may be different for version 12.86.  Specifically, some underlines in tag names may be removed in favour of camel case.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

StarGeek

While trying to make this config file (which is no longer needed), I run exiftool to find all my mp3s that had UserDefinedText. I just ran 12.85 against those and didn't see any obvious problems

Example output with 12.85
c:\exiftool -G1 -a -s -userdefined:all -@ temp.txt
<snip...>
======== Y:/Music/Music/Sorted/Albums/Journey/Greatest_Hits/01-Only_the_Young.mp3
[UserDefined]   MusicBrainzTRMId                : e5d018af-3a9d-4ed9-9593-a7b0b63c4579
[UserDefined]   MusicBrainzArtistId             : abd506e1-6f2b-4d6f-b937-92c267f6f88b
[UserDefined]   MusicBrainzAlbumId              : 43ca7ac8-bd36-4468-bab4-004a9a34de66
[UserDefined]   MusicBrainzAlbumType            : compilation
[UserDefined]   MusicBrainzAlbumStatus          : official
[UserDefined]   MusicBrainzAlbumArtistId        :
[UserDefined]   MusicBrainzAlbumReleaseCountry  : US
* Did you read FAQ #3 and use the command listed there?
* Please use the Code button for exiftool code/output.
 
* Please include your OS, Exiftool version, and type of file you're processing (MP4, JPG, etc).

FixEUser

Thank you both @Phil and @StarGeek for the newest exiftool version.

The commandline
exiftool -G1 -a -s -userdefined:MusicBrainzAlbumArtistId music.mp3now extract this line:
[UserDefined]   MusicBrainzAlbumArtistId        : d87e52c5-bb8d-4da8-b941-9f4928627dc8
I also tried -s3 but still get
UserDefined d87e52c5-bb8d-4da8-b941-9f4928627dc8
What exactly to I have to use as syntax to get only this output value?
d87e52c5-bb8d-4da8-b941-9f4928627dc8

StarGeek

Don't include the -G (-groupNames) option. That's an interaction between -G and -s3 that I didn't know about.

C:\Programs\My_Stuff>exiftool -s3 -MusicBrainzArtistId Y:/Music/Music/Sorted/Albums/Journey/Greatest_Hits/01-Only_the_Young.mp3
abd506e1-6f2b-4d6f-b937-92c267f6f88b

And there is always the -p option, as I mentioned
C:\>exiftool -p "$MusicBrainzArtistId" Y:/Music/Music/Sorted/Albums/Journey/Greatest_Hits/01-Only_the_Young.mp3
abd506e1-6f2b-4d6f-b937-92c267f6f88b
* Did you read FAQ #3 and use the command listed there?
* Please use the Code button for exiftool code/output.
 
* Please include your OS, Exiftool version, and type of file you're processing (MP4, JPG, etc).

FixEUser

GREAT!

Thank you for your patience and the simple and easy to understand solution.

No additional batch file, nothing.
Just exiftool -s3 , the needed UserDefinedText tag name and the music file.

I'm really happy!