SpotCon: Playlist filtering by artist/album

Download: MediaFire (400Kb)

Source: GitHub

The ability to filter a list of tracks by artist and album has been in iTunes and Foobar2000 for quite some time. So long that I find myself missing it while using Spotify.  A few weeks ago, I added this ability for search results in SpotCon but with this update it becomes much more powerful with the introduction of playlist support. Now, for example, you can import your playlists from Spotify and easily listen to a single artist or album from that playlist.

If you don’t care about the technical details regarding the addition of Playlist support, you can stop reading now. 🙂 Continue reading “SpotCon: Playlist filtering by artist/album”

SpotCon: Black theme update

I’ve updated SpotCon in response to Spotify’s new “black” theme and the introduction of the “My Music” feature. Notable changes include:

  • Removal of the “collections” feature. Spotify’s “My Music” feature renders this unnecessary.
  • Black theme and some other aesthetic improvements.

Image

SpotCon: A remote Spotify controller

SpotCon is not a Spotify client. It’s simply a Spotify remote which can control and view the status of Spotify on a networked computer.

SpotCon

Installation

Download from MediaFire (337 Kb)

Source Code

See the GitHub repository

What does it do?

When run on each Windows machine on your home network where you’ve installed Spotify, SpotCon lets you do any of the following to any of the remote (or even local) Spotify clients:

  • Standard operations such as Play, Pause, Previous, and Next track.
  • See the status of the currently playing track, including artist, track name, album art and play position.
  • Search Spotify’s entire music library and remotely play any song.

What can’t it do (yet)?

  • Set the current play position. You can view it, but not set it.
  • Set the shuffle or repeat settings. These are both read-only (and cannot distinguish between repeat-all and repeat-one).
  • En-queue a list of songs. For now, it’s one-at-a-time.

How does it work?

SpotCon communicates with the Windows computers on your home network via TCP on port 1729. The first time you launch it you will be asked to make a Windows Firewall exception. Commands are sent between each  machine which are translated into actions that tell Spotify what to do. See the GitHub repository for the source code.

It doesn’t work. Fix it.

Create a new issue on the GitHub repository or shoot me a mail at spotcon@outlook.com.

Converting a list of song names into a Spotify playlist cont’d

After receiving some positive feedback on my previous post I’ve built a Windows application to make creating and managing Spotify playlists dead simple: Playlistify.

How it works

Provide a list of songs

Image

Drag/drop your playlist’s current tracks from Spotify

Image

Let the magic happen

Playlistify shows suggestions when an exact match is not found. If the suggestions are poor you can specify a Spotify URI directly to the track. Also, it caches all the requests made to the Spotify Web API so once your playlist is created and you attempt to add new tracks to it, it will run much faster!

Image

Drag/drop the output back to your Spotify playlist!

Download

Download at mediafire.com (47Kb)

Source Code

Converting a list of song names into a Spotify playlist

One of my favorite radio stations of the Seattle area is 107.7 The End. It mostly plays Alternative Rock which is my go-to genre for general music listening. I’m also an avid Spotify Premium user and I create lots of playlists, one of which I populate entirely of songs played by 107.7 as listed on its TuneGenie page.

At first I would manually traverse this list of songs, find each track on Spotify, and add it to the playlist. This soon became very tedious because Spotify allows for adding the same song multiple times in the same playlist. For each track I had to first check (or remember) if I had previously added it to the playlist. So, I wrote a tool to do this all for me.

Spotify Metadata API

Spotify publishes a web API that allows for searching its database of artists, tracks, and albums. Example queries look like the following:

I wrote a .NET wrapper on this API called SpotifyWebSharp.

Extracting songs from a TuneGenie list

The 107.7 TuneGenie page is horrible. Slow, bloated, and no RSS feed to boot.

Image

Let’s reformat this page to make it easier to pull the song information out of it. jQuery to the rescue!

  1. Expand the page by clicking each Click to load songs from… links
    $(".hourlink").each(function() { $(this).click() });

    tunegenie-expanded

  2. Remove all the information except for the track artist and song
    $(".sl_item.sl_highlight.opened").removeAttr("style");
    $(".ajaxdetails").remove();
    $(".column.playedat").remove();
    $(".column.pl_tiny.right").remove();
    $(".tourdate").remove();
    $(".boxad.adunit.column.last").remove();

    tunegenie-formatted

  3. Swap the artist and song name
    $(".song.column.tip").each(function(index) { $(this).before($(".artist.column")[index]) });
  4. Add a pipe (|) character between the artist and song name. It will be used later to parse the track information.
    $(".artist.column").each(function() { $(this).append("<div>|</div>") });
  5. Add a double-pipe (||) after each song name. It will be used to distinguish one song from another
    $(".song.column.tip").each(function() { $(this).append("<div>||</div>") });
  6. Remove <script> tags that just get in the way
    $("script").remove();

    tunegenie-strippedSure, right now it looks like crap, but now the data are in a format that can easily be stripped out.

  7. Grab the text of the parent #songlist <div>, remove the plethora of tab characters, and replace the double-pipe (||) with a newline character. Then replace the <body> with this string wrapped in a <pre> tag.
    $("body").html("<pre>" + $("#songlist").text().replace(/\s{2}/g, "").replace(/\|\|/g, "\r\n") + "</pre>");

    tunegenie-finalVoilà! I then copy this list into a text file that will then be read by a command-line tool that hooks into SpotifyWebSharp.

Find the songs missing from the playlist

Preparation

  1. View your playlist in Spotify
  2. Ctrl-A to select all the tracks
  3. Drag/drop them to your favorite text editor. It should look something like this:
    input
  4. Optional, but I found it to work a little better: use the Spotify Lookup service to get the track IDs for each of these tracks. The same song can have multiple hrefs, but only one ID. Save a file that contains this list.

Pseudocode

foreach (artist-songname combination)
{
    if (Spotify artist info is cached)
    {
        get artist info from cache
    }
    else
    {
        get artist info from Spotify search
        if (exact match not found)
        {
            prompt with suggestions or for manually entered name
        }
        cache artist info
    }
    
    if (Spotify track info is cached)
    {
        get track info from cache
    }
    else
    {
        get track info from Spotify search
        if (exact match for the tracknot found)
        {
            prompt with suggestions or for manually entered name
        }
        cache track info
    }

    if (track does not exist in playlist)
    {
        add track to list of new songs
    }
}

Output

  1. The program writes a text file that includes a list of all the track URLs. Drag/drop from your favorite text editor into Spotify and the tracks will be added to the playlist!
  2. The program also outputs a tracklist that piggy-backs off of billboard.com’s playlist viewer. An example.
  3. Here is some sample console output from a recent run:
    output

Partial Fraction Decomposition

Let

r(x) = \frac{b_0 + b_1x + \cdots + b_{n-1}x^{n-1}}{(c_1 + d_1x)(c_2 + d_2x)\cdots(c_n + d_nx)},

where d_i \neq 0 for all i = 1, 2, \dots, n. We assume the denominator consists of unique linear factors and there are no common factors between the numerator and the denominator.

Rewrite r(x) in the form

\hat{r}(x) = \frac{A_1}{c_1 + d_1x} + \frac{A_2}{c_2 + d_2x} + \cdots + \frac{A_n}{c_n + d_nx},

where A_1, A_2, \dots, A_n are real constants.


Let p(x) = b_0 + b_1x + \cdots + b_{n-1}x^{n-1} and q(x) = \prod_{k=1}^n(c_k + d_kx). Thus, r(x) = p(x)/q(x). Let us consider the linear factors of q(x).

Let q(x) = \prod_{k=1}^nl_k(x), where l_k(x) = c_k + d_kx. By inspection, l_k(-c_k/d_k) = 0 and l_i(-c_k/d_k) \neq 0 for all i \neq k since the factors are distinct.

If we equate r(x) = \hat{r}(x) and multiply both sides by the denominator q(x),

\begin{array}{rcl}  r(x)q(x) & = & \hat{r}(x)q(x)\\  p(x) & = & A_1q(x)/l_1(x) + A_2q(x)/l_2(x) + \cdots + A_nq(x)/l_n(x)\\       & = & A_1\frac{\prod_{k=1}^nl_k(x)}{l_1(x)} + A_2\frac{\prod_{k=1}^nl_k(x)}{l_2(x)} + \cdots + A_n\frac{\prod_{k=1}^nl_k(x)}{l_n(x)}.  \end{array}

Let’s define a special function

d_j(x) = \prod_{\begin{tiny}k \neq j\end{tiny}}l_k(x) = \prod_{\begin{tiny}k \neq j\end{tiny}}(c_k + d_kx)

that is the product of all but the j-th linear factor of q(x). Hence p(x) becomes

p(x) = A_1d_1(x) + A_2d_2(x) + \cdots + A_kd_k(x) + \cdots + A_nd_n(x)

Let x_k = -c_k/d_k. Thus,

p(x_k) = A_1d_1(x_k) + A_2d_2(x_k) + \cdots + A_kd_k(x_k) + \cdots + A_nd_n(x_k).

But d_i(x) contains the linear factor l_k(x) for every i \neq k. Hence only d_k(x_k) is nonzero and

p(x_k) = A_kd_k(x_k) \Rightarrow A_k = p(x_k)/d_k(x_k).


For example, rewrite

r(x) = \frac{2 + 4x + 6x^2}{(1 - x)(1 + x)(2 + 3x)}

in the form

\frac{A_1}{1 - x} + \frac{A_2}{1 + x} + \frac{A_3}{2 + 3x}.

We have

\begin{array}{rcl}  p(x) & = & 2 + 4x + 6x^2, \\  d_1(x) & = & (1 + x)(2 + 3x), \\  d_2(x) & = & (1 - x)(2 + 3x), \rm{and} \\  d_3(x) & = & (1 - x)(1 + x).  \end{array}

Hence x_1 = 1, x_2 = -1, and x_3 = -2/3. Thus, A_1 = p(1)/d_1(1) = 12/10 = 6/5, A_2 = p(-1)/d_2(-1) = 4/(-2) = -2, and A_3 = p(-2/3)/d_3(-2/3) = 2/(5/9) = 18/5.

We have

\begin{array}{rcl}  r(x) & = & \frac{2 + 4x + 6x^2}{(1 - x)(1 + x)(2 + 3x)} \\       & = & \frac{6/5}{1 - x} - \frac{2}{1 + x} + \frac{18/5}{2 + 3x}.  \end{array}

Roots of Composite Functions

Find the roots of

C(x) = q_0 \bigg(q_1\Big(q_2\big(\cdots q_n(x)\big)\Big)\cdots\bigg) = q_0 \circ q_1 \circ q_2 \circ \cdots \circ q_n  ,

the composition of q_0(x), q_1(x), \dots, q_n(x), where

q_i(x) = c_{i} + b_{i}x + a_{i}x^2, where c_{i}, b_{i}, a_{i} \neq 0 \in \textbf{R}

Let’s begin with an example.

Let C(x) = q_0\big(q_1(x)\big), where

\begin{array}{rcl}  q_0(x) & = & 12 - 8x + x^2\\  q_1(x) & = & 12 - 7x + x^2  \end{array}

Then, C(x) = 12 - 8q_1(x) + [q_1(x)]^2. So, if we can find the values of x such that the value of q_1(x) is a root of q_0(x), we will find the roots of C(x).

By the quadratic equation,

\begin{array}{rcl}  q_1(x) & = & \frac{8}{2} \pm \frac{1}{2}\sqrt{64-48}\\  & = & 2,6  \end{array}

So when q_1(x) is 2 or 6, q_0\big(q_1(x)\big) will be 0.

From the quadratic equation, q_1(x) = 2 when x = 2,5 and q_1(x) = 6 when x = 1,6

So, the 4 roots of C(x) are 1, 2, 5 and 6.
Continue reading “Roots of Composite Functions”

Making Change

It’s something that every cashier worth their salt knows how to do with their eyes closed. Your favorite candy bar costs 64¢ but got no change in your pocket? Soon 3 coins: a quarter, dime, and penny totaling 36¢ will be happily jingling in your pocket as you leave the supermarket. But what if we had a 36¢ coin? Then the cashier would hand you a single coin, your receipt, and you’d be enjoying your Kit-Kat in no time. Or even better, a 64¢ coin? You’d be in and out before you could say “Give me a break.”

What repercussions would there be with such a coin system? Say we replaced the quarter with a 36¢ piece. Making change for amounts up to 24¢ would be exactly the same as our standard system, but any amounts higher would be a bit different since we have no quarter. All amounts between 25¢  and 35¢ inclusively would begin with 2 dimes and a nickel to make up for the lack of a quarter. This means 2 more coins are necessary to make change for all these amounts. Making 36¢ in change would only take a single coin whereas in the standard system it takes 3.

Assuming all amounts of change to be made are equally likely, replacing the quarter with a 36¢ piece would actually lower the average number of coins required to make change! With our current system, the average number of coins needed is 4.7, with a low of 1 coin to a high of 9 (for 94¢ and 99¢). If we replaced the quarter with a 36¢ piece, this average actually decreases to 4.4 with a high of only 7 (note that 94¢ only requires 6 coins and 99¢ requires 7).

But what if we continued down this path of trying to limit the average number of coins needed to make change? Which coin or coins should we change, or even add or remove?

Continue reading “Making Change”

Sums of Powers

We’ve all seen the formula for the sum of the first n integers:

\displaystyle\sum_{i=1}^{n}i = 1 + 2 + 3 + \dots + n = \frac{n(n + 1)}{2}

A similar formula exists for the sum of the squares of the first n integers.

\displaystyle\sum_{i=1}^{n}i^2 = 1^2 + 2^2 + 3^2 + \dots + n^2 = \frac{n(n + 1)(2n + 1)}{6}

One day during a particularly boring lecture in one of my gen-ed undergraduate courses I was playing with these formulas and I stumbled across a recursive formula for the sum of the first n integers taken to any integer power. That is,

\displaystyle\sum_{i=1}^{n}i^m, where m = 0, 1, 2, \dots

Let’s define a function S such that S(m) = \displaystyle\sum_{k=1}^{n}k^m. The domain of S is the set of nonnegative integers. Let’s begin by investigating several small values of m.

\begin{array}{rcl}  S(0) & = & \displaystyle\sum_{k=1}^{n}k^0\\  & = & \displaystyle\sum_{k=1}^{n}1\\  & = & \underbrace{1 + 1 + \cdots + 1}_{n}\\  & = & n  \end{array}  \begin{array}{rcl}  S(1) & = & \displaystyle\sum_{k=1}^{n}k^1 \\  & = & \displaystyle\sum{k} \\  & = & 1 + 2 + \cdots + n  \end{array}

To derive an iterative formula for S(1), let’s investigate the function y=x. Let’s partition the interval [0, n] into unit subintervals: I_k = [k-1, k], k=1, 2, \dots, n, and draw a rectangle in each subinterval with bottom-left corner at [k-1, 0] and top-right corner at [k, k].

This is similar to using a right endpoint approximation of the definite integral using rectangles. Since each rectangle has a base of 1 and height of k, then the sum of the areas of these rectangles is S(1).

Let’s find the sum of the areas of the rectangles by partitioning each rectangle into two parts: the parts above (pink) and below the curve (red). Adding them together will give the area of any particular rectangle.

