Skip to main content

Notice

Please note that most of the software linked on this forum is likely to be safe to use. If you are unsure, feel free to ask in the relevant topics, or send a private message to an administrator or moderator. To help curb the problems of false positives, or in the event that you do find actual malware, you can contribute through the article linked here.
Topic: Multi-formatting Tech Demo (Read 8793 times) previous topic - next topic
0 Members and 1 Guest are viewing this topic.

Multi-formatting Tech Demo

I have found a way to handle Album List style %<field>% expression in title formatting scripts but without the restrictions of the post-processing approach used in the Album List component. In this topic I want to present my approach to fellow developers. Nonetheless, user comments are also welcome.

Introduction

If you are not familiar with Album List, here is a short introduction what this is all about. Otherwise you can skip this section. In some use cases (as in Album List) you want multiple results from a title formatting script if there are multi-value fields. For example, to list a song under each of its artists in Album List, you would use a format like this:
Code: [Select]
%<artist>%|%title%

In Album List the %<artist>% field will be expanded to a list of the song's artists which might look like
Quote
(Calexico,Iron and Wine)|Red Dust
with the characters delimiting the list highlighted in red. Note that the list encoding scheme here is used only for illustration and is different from the one actually used in Album List. In a post-processing step this single string will be expanded to a list of string:
Quote
Calexico|Red Dust
Iron and Wine|Red Dust

By interpreting each of the strings as a path in a hierarchical structure Album List would derive the following tree view:
Code: [Select]
+ Calexico
| + Red Dust
+ Iron and Wine
  + Red Dust

Suppose we wanted to group artists by their first letter, i.e.
Code: [Select]
+ C
| + Calexico
|   + Red Dust
+ I
  + Iron and Wine
    + Red Dust

In Album List we cannot do this because the $left() function - like all the other functions - is not aware of the list encoding. This is where my new approach comes in to play.

Multi-formatting algorithm

The multi-formatting algorithm is based on the idea that we only process one of the values of a multi-value field at a time. If %<artist>% returns only one artist without decorating it with additional list encoding characters, we can use any title formatting function on the result, even control flow functions like $if(). If there are multiple artist values, we just to run the title formatting script multiple times, once for each value. Sounds simple enough, right?

The foo_multiformat component implements this approach in the branch_formatter class. This class coordinates the repeated execution of a given title formatting script. It uses some helper classes for keeping track of the encountered multi-value fields and the remaining values. In essence, the code does a depth-first search through a tree where each inner node of the tree is a branch point created by %<field>%, $meta_branch_remap(field) or $meta_branch(field).

So what are the restrictions? The depth-first search requires a predictable behaviour of all used titleformat_hook implementations, so it can re-create the state corresponding to the last branch. In particular this means:
  • %field% expressions must evaluate to the same value during all the executions done for a single track.
  • Functions $f() must return evaluate to the same value during all the executions done for a single track, if they are given the same arguments.

The implementation contains safety checks to detect inconsistencies between the current execution state and its log. Nothing bad (like crashes) should happen in the case of inconsistencies but the tree of possible value combinations may not be fully explored.

Documentation

The component adds a context menu command called "Multi-formatting Tech Demo". This command opens a window where title formatting scripts can be entered and evaluated. The window displays the results of the script in the lower part.

The component supports the following special fields and functions.
  • %<field>%: Evaluates to a value of the remapped tag FIELD. Multiple occurrences of %<field>% evaluate to the same value and do not create additional branches.
  • $meta_branch_remap(field): This is equivalent to %<field>% in every aspect, including that all occurrences of %<field>$ and $meta_branch_remap(field) evaluate to the same value.
  • $meta_branch(field): Evaluates to a value of the (non-remapped) tag FIELD. Multiple occurrences of $meta_branch(field) evaluate to the same value and do not create additional branches. However, $meta_branch_remap(field1) or %<field1>% and $meta_branch(field2) create different branches even if FIELD1 is identical to or remapped to FIELD2.

The following functions are provided to explore the limitations of the multi-formatting approach.
  • %counter% and %unsafe_counter%: These represent custom fields that do not obey the rules required for consistency. %counter% returns the number of times the field has been evaluated for a particular track. %unsafe_counter% behaves like %counter% but they have independent values. %counter% is given special treatment by the branch_formatter to enforce the consistency rules while %unsafe_counter% is not.
  • $random(n) and $unsafe_random(n): These represent custom functions that do not obey the rules required for consistency. They return an integer x in the range 0 <= x < n. $random() is given special treatment by the branch_formatter to enforce the consistency rules while $unsafe_random() is not.

Examples:
  • "$upper($left(%<artist>%,1))|%<artist>%|%title%"
  • "%counter%/%unsafe_counter%-%<artist>%-%counter%/%unsafe_counter%"
  • "$ifgreater($unsafe_random(2),0,%<artist>%,%<genre>%)|%title%"

I'll add further documentation as the need arises.

Links

Multi-formatting Tech Demo

Reply #1
It would be awesome if Peter could comment on the plausibility of this being implemented.

Mad props for the work, foosion.

 

Multi-formatting Tech Demo

Reply #2
I agree. This is great stuff, and so it would be great if were able to be folded into the core or at least DUI. Thanks, foosion!

Multi-formatting Tech Demo

Reply #3
So, your approach would limit each tag to single a single branch? I.e. each subsequent %<tag>% would return the same single value?
I don't know what possible use there is to having multiple branches of the same tag, but I would still like to have the option.

The Album List titleformat wiki page says at the very bottom
"The value of a branching expression can be stored in variables and retrieved without loss." I don't think that is true.
It would seem to me that currently the branch value is not stored but the branching operation itself is. (If I am wrong, please point me to the correct usage.)
I'd prefer if one could use variables to "unbranch" or pin down the value, e.g. $puts(constant,%<tag>%), so that the variable value $get(constant) could then be used safely for e.g. string operations.

Using this approach, your first example should then be written something like "$upper($left($put(artist,%<artist>%),1))|$get(artist)|%title%"
whereas "$upper($left(%<artist>%,1))|%<artist>%|%title%" would still have two %artist% branches.
Apart from some possible nasty surprises for people who'd expect the variables to still iterate all the values, I don't think any functionality would be lost that way.

Multi-formatting Tech Demo

Reply #4
So, your approach would limit each tag to single a single branch? I.e. each subsequent %<tag>% would return the same single value?
Correct, although I don't see it as a limitation. In title formatting, you generally get the same value of all occurrences of %tag%. My approach preserves that property for %<tag>%.

I don't know what possible use there is to having multiple branches of the same tag, but I would still like to have the option.
Technically, it would be an easy change to my code and I thought about which uses it might have. I could only come up with two kinds of use cases:
  • Artificial examples which hardly anyone would use.
  • Elaborate use cases which would require additional features bolted onto title formatting to achieve satisfying results.
I then chose to go with the current approach for two reasons. Firstly, the performance of my approach would deteriorate in both cases. Secondly, as far as I know there are third-party components which provide greater control over the tree construction by using a different language. They thereby avoid the limitations of title formatting entirely.

The Album List titleformat wiki page says at the very bottom
"The value of a branching expression can be stored in variables and retrieved without loss." I don't think that is true.
It would seem to me that currently the branch value is not stored but the branching operation itself is. (If I am wrong, please point me to the correct usage.)
Yes, that is one way to visualise it.

I'd prefer if one could use variables to "unbranch" or pin down the value, e.g. $puts(constant,%<tag>%), so that the variable value $get(constant) could then be used safely for e.g. string operations.
I prefer to not use variables in title formatting because I find all the $puts and $get rather cumbersome. So I tried to optimise the most common use cases. Consider the following lines of code, starting with a version without branching:
Code: [Select]
$upper($left(%artist%,1))|%artist%|%title%
$upper($left(%<artist>%,1))|%<artist>%|%title%
$upper($left($put(artist,%<artist>%),1))|$get(artist)|%title%
There is also a usability perspective here. What does $get(artist) do? You won't know until you find the corresponding $put or $puts. What does %<artist>% do? Always the same thing, it returns the current artist value.

If needed, it would be easy to add a $meta_branch_ex(tag) function which would branch each time it is called.