Skip to content

Examples

Here are a few examples of working with the spiff files from mbzlists.

Playing playlists

Most music players can safely ignore the annotations from the spiff file and just focus on the <tracklist> element. To be truly portable, mbzlists identifies tracks using Musicbrainz IDs. Here is how each <track> element looks like:

<track>
  <title>In Your Room</title>
  <creator>Airiel</creator>
  <album>Winks &amp; Kisses: 20th Anniversary Deluxe Edition</album>
  <info>https://musicbrainz.org/recording/7b54cad3-7542-4edf-8d0e-fd423c9b8166</info>
  <location>https://musicbrainz.org/recording/7b54cad3-7542-4edf-8d0e-fd423c9b8166</location>
</track>

To import or play, a player needs to be able to resolve this info to an available song. To do this for YouTube, you can directly use the Play button on the web app. For Spotify and other systems like Subsonic compatible players, checkout the resolver hosted here.

Generating static HTML playlists

Even if you self host mbzlists, you might not want to run a server just to share read-only playlists. With the mbzlists format it's easy to go from XSPF files to custom HTMLs using an XSLT based transformation. Here is an example XSL:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns:mbzlists="http://docs.lepisma.xyz/mbzlists/ns/1.0/"
                xmlns:xspf="http://xspf.org/ns/0"
                exclude-result-prefixes="mbzlists xspf xs"
                version="2.0">

  <xsl:output method="html" indent="yes" encoding="UTF-8"/>

  <xsl:template match="/">
    <html>
      <head>
        <title><xsl:value-of select="/xspf:playlist/xspf:title"/></title>
        <link rel="stylesheet" href="https://unpkg.com/sakura.css/css/sakura.css" type="text/css"/>
      </head>
      <body>
        <h1><xsl:value-of select="/xspf:playlist/xspf:title"/></h1>
        <p><strong>Creator: </strong> <a href="{/xspf:playlist/xspf:info}"><xsl:value-of select="/xspf:playlist/xspf:creator"/></a></p>
        <p><strong>Date: </strong>
          <xsl:value-of select="format-dateTime(xs:dateTime(//xspf:date), '[MNn] [D], [Y] [H01]:[m01] [PN]')"/>
        </p>
        <xsl:apply-templates select="//mbzlists:blocks/*"/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="mbzlists:paragraph">
    <p>
      <xsl:value-of select="." disable-output-escaping="yes"/>
    </p>
  </xsl:template>

  <xsl:template match="mbzlists:header">
    <xsl:element name="h{ @level }">
      <xsl:value-of select="."/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="mbzlists:mbrecording">
    <div>
      <p>
        🎵 
        <strong>
          <a href="https://musicbrainz.org/recording/{@mbid}">
            <xsl:value-of select="mbzlists:title"/>
          </a>
        </strong>
        by 
        <a href="https://musicbrainz.org/artist/{mbzlists:artist/@mbid}">
          <xsl:value-of select="mbzlists:artist"/>
        </a>
        — 
        <em>
          <a href="https://musicbrainz.org/release/{mbzlists:release/@mbid}">
            <xsl:value-of select="mbzlists:release"/>
          </a>
        </em>
      </p>
    </div>
  </xsl:template>

  <xsl:template match="mbzlists:list">
    <xsl:choose>
      <xsl:when test="@style='ordered'">
        <ol>
          <xsl:apply-templates select="mbzlists:listItem"/>
        </ol>
      </xsl:when>
      <xsl:when test="@style='unordered'">
        <ul>
          <xsl:apply-templates select="mbzlists:listItem"/>
        </ul>
      </xsl:when>
      <xsl:when test="@style='checklist'">
        <ul>
          <xsl:for-each select="mbzlists:listItem">
            <li>
              <input type="checkbox">
                <xsl:if test="@checked='true'">
                  <xsl:attribute name="checked">checked</xsl:attribute>
                </xsl:if>
              </input>
              <xsl:value-of select="mbzlists:listContent"/>
            </li>
          </xsl:for-each>
        </ul>
      </xsl:when>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="mbzlists:listItem">
    <li>
      <xsl:value-of select="mbzlists:listContent"/>
      <xsl:apply-templates select="mbzlists:list"/>
    </li>
  </xsl:template>

  <xsl:template match="mbzlists:image">
    <figure>
      <img src="{mbzlists:file/@url}"/>
      <xsl:if test="@caption != ''">
        <figcaption><xsl:value-of select="@caption"/></figcaption>
      </xsl:if>
    </figure>
  </xsl:template>

  <xsl:template match="mbzlists:quote">
    <blockquote>
      <p><xsl:value-of select="."/></p>
      <footer><i><xsl:value-of select="@caption"/></i></footer>
    </blockquote>
  </xsl:template>

</xsl:stylesheet>

When run on the playlist file from here, you get this generated HTML.

You can also write extensions in, say, Python and make the conversion step also run a resolver like YouTube so as to embed YouTube links in the HTML.