15 Minute Sinatra Framework Karaoke App

 

Why?

The Sinatra framework is a great start for folks who are learning Ruby, because of its minimalist approach to getting shit done. Ruby on Rails makes a bunch of assumptions of what is needed for your application. That’s great for large scale applications, but for micro ruby applications like this I would choose Sinatra.

So lets get started.

The App

The app hits Rap Genius’s private api and gets either a youtube video or soundcloud stream and lyrics.

The end result:
http://karaokeflowmo.herokuapp.com/

The code:
https://github.com/EdwardReed/karaoke

This app took me in total about two to three hours to create, with no planning involved. Most of the time was spent thinking of features to add.

The Hierarchy

-- karaoke/
   +-- Gemfile               # Contains ruby gems
   +-- config.ru             # This file tells a handler (shotgun,heroku) what to run.
   +-- app.rb                # Sinatra app
   +-- sound_cloud_embed.rb  # Class that embeds soundcloud urls
   +-- views/                # Contains view templates
       +-- layout.erb        # HTML that's displayed on every page embeds the index.erb page
       +-- index.erb         # Displays karaoke

Step One

Make a project folder then create a Gemfile with the following code.

# Gemfile
  source "https://rubygems.org"

  gem "sinatra"
  gem "dotenv"

  gem "rapgenius", "~> 1.0.2"
  gem "video_info"
  gem "soundcloud"

  group :development do
    gem "shotgun"
    gem "tux"
  end

Now in your terminal run:

bundle install

Explanation:

You have just installed the gems listed below and their dependencies.

gem "sinatra"    # Framework
  gem "dotenv"     # Loads environment variables from `.env`
  gem "rapgenius"  # For accessing lyrics and explanations on RapGenius.com
  gem "video_info" # Get video embed info from YouTube Url
  gem "soundcloud" # SoundCloud API wrapper.

  group :development do # will only run these gems in development mode
    gem "shotgun"  # Development Server
    gem "tux"      # Development Console for debugging
  end

All of these gems can be found on github.

Next you want to create an config.ru file with the following.

# config.ru
  require "./app"
  run Sinatra::Application

Explanation:

The config.ru file is used when we connect the app to different web severs such as heroku. It tells the webserver what to run.
You can find more info about when to use this file here.

Step Two

Create an app.rb file with the following.

# app.rb
  require "sinatra"

  require "dotenv"
  Dotenv.load

  require "rapgenius"
  require "video_info"

  require "./sound_cloud_embed"

  get "/" do
    erb :"index"
  end

Explanation:

require "sinatra"               # Use the Sinatra framwork

  require "dotenv"                # Import the dotenv class
  Dotenv.load                     # Get the .env file with enviroment variables (Note this is position at the top of the file.)

  require "rapgenius"             # Import the RapGenius class

  get "/" do     # When the root url is visited it should load the index.erb
    erb :"index"
  end

Now open your terminal and run:

tux

You are now in the development console. Here you can interact with your Sinatra app.
Try typing:

RapGenius.search_by_lyrics("It goes It goes").first

You should see an object like this returned.

#<RapGenius::Song:0x007fb7f9e0d220 @id=50366, @artist=#<RapGenius::Artist:0x007fb7f9e0d388 @id=11778, @name="Death Grips", @type=:primary, @url="https://api.rapgenius.com/artists/11778">, @title="Guillotine", @url="https://api.rapgenius.com/songs/50366">

You can do a number of things with this object. When your done playing with the object type:

exit

Step Three – Putting it all together

Create a layout.erb file with the following.

<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width" />

  <title>Rapper</title>
  <meta name="description" content="Paypal Example">
  <meta name="author" content="Edward II">
  <!--[if lt IE 9]>
  <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  <![endif]-->
  <style>
  body {
    color: white;
    background-color: black;
  }
  #search {
    z-index: 10;
    margin-top: 30px;
    margin-left: 20px;
  }
  input {
    padding: none;
  }
  input[type="text"] {
    height: 6rem;
    width: 70%;
    border: none;
    font-size: 5rem;
    background-color: black;
    color: white;
    border-bottom: 2px solid white;
    padding-right: 40px;
  }
  input[type="submit"] {
    border: .2rem solid;
    border-radius: 5rem;
    width: 10rem;
    height: 6rem;
    font-size: 2rem;
    color: white;
    background: #000000;
    cursor: pointer;
    margin-left: 20px;
  }
  *:focus { outline: 0; }
  iframe {
    position: fixed; right: 0; bottom: 0;
    min-width: 100%; min-height: 100%;
    width: auto; height: auto; z-index: -100;
  }
  .lyrics {
    text-align: center;
    font-size: 5rem;
  }
  .no-lyrics {
    position: absolute;
    bottom: 0px;
    font-size: 4.3rem;
  }
  @media only screen and (max-width: 480px) {
    .no-lyrics {
      font-size: 2rem;
    }
  }
  @media only screen and (max-width: 800px) {
    .no-lyrics {
      font-size: 2rem;
    }
  }
  </style>
</head>
  <body>
    <%= yield %>
  </body>
  <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
  <script type="text/javascript" src="js/jquery.scrollmagic.min.js"></script>
  <script>

  </script>
</html>

The most imporant part of the layout.erb file is the

<%= yield %>

