Creating YouTube Mixtape Playlists

Sometime last Spring YouTube dropped a feature I was using called Collections. Essentially, Collections were just subgroups within your subscriptions. This was a cool feature if you subscribe to a bunch of music channels. Many electronic music labels and artists will throw their newest tracks on YouTube with a simple backdrop for the video. I could jump into my Music Collection and play new videos restricted to the ten or so music channels I liked.

After they removed the feature, I’ve been struggling to keep up with those channels. It was quite a bit harder to just surf through the full list of new videos from my subscriptions and pick out the music videos. You also end up having to click on each one. I couldn’t just turn it on and let it play through at work.

After screwing around with RSS feeds of each channel and attempting to setup a special category in my feed reader, I decided to write a script to do what I wanted.

The script I came up with is written in Ruby and uses a gem called Yt. The idea was to pull the latest videos from a select few channels and make a weekly mixtape playlist for my user account.

Overall, the script that resulted is pretty simple. Check it out on Github

require 'yt'
require 'pp'
require 'date'

class Mixtape
  @@channels = [
    'UCJ6td3C9QlPO9O_J5dF4ZzA', # monstercat
    'UCNyo1qwT4ZKuoWsyrrdoc6g', # med school
    'UCpEYMEafq3FsKCQXNliFY9A', # arctic empire
    'UCxaLJvYDW8XMgrNbdnZ-uMQ', # camo and krooked
    'UCD7UAd18FFkcJ22wxNNwq7A', # dirty bird
    'UCkUTBwZKwA9ojYqzj6VRlMQ', # one chilled panda
    'UCyCK2Qw6YnmZzpsIwZ2CT6g', # rusted media
    'UChVfER-3s533FTh8Uae0Rhg', # seven lions
    'UC2EMLFSTChy7wpL72D-23Ug', # solar heavy
    'UCROK2sVv9Jhxcpha8v4A8KA', # spearhead
    'UCLTZddgA_La9H4Ngg99t_QQ', # suicide sheeep
    'UC5nc_ZtjKW1htCVZVRxlQAQ', # mr. suicide sheep
    'UC0n9yiP-AD2DpuuYCDwlNxQ', # tasty
    'UCH3V-b6weBfTrDuyJgFioOw', # the dub rebellion
    'UCr8oc-LOaApCXWLjL7vdsgw', # UKF DnB
    'UCfLFTP1uTuIizynWsZq2nkQ', # UKF Dubstep
  ]

  def initialize
    Yt.configure do |config|
      # config.log_level = :debug
      config.client_id = ENV['youtube_client_id']
      config.client_secret = ENV['youtube_client_secret']
    end
  end

  def get_videos
    Yt.configuration.api_key = ENV['youtube_api_key']
    lastWeek = []

    @@channels.each do |channelId|
      channel = Yt::Channel.new id: channelId
      vidCollection = channel.videos.where(publishedAfter: 1.week.ago.to_datetime.rfc3339)
      lastWeek << vidCollection.map{|v| v.id}
    end

    lastWeek = lastWeek.flatten

    if Yt.configuration.log_level == :debug
      pp lastWeek
    end

    # blank out api_key because the Yt api doesn't like to do oauth based requests with it enabled
    Yt.configuration.api_key = ""

    return lastWeek
  end

  def create_playlist(videos)
    account = Yt::Account.new(refresh_token: ENV['youtube_refresh_token'])

    mixtapePlaylist = account.create_playlist(title: "Mixtape " + DateTime.now.strftime("%D"))
    mixtapePlaylist.add_videos(videos.shuffle, auth: account)

    return mixtapePlaylist.id
  end
end

tape = Mixtape.new
videos = tape.get_videos
playlistId = tape.create_playlist(videos)

puts "https://www.youtube.com/playlist?list=" + playlistId

Since Google uses OAuth to manage authentication for anything user related (eg. creating a new playlist) things become slightly more complicated, than they may first seem. A sloppy summarization of what you’ll need to setup to run a script against your own YouTube account is as follows:

  1. Obtain a Client ID, Secret, and API Key from Google. You’ll need access to the YouTube Data API.

  2. Determine the scopes you will need. For my script youtube and youtube.upload are needed.

  3. Use your Client ID, Client Secret, and scope values to hit an authentication URL on Google. This is where you grant your script access to do the actions requested by your scope values.

  4. From step 3, you will end up with an Authorization Code. This is usually part of the URL that you are sent to after you grant access. For the return URL I generally just used localhost.

  5. Use the Authorization Code to request a Refresh Token (long lived). This token is what you will save for use with your script.

Once you have all of those chunks of user data you can add them as bash/zsh Environment variables or use a runner shell script to set them for you. It will depend on how you are planning to run the script, but I’ve found that using a little runner shell script is pretty versatile. Mine simple sets the ruby version, sets the Environment variables, and runs the script.

My ultimate goal was to make something that was automated, so I wanted this script to be run once a week. Instead of using cron or launchd, I decided to use Keyboard Maestro. This might seem strange, but it does have a couple advantages. If you are using a laptop, having something reliably run at the same time every week can be a little dicey. What if the laptop is powered down or sleeping at the time? Also, I wanted to get a notification that a new mixtape had been generated. Keyboard Maestro allows me to handle all of that pretty easily. I was able to get it setup so that on Monday morning, the first time my computer comes out of sleep this script gets kicked off and then automatically opened in my browser.

I’ve been running the script for a couple weeks now, and I am happy with the results.