Author Topic: A Picard script to create Album Genre tags derived from track genres  (Read 1363 times)

hiccup

  • Hero Member
  • *****
  • Posts: 9111

A Picard script to create Album Genre tags derived from its track genres.


If you tag your albums with genre tags per individual track, it is not possible to filter complete albums on their genre.
A filter or an auto-playlist based on genre will then only return tracks, not full albums.
To make that possible, I have created a script (well, two of them) that will create an Album genre tag, consistent for all the album's tracks, derived from the individual track's genres.
So when using that tag for filters or playlists, complete albums will be returned.

To give some idea:




The script uses these rules for deciding on what genres to use for this Album genre tag:

1. All genres that exist in at least 70% of the tracks are used.
2. If there are no genres that meet that condition, the threshold is lowered to 30%.
    with the exception that genres that only exist for 1 track are ignored
3. If there are also no genres that meet the 30% condition, all genres are used.
    again with the exception that genres that only exist for 1 track are ignored
4. For releases that contain only two tracks, all genres for these two tracks are used.


The album genres are ordered by the frequency of them occurring in the album's tracks.


Three important notes:

1. This requires Picard's Persistent Variables plugin.
2. Both scripts must be added individually, so do not join them together in one script.
3. The script creates a tag named: album_genres
   So in MusicBee you'll need to create a custom tag that uses that tag as the source.


In Picard the scripts should look something like this:




These are the scripts:

create album genres - 1
Code
$setmulti(_001_trackgenres,$rreplace(%genre%,\(\\w\)\\s,\\1@))
$set(_002_track_genres_count,$lenmulti(%genre%))
$set_a(_aggregate_genres,$trim($get_a(_aggregate_genres); %_001_trackgenres%, ;))
$setmulti(_003_aggregated_genres,$get_a(_aggregate_genres))
$set(_003_aggregated_genres_count,$lenmulti(%_003_aggregated_genres%))
$if($and($not($get_a(_processed_track_%tracknumber%)),$add(0,%_002_track_genres_count%)),
  $set_a(_processed_track_%tracknumber%,1)
  $set_a(total,$add($if2($get_a(total),0),%_002_track_genres_count%))
)