which executes the specific layout from the app.rb.

Ok now go to this link to register a new Sound Cloud Application.
http://soundcloud.com/you/apps/new

Create a new file called .env and add this:

SOUND_CLOUD_CLIENT=1234567890ABCDEF1234567890ABCDEF

Now you want to update this part of the environment key with the client id given to your app by soundcloud.

1234567890ABCDEF1234567890ABCDEF

Create a new file named sound_cloud_embed.rb and add the following.

require "soundcloud"

class SoundCloudEmbed

  @client = SoundCloud.new(client_id: ENV['SOUND_CLOUD_CLIENT'] )

  def self.embed(url)
    new_uri = @client.get('/resolve', url: url).uri
       %{<iframe width="100%" height="100%" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=#{new_uri}&amp;auto_play=true&amp;hide_related=true&amp;show_comments=false&amp;show_user=false&amp;show_reposts=false&amp;visual=true"></iframe>}
  end

end

Explanation:

require "soundcloud"  # Get SoundCloud class from soundcloud gem

class SoundCloudEmbed # Create a new class

  @client = SoundCloud.new(client_id: ENV['SOUND_CLOUD_CLIENT'] ) # Create a client from SoundCloud by providing the client id from the .env file

  def self.embed(url) # this method returns an iframe when a SoundCloud url is given
    new_uri = @client.get('/resolve', url: url).uri # Gets SoundCloud Uri and formats it properly
    # The next line returns a iframe with the properly formated uri
       %{<iframe width="100%" height="100%" scrolling="no" frameborder="no"
          src="https://w.soundcloud.com/player/?url=#{new_uri}&amp;auto_play=true&amp;hide_related=true&amp;show_comments=false&amp;show_user=false&amp;show_reposts=false&amp;visual=true"></iframe>}
  end

end

Amend the end of app.rb with the following.

# app.rb
  post "/" do
    @song = RapGenius.search_by_lyrics(params[:song]).first
    erb :"index"
  end

Add this under the require rapgenius in app.rb

require "video_info"
  require "./sound_cloud_embed"

Explanation:

#app.rb
###
  require "video_info"            # Import the VideoInfo class
  require "./sound_cloud_embed"   # This will Import the SoundCloudEmbed class
###
  post "/" do     # When a post action is made to the root url and
                  # @song instance var is given and the index page loads
    @song = RapGenius.search_by_lyrics(params[:song]).first #The @song instance variable will contain the first song listed from Rap Genius.
    erb :"index"  # render the index.erb page with the @song variable
  end

Next, create an index.erb with the following:

<form id="search" action="/" method="post" >
    <input type="text" name="song" value="<%= @song ? @song.title : "think that im kobe" %>"/>
    <input value="Search" type="submit"/>
  </form>
  <% if @song %>
    <div class="lyrics">
      <% @song.lines.count.times do |i| %>
        <p><%= @song.lines[i].lyric %></p>
      <% end %>
    </div>
  <% else %>
    <div class="no-lyrics">
      <h1>Search your favorite lyrics above.</h1>
    </div>

  <% end %>
  <% if @song && @song.media.first %>
      <% if @song.media.first.provider == "youtube" %>

        <%= VideoInfo.new(@song.media.first.url).embed_code(
          url_attributes: {
            autoplay: 1,
            controls: 0
          },
          iframe_attributes: { width: "100%", height: "100%" }) %>

      <% else %>
        <%= SoundCloudEmbed.embed(@song.media.first.url) %>
      <% end %>
    <% end %>

Explanation:

<!-- This form is the search form that makes a post request to "/" -->
  <form id="search" action="/" method="post" >
  <!-- The input value is filled with some lyrics from Chief Keef by default or the searched song's title. -->
    <input type="text" name="song" value="<%= @song ? @song.title : "think that im kobe" %>"/>
    <input value="Search" type="submit"/>
  </form>
  <% if @song %><!-- If a song has been returned we what to loop through all of the lyrics and display them -->
    <div class="lyrics">
      <% @song.lines.count.times do |i| %>
        <p><%= @song.lines[i].lyric %></p>
      <% end %>
    </div>
  <% else %>
    <div class="no-lyrics">
      <h1>Search your favorite lyrics above.</h1>
    </div>

  <% end %>
  <!-- The Rap Genius Api returns a youtube video or soundcloud url for the media type. -->
  <% if @song && @song.media.first %>
      <!-- If a youtube url is returned we want to use the videoinfo gem to embed the video -->
      <% if @song.media.first.provider == "youtube" %>

        <%= VideoInfo.new(@song.media.first.url).embed_code(
          url_attributes: {
            autoplay: 1,
            controls: 0
          },
          iframe_attributes: { width: "100%", height: "100%" }) %>

      <% else %>
        <!-- If a SoundCloud url is returned we use the SoundCloudEmbed class to embed the link -->
        <%= SoundCloudEmbed.embed(@song.media.first.url) %>
      <% end %>
    <% end %>

All Done

Open your terminal and run:

shotgun

You should have a webpage at:

http://127.0.0.1:9393/

What now ?

Well thats up to you.

Regardless of what you do you can expect a follow up of this post, covering the completion of our Karaoke app. After all, what good is a karaoke app without a mic?

Author: Edward Reed II

More Posts by Edward Reed: