SongKong Jaikoz

SongKong and Jaikoz Music Tagger Community Forum

Noob help: Can i achieve my goals with SongKong and if so how?

Hi Paul. Yes that’s what it should have been. Good spot :joy:, sorry.

What release types can sk insert in the mask, such as ep, single, etc? Can it also add interview and live as well as bootleg?

Thank you.yes the audiobook naming requires the extra sub folder because i thought some audiobooks could potentially be quite long in name so this helps reduce the filename.

So the release type usually comes from MusicBrainz, and within MusicBrainz you have a mandatory primary type:

  • Album
  • Single
  • EP
  • Broadcast
  • Other

and then optionally multiple or none secondary types, these can be

  • Compilation
  • Soundtrack
  • Spokenword
  • Interview
  • Audiobook
  • Audio Drama
  • Live
  • Remix
  • DJ Mix
  • Mixtape
  • Demo
  • Field Recording

Both are stored in the mbreleasetype field, primary type always comes first, some example values are

  • Album
  • EP
  • Album; Live
  • Album; Compilation; Live
  • Single; Remix
  • Album, Audiobook

So the releasetype logic I have written just looks at the primary type and displays full type if not primary type album. So at the moment it would only show live if the primary type was not album (e.g Single; Live)

1 Like

I cannot see this extra subfolder you mention?

sorry. they are present in the required mask template but i didn’t reproduce them correctly in the examples i gave so they should be:

STANDARD AUDIOBOOK (NOT a ‘compilation Audiobook’)
/Organised/Audiobooks/{Album} ({YearOnly}) by {AlbumArtist}/<IF multidisc THEN “CD{DiscNumber }><IF subtitle THEN “ - Subtitle“>/{TrackNumber} - {Title}

eg:
/Organised/Audiobooks/Backwards (1990) by Doug Naylor/CD01 - Blue Disc/ 01 - Chapter 1

COMPILATION AUDIOBOOK (Audiobook created by Various Artists)
/Organised/Audiobooks/{Album} ({YearOnly}) by Various Artists/<IF multidisc THEN “[CD{DiscNumber }><IF subtitle THEN “ - Subtitle] “>/{TrackNumber} - {Title} [{Artist} feat. {FeaturedArtists}]

eg:
/Organised/Audiobooks/NOW 82 (1985) by Various Artists/CD01 - Blue Disc/01 - Chapter 1 [Craig Charles feat. Xxx]

and i took the square brackets out because they aren’t really needed visually to help break things up because they are in their own subfolder.

ok, so is it not possible to have just a live release because its not part of the primary type?

I’m trying to understand how sk could be scripted to make use of these types to better inform my library at a glance and when seeing the metadata that my music player displays initially (i use plexamp). does it sound like the logic i wanted for my renaming masks is possible?

Yes, you can certainly do it, the difficulty when you want a complex mask is you need to understand the data well and work out exactly what you require.

So for example do you want returned if mbreleasetype contains Album; Live I imagine just (Live), but what about Single; Live I imagine (Single, Live) but I don’t know, and what about Interview or Bootleg variations ?

So Ive updated the function for Live

function formatReleaseType()
{
   if(mbreleasetype.toLowerCase().contains("live") && mbreleasetype.toLowerCase().contains("album")) return brackets('Live') + ' ';
   if(mbreleasetype.toLowerCase().contains("album")) return "";
   return brackets(mbreleasetype) + ' ';
}

If you want to make it more complex it should be fairly easy for you to decide on the rules and then write them in the function.

But maybe you are now where Rename masks are good enough and you can try them out, and then just make minor adjustments when you see things you dont like and simply rerun.

1 Like

Okay updated masks are

Rename mask

function ignoreThe(name)
{
   if(name.startsWith('The ')) return name.substring(4);
   return name;
}

function brackets(name)
{
   if(name.length>0) return '(' + name + ')';
   return '';
}

function squareBrackets(name)
{
   if(name.length>0) return '[' + name + ']';
   return '';
}

function yearOnly(year)
{
   if(year.length>4) return year.substring(0,4);
   return year;
}

function r(value)
{
   return value.replaceAll("\\?","");
}