create album genres - 2
Code
$set(_004_total_album_genres_count,$get_a(total))$set(_005_repeats,%_004_total_album_genres_count%))$set(_006_aggregated_genres_flat,$rreplace(%_003_aggregated_genres%,;\\s,\,))$while($lt(%_loop_count%,%_005_repeats%),$set(_007_aggregated_genres_flat_corrected,$rsearch(%_006_aggregated_genres_flat%,^.[^\,]*\,?))$set(_006_aggregated_genres_flat,$rreplace(%_006_aggregated_genres_flat%,^.[^\,]*\,?,))$copymerge(_007_aggregated_genres_flat_corrected_b,_007_aggregated_genres_flat_corrected,keep_duplicates))$setmulti(_007_aggregated_genres_flat_corrected_multi,$replace(%_007_aggregated_genres_flat_corrected_b%,\,,))$set(_008_genres_count,$lenmulti(%_007_aggregated_genres_flat_corrected_multi%))$setmulti(_009_unique_genres,$unique(%_007_aggregated_genres_flat_corrected_multi%))$set(_010_unique_genres_count,$lenmulti(%_009_unique_genres%))$foreach(%_007_aggregated_genres_flat_corrected_multi%,$set(_count_%_loop_value%,$add($if2($get(_count_%_loop_value%),0),1)))$unset(_011_tagcounts)$foreach($unique(%_007_aggregated_genres_flat_corrected_multi%),$setmulti(_011_tagcounts,%_011_tagcounts%; %_loop_value%_$get(_count_%_loop_value%)))$cleanmulti(_011_tagcounts)$set(_012_tagcounts,$rreplace(%_011_tagcounts%,\(?<=_\)\(\\d\)\(?=;|\$\),0\\1))$set(_012_tagcounts,$rreplace(%_012_tagcounts%,\(?<=_\)\(\\d\\d\)\(?=;|\$\),0\\1))$setmulti(_013_tagcounts,$rreplace(%_012_tagcounts%,^\(.[^_]*\)_\(\\d\\d\\d\),\\2_\\1))$while($lt(%_loop_count%,$sub(%_010_unique_genres_count%,1)),$setmulti(_013_tagcounts,$rreplace(%_013_tagcounts%,\(?<!_\\d\\d\\d\);\\s\(.[^_]*\)_\(\\d\\d\\d\(?=\$|;\)\),; \\2_\\1)))$setmulti(_014_tagcounts_reverse,$reversemulti($sortmulti(%_013_tagcounts%)))$set(_026_track_perc_required_70,$truncate($sub($mul(%totaltracks%,10),$mul(%totaltracks%,3)),-1))$set(_027_track_perc_flatten_70,\,$rreplace(%_014_tagcounts_reverse%,;\\s,\,))$set(_028_track_perc_list_70,\,001|\,002|\,003|\,004|\,005|\,006|\,007|\,008|\,009|\,010|\,011|\,012|\,013|\,014|\,015|\,016|\,017|\,018|\,019|\,020)$set(_029_track_perc_list_to_remove_70,$rreplace(%_028_track_perc_list_70%,\\|\,$rreplace($rreplace(%_026_track_perc_required_70%,^\(\\d\)\$,0\\1),^\(\\d\\d\)\$,0\\1).*,))$set(_030_track_perc_for_regex_70,$rreplace(%_029_track_perc_list_to_remove_70%,^\(.*\),\(\\1\)))$set(_031_track_perc_keep_70,$rreplace(%_027_track_perc_flatten_70%,%_030_track_perc_for_regex_70%,~))$set(_032_track_perc_keep_70,$rreplace(%_031_track_perc_keep_70%,~.*,))$set(_033_track_perc_keep_70,$rreplace(%_032_track_perc_keep_70%,\\d\\d\\d_,))$set(_033_track_perc_keep_70,$rreplace(%_033_track_perc_keep_70%,^\,,))$setmulti(_034_track_perc_result_70,$rreplace(%_033_track_perc_keep_70%,\,,; ))$setmulti(_035_track_perc_result_70,$replace(%_034_track_perc_result_70%,@, ))$setmulti(_criterion_result,%_035_track_perc_result_70%)$if(%_criterion_result%,,$set(_036_track_perc_required_30,$truncate($sub($mul(%totaltracks%,10),$mul(%totaltracks%,7)),-1)))$if(%_036_track_perc_required_30%,$set(_037_track_perc_flatten_30,\,$rreplace(%_014_tagcounts_reverse%,;\\s,\,))$set(_038_track_perc_list_30,\,001|\,002|\,003|\,004|\,005|\,006|\,007|\,008|\,009|\,010|\,011|\,012|\,013|\,014|\,015|\,016|\,017|\,018|\,019|\,020)$set(_039_track_perc_list_to_remove_30,$rreplace(%_038_track_perc_list_30%,\\|\,$rreplace($rreplace(%_036_track_perc_required_30%,^\(\\d\)\$,0\\1),^\(\\d\\d\)\$,0\\1).*,))$set(_040_track_perc_for_regex_30,$rreplace(%_039_track_perc_list_to_remove_30%,^\(.*\),\(\\1\)))$set(_041_track_perc_keep_30,$rreplace(%_037_track_perc_flatten_30%,%_040_track_perc_for_regex_30%,~))$set(_042_track_perc_keep_30,$rreplace(%_041_track_perc_keep_30%,~.*,))$set(_043_track_perc_keep_30,$rreplace(%_042_track_perc_keep_30%,\\d\\d\\d_,))$set(_043_track_perc_keep_30,$rreplace(%_043_track_perc_keep_30%,^\,,))$setmulti(_044_track_perc_result_30,$rreplace(%_043_track_perc_keep_30%,\,,; ))$setmulti(_045_track_perc_result_30,$replace(%_044_track_perc_result_30%,@, ))$setmulti(_criterion_result,%_045_track_perc_result_30%),)$if(%_criterion_result%,,$set(_046_all_more_than_once,$rreplace(%_027_track_perc_flatten_70%,\,001,~))$set(_047_all_more_than_once,$rreplace(%_046_all_more_than_once%,~.*,))$set(_048_all_more_than_once,$rreplace(%_047_all_more_than_once%,\\d\\d\\d_,))$set(_049_all_more_than_once,$rreplace(%_048_all_more_than_once%,^\,,))$setmulti(_050_all_more_than_once,$replace($replace(%_049_all_more_than_once%,@, ),\,,; ))$setmulti(_criterion_result,%_050_all_more_than_once%))$if($eq(%_026_track_perc_required_70%,1),$setmulti(_051_all_unique_genres,$replace(%_009_unique_genres%,@, )),)$if(%_051_all_unique_genres%,$setmulti(_criterion_result,%_051_all_unique_genres%),)$set(_000_push,@$replace(%_criterion_result%,; ,@)@)$set(_001_push,$rreplace(%_000_push%,\(^.*\(?=@Soul\)@?\)\(Soul\(?=@\)@\)\(.*\),\\1\\3\\2))$set(_001_push,$rreplace(%_001_push%,\(^.*\(?=@Electronic\)@?\)\(Electronic\(?=@\)@\)\(.*\),\\1\\3\\2))$set(_001_push,$rreplace(%_001_push%,\(^.*\(?=@Folk\)@?\)\(Folk\(?=@\)@\)\(.*\),\\1\\3\\2))$set(_001_push,$rreplace(%_001_push%,\(^.*\(?=@Latin\)@?\)\(Latin\(?=@\)@\)\(.*\),\\1\\3\\2))$set(_001_push,$rreplace(%_001_push%,\(^.*\(?=@Metal\)@?\)\(Metal\(?=@\)@\)\(.*\),\\1\\3\\2))$set(_001_push,$rreplace(%_001_push%,\(^.*\(?=@Jazz\)@?\)\(Jazz\(?=@\)@\)\(.*\),\\1\\3\\2))$set(_001_push,$rreplace(%_001_push%,\(^.*\(?=@Classical\)@?\)\(Classical\(?=?\)@\)\(.*\),\\1\\3\\2))$set(_001_push,$rreplace(%_001_push%,\(^.*\(?=@House\)@?\)\(House\(?=?\)@\)\(.*\),\\1\\3\\2))$set(_001_push,$rreplace(%_001_push%,\(^.*\(?=@Hip hop\)@?\)\(Hip hop\(?=@\)@\)\(.*\),\\1\\3\\2))$set(_001_push,$rreplace(%_001_push%,\(^.*\(?=@Rock\)@?\)\(Rock\(?=@\)@\)\(.*\),\\1\\3\\2))$set(_001_push,$rreplace(%_001_push%,\(^.*\(?=@Pop\)@?\)\(Pop\(?=@\)@\)\(.*\),\\1\\3\\2))$set(_002_push,$rreplace(%_001_push%,@{2\,},@))$set(_003_push,$rreplace(%_002_push%,^@,))$set(_004_push,$rreplace(%_003_push%,@\$,))$setmulti(album_genres,$replace(%_004_push%,@,; ))