\begin{array}{rcl}  S(1) & = & \displaystyle\int_{0}^{n}xdx + \sum_{k=1}^{n}\left[\int_{k-1}^{k}(k-x)dx\right] \\       & = & \displaystyle\frac{n^2}{2}+\sum_{k=1}^n \left[ \left. \left( kx-\frac{x^2}{2} \right) \right|_{k-1}^k \right] \\       & = & \displaystyle\frac{n^2}{2}+\sum_{k=1}^n \left[k (k - (k - 1))-\left(\frac{k^2}{2} - \frac{(k-1)^2}{2}\right) \right] \\       & = & \displaystyle\frac{n^2}{2}+\sum_{k=1}^n \left[k -\left(\frac{k^2}{2} - \frac{k^2-2k+1}{2}\right) \right] \\       & = & \displaystyle\frac{n^2}{2}+\sum_{k=1}^n \left[\frac{2k - k^2 + k^2 - 2k + 1}{2} \right] \\       & = & \displaystyle\frac{n^2}{2}+\sum_{k=1}^n \frac{1}{2} \\       & = & \displaystyle\frac{n^2}{2} + \frac{1}{2}S(0) \\       & = & \displaystyle\frac{n^2}{2} + \frac{n}{2} \\       & = & \displaystyle\frac{n(n+1)}{2}  \end{array}

Similarly, we can derive an iterative formula for S(2).

\begin{array}{rcl}  S(2) & = & \displaystyle\int_{0}^{n}x^2dx + \sum_{k=1}^{n}\left[\int_{k-1}^{k}(k^2-x^2)dx \right] \\       & = & \displaystyle\frac{n^3}{3} + \sum_{k=1}^{n}\left[k^2-\frac{k^3}{3} + \frac{(k-1)^3}{3} \right] \\       & = & \displaystyle\frac{n^3}{3} + \sum_{k=1}^{n}\left[ \frac{3k-1}{3} \right] \\       & = & \displaystyle\frac{n^3}{3} + S(1) - \frac{1}{3}S(0) \\       & = & \displaystyle\frac{n^3}{3} + \frac{n(n+1)}{2} - \frac{n}{3} \\       & = & \displaystyle\frac{n(n+1)(2n+1)}{6}  \end{array}

Let’s begin to investigate the general case S(m).

\begin{array}{rcl}  S(m) & = & \displaystyle\int_{0}^{n}x^mdx + \sum_{k=1}^{n}\left[\int_{k-1}^{k}(k^m-x^m)dx \right] \\       & = & \displaystyle\frac{n^{m+1}}{m+1} + \sum_{k=1}^{n}\left[k^m - \frac{k^{m+1}}{m+1} + \frac{(k-1)^{m+1}}{m+1} \right] \\       & = & \displaystyle\frac{n^{m+1}}{m+1} + \frac{1}{m+1}\left[ \sum_{k=1}^{n}\left[ (m+1)k^m - k^{m+1} + (k-1)^{m+1} \right] \right]  \end{array}

But we can expand (k-1)^{m+1} using the Binomial Theorem.

\begin{array}{rcl}  (k-1)^{m+1} & = & \displaystyle \sum_{i=0}^{m+1}\binom{m+1}{i}k^{m+1-k}(-1)^k \\              & = & \displaystyle k^{m+1} - (m+1)k^m + \cdots + (-1)^m(m+1)k + (-1)^{m+1}  \end{array}

Substituting into the above equation for S(m),
\displaystyle S(m) = \frac{n^{m+1}}{m+1} + \frac{1}{m+1}\left[ \sum_{k=1}^{n}\left[ (m+1)k^m - k^{m+1} + (k^{m+1} - (m+1)k^m + \cdots) \right] \right]

These terms cancel and filling in the \cdots,

\begin{array}{rcl}  S(m) & = & \displaystyle\frac{n^{m+1}}{m+1} + \frac{1}{m+1}\left[ \sum_{k=1}^{n} \left[ \binom{m+1}{2}k^{m-1} -+ \cdots + (-1)^{m}(m+1)k + (-1)^{m+1} \right] \right] \\       & = & \displaystyle\frac{n^{m+1}}{m+1} + \frac{1}{m+1}\left[ \sum_{i=2}^{m+1}(-1)^i\binom{m+1}{i}S(m+1-i)\right], \textrm{where } S(0) = n.  \end{array}

Or if we write it in terms of m-1,
S(m - 1) = \displaystyle\frac{n^{m}}{m} + \frac{1}{m}\left[ \sum_{i=2}^{m}(-1)^i\binom{m}{i}S(m-i)\right], \textrm{where } S(0) = n.

We now have a recursive formula for S(m). Let’s try it out with m=3

\begin{array}{rcl}  S(3) & = & \displaystyle\frac{n^4}{4} + \frac{1}{4} \left[ \binom{4}{2}S(2) - \binom{4}{3}S(1) + \binom{4}{4}S(0) \right] \\       & = & \displaystyle\frac{n^4}{4} + \frac{1}{4} \left[ 6\left( \frac{n(n+1)(2n+1)}{6} \right) - 4\left( \frac{n(n+1)}{2} \right) + n \right] \\       & = & \displaystyle\frac{1}{4} \left( n^4 + 2n^3 + 3n^2 + n - 2n^2 -2n + n \right) \\       & = & \displaystyle\frac{1}{4} ( n^4 + 2n^3 + n^2 ) \\       & = & \displaystyle\frac{n^2(n+1)^2}{4}  \end{array}

The results for larger values of m are summarized in the following table.

m S(m) S(m)^*
0 \displaystyle n \displaystyle n
1 \displaystyle\frac{n(n+1)}{2} \displaystyle\frac{1}{2}n^2 + \frac{1}{2}n
2 \displaystyle\frac{n(n+1)(2n+1)}{6} \displaystyle\frac{1}{3}n^3 + \frac{1}{2}n^2 + \frac{1}{6}n
3 \displaystyle\frac{n^2(n+1)^2}{4} \displaystyle\frac{1}{4}n^4 + \frac{1}{2}n^3 + \frac{1}{4}n^2
4 \displaystyle\frac{n(n+1)(2n+1)(3n^2+3n-1)}{30} \displaystyle\frac{1}{5}n^5 + \frac{1}{2}n^4 + \frac{1}{3}n^3 - \frac{1}{30}n
5 \displaystyle\frac{n^2(n+1)^2(2n^2+2n-1)}{12} \displaystyle\frac{1}{6}n^6 + \frac{1}{2}n^5 + \frac{5}{12}n^4 - \frac{1}{12}n^2
6 \displaystyle\frac{n(n+1)(2n+1)(3n^4+6n^3-3n+1)}{42} \displaystyle\frac{1}{7}n^7 + \frac{1}{2}n^6 + \frac{1}{2}n^5 - \frac{1}{6}n^3 + \frac{1}{42}n
7 \displaystyle\frac{n^2(n+1)^2(3n^4+6n^3-n^2-4n+2)}{24} \displaystyle\frac{1}{8}n^8 + \frac{1}{2}n^7 + \frac{7}{12}n^6 - \frac{7}{24}n^4 + \frac{1}{12}n^2
8 \displaystyle\frac{n(n+1)(2n+1)(5n^6+15n^5+5n^4-15n^3-n^2+9n-3)}{90} \frac{1}{9}n^9 + \frac{1}{2}n^8 + \frac{2}{3}n^7 - \frac{7}{15}n^5 + \frac{2}{9}n^3 - \frac{1}{30}n
9 \displaystyle\frac{n^2(n+1)^2(2n^6+6n^5+n^4-8n^3+n^2+6n-3)}{20} \displaystyle\frac{1}{10}n^{10} + \frac{1}{2}n^9 + \frac{3}{4}n^8 - \frac{7}{10}n^6 + \frac{1}{2}n^4 - \frac{3}{20}n^2

The following table simply contains the coefficients of the expanded function S(m)^* where the i-th entry is the coefficient of n^i.

m S(m)^*
10 \displaystyle \frac{5}{66}, 0, -\frac{1}{2}, 0, 1, 0, -1, 0, \frac{5}{6}, \frac{1}{2}, \frac{1}{11}
11 0, \frac{5}{12}, 0, -\frac{11}{8}, 0, \frac{11}{6}, 0, -\frac{11}{8}, 0, \frac{11}{12}, \frac{1}{2}, \frac{1}{12} ]