A terminal based music streamer using Puppeteer & NodeJs

Chaitanya Reddy
4 min readOct 25, 2020

First of all what is puppeteer?

Puppeteer is an open source Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. We can leverage this control over browser to do interesting things. In this blog, we will use puppeteer to launch a headless browser, go to Soundcloud, show available genres and play some good playlists from a selected genre all from a terminal!

Let’s start by installing puppeteer-core. We are going to use puppeteer-core because it can bind to existing chromium based browser whereas puppeteer installs it’s own browser.

npm i puppeteer-core

Since puppeteer takes a while to complete each task we will first wrap our code in an async function:

Now we have a 6 step process that we want puppeteer to go through in order to achieve this:

  1. Launch the browser
  2. Open a new tab
  3. Go to Soundcloud URL
  4. Scrape the available genres and show it on terminal
  5. Take user input for selection of genre
  6. Play any of the Top 5 playlists from selected genre
  7. Scrape for the current track continuously and show it on terminal

1. Launch the browser

First, we need to launch a headless browser. But by default, the headless browser ignores the audio, so we will add config to enable the audio play. Change the executablePath to your browser accordingly. For chrome, you can find it on “chrome://version”.

2 & 3 . Open a new tab and go to URL

These steps are pretty simple, we use ‘networkidle0’ to ensure the page is completely loaded before we move to next tasks.

4. Scrape the genres and printing them

In this step, we will scrape the page for available genres. Puppeteer has a method on a page called evaluate which runs a callback function where you can specify what DOM element you want the data from. After inspecting the soundcloud/discover page, we can find that the genres are simply list items with the class name of ‘mixedModularHome__item’. Selecting all of them will return a node list which we will save into a constant variable like this:

Now in order to access the actual genre titles, we need to dig deeper within each element of the list in the following way:

Finally, we want to loop through listOfAllGenres list and push all the genre names to a new list and show it on terminal. The whole code for step 4 looks the following way:

5. Take user input for genre selection

Now, we take user input for genre selection. Here, we prompt the user continually until correct input is given. We use ‘readline’ to create interface for input and output and add a getInput() function that awaits for the user input. We then loop continuously prompting for the right input and finally, show the selected genre like this:

6. Play any top playlist and print its name

This is the part where most work lies. So, let’s break into three sub-steps:

6.1 Fetch the list of playlists for a genre

After we get the user selection, we again call the page.evaluate() to search for the playlist elements for a particular genre. Since we already know the genre list item, we are going to search over its children for the playlist elements. Again after inspecting its children, we find that under each genre item, there is list of <div> elements with class name, ‘audibleTile’ that make up the playlist. So, we get the list of playlist elements by querying over the children of each genre item like this:

After inspecting more thoroughly we can see that a few genres have their playlists grouped under class name, ‘systemPlaylistTile’. We also get these playlist items similarly.

6.2 Select a random playlist and scrape its name

The main part of Step 6 comes here, we create a function that selects a random playlist from the available playlist collection and plays the playlist using click() on play button element of the playlist item. In the end, we scrape the name of the playlist selected and return it. The code for this function:

6.3 Select from available playlist collection

In this step, we select the playlist collection which is not empty and pass it to the above function with selector for getting playlist name and show the user that there are no playlists in case we have both empty playlist collections:

After combining the above sub steps, in the end we print to terminal the playlist title being played. The whole code for Step 6 looks like this:

7. Scrape for current track and update

Now that the heavy part’s over, for Step 7, we need to scrape the title of the track being played, print it to terminal. Also, we need to check if the track has changed and print the new track to terminal. After inspecting the DOM elements we find that the current track is nested under the element with class name, ‘playbackSoundBadge__titleLink’. We again call the page.evaluate() to get this element and the text inside it like this:

To update our current track we use the above function in a continual loop to check for track change and print to terminal if there’s a new track. We also add a 1s delay for each iteration to reduce number of calls to page.evaluate() inside this loop. We also need to add a delay after Step 6 to wait for the playlist to begin playing before we start scraping for the current track. Finally, the code for Step 7 looks like this:

Now to run this script, just go to your terminal and enter this:

node soundcloud_streamer.js

You can see how the script works in this demo. Don’t forget to turn up the volume :)

Hope you found this useful. You can go through the whole code in this repo and read more about puppeteer here.

--

--