P.S.
For the best and most complete results, consider this
 
Last Edit: October 30, 2024, 04:23:09 PM by hiccup

hiccup

  • Hero Member
  • *****
  • Posts: 9111
And as a free addition, here is a virtual tag that will display only the first three genres that are in the Album genre tag.
It can be used for displaying purposes where space is limited.

Album genres pruned
Code
$Replace($RxReplace(<Album genre>,"^(((;?[^;]+)?){3}).*","$1"),;," ·")

An example of how this can be used:



The virtual tag as it is will display (up till) the first three Album genres.
If you want to see a different number of genres, simply locate {3} in the formula and change that to another number.
Last Edit: August 17, 2024, 07:44:59 PM by hiccup

hiccup

  • Hero Member
  • *****
  • Posts: 9111
updated

genres such as Pop, Rock, House, Jazz, Electronic etc. that are more generic (and often misused) are now moved to the end of the list of genres.
   this makes sure that the list begins with more detailed and descriptive genres. In my opinion this has improved the results immensely.

I've created a virtual tag that will display only the first couple of genres of the full Album genre tag. (see post #2)
   This virtual tag is very useful for displaying purposes where space is limited.
   For filtering and playlists the full Album genre tag should still be used.
 

hiccup

  • Hero Member
  • *****
  • Posts: 9111

azcore

  • Newbie
  • *
  • Posts: 9
This is a really great idea!
I would like to try it out, but for some reason I don't see the second script of the two mentioned.

hiccup

  • Hero Member
  • *****
  • Posts: 9111
This is a really great idea!
I would like to try it out, but for some reason I don't see the second script of the two mentioned.
Welcome to the forum azcore

Somehow the first script was missing.
it's fixed now. Good luck ;-)

azcore

  • Newbie
  • *
  • Posts: 9
