Help with folder structure

The code in line with text is from surrounding it with backticks (the ` which is under the ~ on at least US keyboards) – it looks like you’ve figured out the four-spaces-at-the-front for doing the entire line, which automatically joins multiple lines into larger blocks. How’d you get the colors on yours, though? That’s something I don’t know. And sorry about the two dashes; I meant for them to just be an en-dash separating the second part of the sentence.

Not sure why the Compilations folder has disappeared – that (with no ‘s’) was actually what most of my tests wound up being sorted with – but the second soundtrack looks like it might be due to the album artist having been overridden; it’s in the same “slot” as the artists for the other release types. Do both artist fields show up correctly in the bottom pane of Picard?

EDIT: There’s a chance that has something to do with MagicScript, since it doesn’t include artists in the soundtrack hierarchy, but it doesn’t save anything over the tags so unless you have something in your file namer that doesn’t show up in your original post, I’m not sure why it would affect anything

And definitely happy to explain it! One resource I’ve found invaluable, if you haven’t seen it already, is the scripting documentation page. Not necessarily a fun read, but it’s really nice to take twenty minutes and just figure out everything that’s possible.

$if2(%_secondaryreleasetype%,studio)

%_secondaryreleasetype% unsurprisingly contains a list of all the secondary types on the release group, in lowercase. You can find a list of everything you can use on the tags page, but my favorite way of finding something helpful is using the “View script variables” plugin. Anyway, when this list variable’s used normally (as in, not through one of the $...multi functions), the types are separated by a semicolon and a space; this will come into play later. For now, all we care about is if the release group has any of them set, so we use an $if2(...) to select the entire list if it’s not empty, or the text “studio” if it is. The function can take pretty much as many comma-separated arguments as you want to give it, and returns the first one that’s not empty.

$upper($firstalphachar([...]))

From that result, take only the first character and turn it into uppercase. This would normally cause unexpected behaviour if the first character wasn’t a letter, but since none of the secondary types use anything but letters in their name, we don’t have to worry about it. As a note, the first “Albums” you added to your line (and, in fact, everything after the s) doesn’t do anything because $firstalphachar drops it. That’s also why I had studio in all lowercase – it’s automatically capitalized, so if I match what’s in %_secondaryreleasetype, it’s less likely to do anything weird if I edit the script in the future.

$rreplace([...],.\([^;]*\).*,\​\1)/

(Don’t copy that, there’s a zero-width space between the two backslashes.) This, admittedly, looks a bit like magic. $rreplace([...]) runs a regex on some text, and those are rather infamously difficult to read. It isn’t helped at all by the fact that Picard parses the script before the regex is run, so there’s more backslashes in there than normal (for a digression on this, check out this page and look for sections talking about the title text). At a high level, the expression between the commas is matched to parts of the text before the first comma, and those matches are replaced with the value after the second comma. To take it an element at a time:

  • . - This matches any single character. Since regexes are greedy (matching at the first possible point and not going to the next part of the expression until they have to), this will always be the first character in the string, If we were using something else or wanted to be particularly careful, we would put a ^ at the beginning of the expression, which only matches the beginning of the string. The . is in here because we already have the first character from the $upper([...]) expression.

  • \( - I’ll explain this later, but it’s escaped (the backslash) because a plain ( has a special meaning in the script that we don’t want to invoke.

  • [^;] - Square brackets usually mean “match any of these characters”, but the ^ being right after the [ makes it “match everything except these characters” (as opposed to what ^ means at the start of a regex). Therefore, this matches any (single) character that’s not a semicolon – what separates the elements of the list, remember.

  • * - This takes the last element (in this case, the [^;]) and matches it any number of times, zero to theoretical infinity. Again, regexes are greedy, so if we want it to stop at some point before the end of the line, we have to tell it when to stop before the *: in this case, when it hits the first semicolon (if there’s no semicolon in the string, it just takes everything until the end of the input). This will then be “the first element of the list, ignoring the first character” since that was already matched.

  • \) - Again, escaped due to having another meaning; the pair would typically be read ([^;]*). Doesn’t match anything on its own, but saves everything between the parentheses into memory to be referred back to later.

  • .* - I assume you’ve been paying attention and can figure this out from the above, but for completeness, match any number of any character; basically, “match the entire rest of the line”. Like with ^, if we weren’t using such a broad wildcard or we wanted to be careful, we could add a $ to the end to be sure we match the end of the line, but it’s not necessary after .*.

  • \​\1 - (Like above, retype this, don’t copy it.) This is how we retrieve the value saved by ( and ) – or, more generally, \1, but the backslash also needs to be escaped here. Since this is after the second comma, we’ve switched from matching text to replacing whatever was matched. Because we made sure to get the entire line, this takes whatever we gave as input and replaces it with the first saved group, essentially discarding the first character, the first semicolon, and everything after that.

    $rreplace([…]) Albums/
    If I may make another suggestion, this is what I’d do to add the “Albums” part of the name, as long as you don’t mind “Compilation Albums” and “Soundtrack Albums”. You get it applied across the board, and don’t run into “Studio Albums” behaving differently than everything else. If you do want folders without the second word, you can always do something like $if($ne($left(%_secondaryreleasetype%,10),soundtrack), Albums,) to only add it if the first 10 characters of the first release type in the list don’t match “soundtrack” (or, more correctly, to add an empty string if they do), and if you’re being lazy, you can even replace 10 with $len(soundtrack) to do the counting for you.

1 Like