function formatSubtitle(subtitle)
{
    if(subtitle.length>0) return ' - ' + subtitle;
    return "";
}

function formatTrackArtist()
{
     featuredartists="";
     for(i=0; i<artists_index.length;i++)
     {
        if(!albumartists.contains(artists_index[i]))
        {
            featuredartists+=artists_index[i] + ", ";
        }
     }
     if(featuredartists.length>0)
     {
         return 'feat. ' + substring(featuredartists, featuredartists.length -2);
     }
     return ""; 
}

function formatReleaseType()
{
   if(mbreleasetype.toLowerCase().contains("live") && mbreleasetype.toLowerCase().contains("album")) return brackets('Live') + ' ';
   if(mbreleasetype.toLowerCase().contains("album")) return "";
   return brackets(mbreleasetype) + ' ';
}

function writeArtist(type)
{
   return r(type
    + "/"
    + ifnotempty2(ignoreThe(albumartist).substring(0,1),ignoreThe(artist).substring(0,1),'/')
    + ifnotempty2(albumartist,artist,'/')
    + formatReleaseType()
    + ifnotempty(album + ' ' + brackets(yearOnly(year)),'/')
    + ifmultidisc(squareBrackets('CD' + pad(discno,2) +  formatSubtitle(subtitle)) + ' ')
    + ifnotempty(pad(trackno,2),' - ')
    + ifnotempty(title,' ') 
    + squareBrackets(formatTrackArtist()))
}

function writeSoundtrack(type)
{
   return r(type
    + "/"
    + ifnotempty(album + ' ' + brackets(yearOnly(year)) + " by " + albumartist,'/')
    + ifmultidisc(squareBrackets('CD' + pad(discno,2) +  formatSubtitle(subtitle))+ ' ')
    + ifnotempty(pad(trackno,2),' - ')
    + ifnotempty(title,' ')
    + squareBrackets(formatTrackArtist()))
}

function writeAudiobook(type)
{
   return r(type
    + "/"
    + ifnotempty(album + ' ' + brackets(yearOnly(year)) + " by " + albumartist,'/')
    + ifmultidisc('CD' + pad(discno,2) + formatSubtitle(subtitle)+ '/')
    + ifnotempty(pad(trackno,2),' - ')
    + ifnotempty(title,' '))
}

if(mbreleasetype.contains("Soundtrack"))
{
    writeSoundtrack("Soundtracks");
}
else if(mbreleasetype.contains("Audiobook"))
{
    writeAudiobook("Audiobooks");
}
else
{
    writeArtist("Artists");
}

Compilation Rename Mask

function ignoreThe(name)
{
   if(name.startsWith('The ')) return name.substring(4);
   return name;
}

function brackets(name)
{
   if(name.length>0) return '(' + name + ')';
   return '';
}

function squareBrackets(name)
{
   if(name.length>0) return '[' + name + ']';
   return '';
}

function yearOnly(year)
{
   if(year.length>4) return year.substring(0,4);
   return year;
}

function r(value)
{
   return value.replaceAll("\\?","");
}

function formatSubtitle(subtitle)
{
    if(subtitle.length>0) return ' - ' + subtitle;
    return "";
}


function formatTrackArtist()
{
     mainartist=artists_index[0];
     featuredartists="";
     for(i=1; i<artists_index.length;i++)
     {
        if(!albumartists.contains(artists_index[i]))
        {
            featuredartists+=artists_index[i] + ", ";
        }
     }
     if(featuredartists.length>0)
     {
         return mainartist + ' feat. ' + substring(featuredartists, featuredartists.length -2);
     }
     return mainartist; 
}

function writeBody(subFolder)
{
  return r(subFolder + "/"
  + ifnotempty(album + ' ' + brackets(yearOnly(year)) + " by " + albumartist,'/')
  + ifmultidisc(squareBrackets('CD' + pad(discno,2) +  formatSubtitle(subtitle))+ ' ')
  + ifnotempty(pad(trackno,2),' - ')
  + ifnotempty(title,' ')
  + squareBrackets(formatTrackArtist()))
}