Thank you! It works great so far with a few exceptions.
I notice that sometimes the scripts are not idempotent (change the genre tag when running multiple times in a row) but not yet sure if it's something specific to my setup, will try to debug that.

hiccup

  • Hero Member
  • *****
  • Posts: 9111
Thank you! It works great so far with a few exceptions.
Do you mean you have run into album releases that don't get uniform album genres for all of the tracks?
If that is the case, can you provide some links to those releases?

Quote from: azcore
I notice that sometimes the scripts are not idempotent (change the genre tag when running multiple times in a row) but not yet sure if it's something specific to my setup, will try to debug that.
Making sure I understand what you mean:
Do you mean that after a release has already been matched in the right panel, but you then use the 'refresh' function?
I think I recall having noticed that sometimes when I constructed and tested the scripts, but I don't think there is anything I can do about that.
It's how the internals of Picard and the script functions that I am using in the script happen to work.
(it was already a p.i.t.a. finally getting this to work at all)
Last Edit: November 01, 2024, 08:24:14 AM by hiccup

azcore

  • Newbie
  • *
  • Posts: 9
Making sure I understand what you mean:
Do you mean that after a release has already been matched in the right panel, but you then use the 'refresh' function?
I think I recall having noticed that sometimes when I constructed and tested the scripts, but I don't think there is anything I can do about that.
It's how the internals of Picard and the script functions that I am using in the script happen to work.
(it was already a p.i.t.a. finally getting this to work at all)

Yes, exactly, after refreshing the results change sometimes (for some boxsets I have). That's the only issue and that's what I meant when I said that it works great with a few exceptions (meaning those boxsets). No big deal though, and it's really very useful, thank you again!

hiccup

  • Hero Member
  • *****
  • Posts: 9111
Yes, exactly, after refreshing the results change sometimes (for some boxsets I have). That's the only issue…
I am curious:
Why would you want to use 'refresh' after these releases (also boxsets) are matched in the right panel and have the correct album genre tags already showing?

azcore

  • Newbie
  • *
  • Posts: 9
Yes, exactly, after refreshing the results change sometimes (for some boxsets I have). That's the only issue…
I am curious:
Why would you want to use 'refresh' after these releases (also boxsets) are matched in the right panel and have the correct album genre tags already showing?

When using "Classical extras" pluging you have to refresh at least once (it produces a warning "No file with matching trackid - IF THERE SHOULD BE ONE, TRY 'REFRESH' - (unable to process any saved options, lyrics or 'keep' tags)" the first time). Also I quite often adjust my own scripts with custom renames etc. and test the changes that way.

hiccup

  • Hero Member
  • *****
  • Posts: 9111
When using "Classical extras" pluging you have to refresh at least once (it produces a warning "No file with matching trackid - IF THERE SHOULD BE ONE, TRY 'REFRESH' - (unable to process any saved options, lyrics or 'keep' tags)" the first time). Also I quite often adjust my own scripts with custom renames etc. and test the changes that way.
Thanks for explaining.

- If the Classical Extras plugin fails to produce correct results at the first matching run you could post a bug report in its thread on MusicBrainz forum.
   (but do the results of any relevant tag actually change when doing a refresh, or is it just the warning that disappears? I myself have always ignored these warnings and things seem to go well regardless)
- If you are refreshing for the purpose of testing scripts, I am guessing it is not an actual problem that my scripts present different results after such a refresh.

So for now I am assuming that my scripts are working well and as intended, and there is not much I could or need to do to accommodate for the above scenarios.

azcore

  • Newbie
  • *
  • Posts: 9

- If the Classical Extras plugin fails to produce correct results at the first matching run you could post a bug report in its thread on MusicBrainz forum.
   (but do the results of any relevant tag actually change when doing a refresh, or is it just the warning that disappears? I myself have always ignored these warnings and things seem to go well regardless)
- If you are refreshing for the purpose of testing scripts, I am guessing it is not an actual problem that my scripts present different results after such a refresh.

So for now I am assuming that my scripts are working well and as intended, and there is not much I could or need to do to accommodate for the above scenarios.

For Classical Extras it's just how the plugin works, it always happens when the files are tagged the first time and it's documented like that.
And I suspect that if the issue triggers on refresh, then it can also occur when retagging already tagged files anew (to accomodate some changes from MB database or retagging them with different options). But like I said, it's not a big deal in any case and I agree that it's something not worth fixing.