function writeAudiobooks(subFolder)
{
  return r(subFolder + "/"
  + ifnotempty(album + ' ' + brackets(yearOnly(year)) + " by " + albumartist,'/')
  + ifmultidisc('CD' + pad(discno,2) +  formatSubtitle(subtitle)+ '/')
  + ifnotempty(pad(trackno,2),' - ')
  + ifnotempty(title,' ')
  + squareBrackets(formatTrackArtist()))
}

if(mbreleasetype.contains("Soundtrack"))
{
  writeBody("Soundtracks")
}
else if(mbreleasetype.contains("Audiobook"))
{
  writeAudiobooks("Audiobooks")
}
else
{
  writeBody("Compilations");
}

So would it be valid to add something like this as an extra line between the existing lines 1 and 2 like this?:

if(mbreleasetype.toLowerCase().contains(“bootleg”) && mbreleasetype.toLowerCase().contains(“album”)) return brackets(‘Bootleg’) + ’ ';

And

if(mbreleasetype.toLowerCase().contains(“interview”) && mbreleasetype.toLowerCase().contains(“album”)) return brackets(‘Interview’) + ’ ';

Yes you got it, as long as you use the regular double quote and apostophe easily available on the keyboard rather than the fancy unicode ones that you used above in your example

e.g

if(mbreleasetype.toLowerCase().contains("bootleg") && mbreleasetype.toLowerCase().contains("album")) return brackets('Bootleg') + ' ';

1 Like

Can i ask, how this field is output, how would i need to format them when including them in the naming masks?
eg; “single; live”. i can’t remember if “;” is an illegal character on some systems? is it better to change the “;” to a “-” or “,” for example?

because some ‘types’ are primary or secondary, etc does this affect how i need to deal with them when adding in the masks? (any logic i need to observe?)

i adjusted the mask like this (so far):

function ignoreThe(name)
{
   if(name.startsWith('The ')) return name.substring(4);
   return name;
}

function brackets(name)
{
   if(name.length>0) return '(' + name + ')';
   return '';
}

function squareBrackets(name)
{
   if(name.length>0) return '[' + name + ']';
   return '';
}

function yearOnly(year)
{
   if(year.length>4) return year.substring(0,4);
   return year;
}

function r(value)
{
   return value.replaceAll("\\?","");
}

function formatSubtitle(subtitle)
{
    if(subtitle.length>0) return ' - ' + subtitle;
    return "";
}

function formatTrackArtist()
{
     featuredartists="";
     for(i=0; i<artists_index.length;i++)
     {
        if(!albumartists.contains(artists_index[i]))
        {
            featuredartists+=artists_index[i] + ", ";
        }
     }
     if(featuredartists.length>0)
     {
         return 'feat. ' + substring(featuredartists, featuredartists.length -2);
     }
     return ""; 
}

function formatReleaseType()
{
   if(mbreleasetype.toLowerCase().contains("live") && mbreleasetype.toLowerCase().contains("album")) return brackets('Live') + ' ';
   if(mbreleasetype.toLowerCase().contains("bootleg") && mbreleasetype.toLowerCase().contains("album")) return brackets('Bootleg') + ' ';
   if(mbreleasetype.toLowerCase().contains("interview") && mbreleasetype.toLowerCase().contains("album")) return brackets('Interview') + ' ';
   if(mbreleasetype.toLowerCase().contains("album")) return "";
   return brackets(mbreleasetype) + ' ';
}

function writeArtist(type)
{
   return r(type
    + "/"
    + ifnotempty2(ignoreThe(albumartist).substring(0,1),ignoreThe(artist).substring(0,1),'/')
    + ifnotempty2(albumartist,artist,'/')
    + formatReleaseType()
    + ifnotempty(album + ' ' + brackets(yearOnly(year)),'/')
    + ifmultidisc(squareBrackets('CD' + pad(discno,2) +  formatSubtitle(subtitle)) + ' ')
    + ifnotempty(pad(trackno,2),' - ')
    + ifnotempty(title,' ') 
    + squareBrackets(formatTrackArtist()))
}

function writeSoundtrack(type)
{
   return r(type
    + "/"
    + ifnotempty(album + ' ' + brackets(yearOnly(year)) + " by " + albumartist,'/')
    + ifmultidisc(squareBrackets('CD' + pad(discno,2) +  formatSubtitle(subtitle))+ ' ')
    + ifnotempty(pad(trackno,2),' - ')
    + ifnotempty(title,' ')
    + squareBrackets(formatTrackArtist()))
}

function writeAudiobook(type)
{
   return r(type
    + "/"
    + ifnotempty(album + ' ' + brackets(yearOnly(year)) + " by " + albumartist,'/')
    + ifmultidisc('CD' + pad(discno,2) + formatSubtitle(subtitle)+ '/')
    + ifnotempty(pad(trackno,2),' - ')
    + ifnotempty(title,' '))
}

if(mbreleasetype.contains("Soundtrack"))
{
    writeSoundtrack("Soundtracks");
}
else if(mbreleasetype.contains("Audiobook"))
{
    writeAudiobook("Audiobooks");
}
else
{
    writeArtist("Artists");
}

could i do this and it would work?:

   if(mbreleasetype.toLowerCase().contains("live") && mbreleasetype.toLowerCase().contains("album")) return brackets('Live') + ' ';
   if(mbreleasetype.toLowerCase().contains("bootleg") && mbreleasetype.toLowerCase().contains("album")) return brackets('Bootleg') + ' ';
   if(mbreleasetype.toLowerCase().contains("interview") && mbreleasetype.toLowerCase().contains("album")) return brackets('Interview') + ' ';
   if(mbreleasetype.toLowerCase().contains("broadcast") && mbreleasetype.toLowerCase().contains("album")) return brackets('Broadcast') + ' ';
   if(mbreleasetype.toLowerCase().contains("spokenword") && mbreleasetype.toLowerCase().contains("album")) return brackets('Spokenword') + ' ';
   if(mbreleasetype.toLowerCase().contains("audio drama") && mbreleasetype.toLowerCase().contains("album")) return brackets('Audio Drama') + ' ';
   if(mbreleasetype.toLowerCase().contains("remix") && mbreleasetype.toLowerCase().contains("album")) return brackets('Remix') + ' ';
   if(mbreleasetype.toLowerCase().contains("dj mix") && mbreleasetype.toLowerCase().contains("album")) return brackets('DJ Mix') + ' ';
   if(mbreleasetype.toLowerCase().contains("mixtape") && mbreleasetype.toLowerCase().contains("album")) return brackets('Mixtape') + ' ';
   if(mbreleasetype.toLowerCase().contains("demo") && mbreleasetype.toLowerCase().contains("album")) return brackets('Demo') + ' ';
   if(mbreleasetype.toLowerCase().contains("album")) return "";
   return brackets(mbreleasetype) + ' ';

and this for compilations?:

function ignoreThe(name)
{
   if(name.startsWith('The ')) return name.substring(4);
   return name;
}

function brackets(name)
{
   if(name.length>0) return '(' + name + ')';
   return '';
}

function squareBrackets(name)
{
   if(name.length>0) return '[' + name + ']';
   return '';
}

function yearOnly(year)
{
   if(year.length>4) return year.substring(0,4);
   return year;
}

function r(value)
{
   return value.replaceAll("\\?","");
}

function formatSubtitle(subtitle)
{
    if(subtitle.length>0) return ' - ' + subtitle;
    return "";
}

function formatReleaseType()
{
   if(mbreleasetype.toLowerCase().contains("live") && mbreleasetype.toLowerCase().contains("compilation")) return brackets('Live') + ' ';
   if(mbreleasetype.toLowerCase().contains("bootleg") && mbreleasetype.toLowerCase().contains("compilation")) return brackets('Bootleg') + ' ';
   if(mbreleasetype.toLowerCase().contains("interview") && mbreleasetype.toLowerCase().contains("compilation")) return brackets('Interview') + ' ';
   if(mbreleasetype.toLowerCase().contains("broadcast") && mbreleasetype.toLowerCase().contains("compilation")) return brackets('Broadcast') + ' ';
   if(mbreleasetype.toLowerCase().contains("spokenword") && mbreleasetype.toLowerCase().contains("compilation")) return brackets('Spokenword') + ' ';
   if(mbreleasetype.toLowerCase().contains("audio drama") && mbreleasetype.toLowerCase().contains("compilation")) return brackets('Audio Drama') + ' ';
   if(mbreleasetype.toLowerCase().contains("remix") && mbreleasetype.toLowerCase().contains("compilation")) return brackets('Remix') + ' ';
   if(mbreleasetype.toLowerCase().contains("dj mix") && mbreleasetype.toLowerCase().contains("compilation")) return brackets('DJ Mix') + ' ';
   if(mbreleasetype.toLowerCase().contains("mixtape") && mbreleasetype.toLowerCase().contains("compilation")) return brackets('Mixtape') + ' ';
   if(mbreleasetype.toLowerCase().contains("demo") && mbreleasetype.toLowerCase().contains("compilation")) return brackets('Demo') + ' ';
   if(mbreleasetype.toLowerCase().contains("compilation")) return "";
   return brackets(mbreleasetype) + ' ';
}

function formatTrackArtist()
{
     mainartist=artists_index[0];
     featuredartists="";
     for(i=1; i<artists_index.length;i++)
     {
        if(!albumartists.contains(artists_index[i]))
        {
            featuredartists+=artists_index[i] + ", ";
        }
     }
     if(featuredartists.length>0)
     {
         return mainartist + ' feat. ' + substring(featuredartists, featuredartists.length -2);
     }
     return mainartist; 
}

function writeBody(subFolder)
{
  return r(subFolder + "/"
  + ifnotempty(album + ' ' + brackets(yearOnly(year)) + " by " + albumartist,'/')
  + ifmultidisc(squareBrackets('CD' + pad(discno,2) +  formatSubtitle(subtitle))+ ' ')
  + ifnotempty(pad(trackno,2),' - ')
  + ifnotempty(title,' ')
  + squareBrackets(formatTrackArtist()))
}

function writeAudiobooks(subFolder)
{
  return r(subFolder + "/"
  + ifnotempty(album + ' ' + brackets(yearOnly(year)) + " by " + albumartist,'/')
  + ifmultidisc('CD' + pad(discno,2) +  formatSubtitle(subtitle)+ '/')
  + ifnotempty(pad(trackno,2),' - ')
  + ifnotempty(title,' ')
  + squareBrackets(formatTrackArtist()))
}

if(mbreleasetype.contains("Soundtrack"))
{
  writeBody("Soundtracks")
}
else if(mbreleasetype.contains("Audiobook"))
{
  writeAudiobooks("Audiobooks")
}
else
{
  writeBody("Compilations");
}

i’m also trying to remember, theres a setting to include release type right? but then we are also adding them with the mask. i can’t remember why though, is it to have more granular control? and should i turn off the setting for this if so?

Yes the rename mask looks fine.

Compilation Mask is okay except the formatReleaseType() function is never called because if you look at your example masks you see there wasn’t a requirement to show release type for compilations.

Apologies, I have just found a bug

SongKong adds multiple types as seperate values to the ReleaseType field.

e.g

  • Album
  • Live

but RenameMasks and Scripter think it is added as single value

e.g
Album; Live

So the example files show Album; Compilation when it should be Album;;;Compilation and the Script Fields 1st Value and 2nd Value buttons are incorrectly disabled, and therefore formatReleaseType() function is not correct

I have raised a bug and will prioritize this once I have finished the issue I am currently working on

Yes, you mean the Fix Songs:Album Format:Add EP, Single, Compilation, Live and Remix release types to release title option. So this adds release type to the album field, and then if the album field is used in your rename mask you would see release types as part of the filename.

So this is a simpler solution to using the mbreleasetype field directly in the mask but I dont think it offers the control you require, for example it doesnt do anything with some secondary type such as Interview it only deals with Compilation, Live, Remix and Soundtrack,

Aso it only adds primary types EP and Single, it would not add Other or Audiobook to the album title

Fixed issue now, and turns out the formatReleaseType() function does continue to work fine after all as it is, also ran your masks on some test folders and could not see any issues. Will do a new release soon either the end of this week or the start of next week.

Fixes now released as part of 11.6.1