Youtube Together 7 — Touchups

(Originally posted July 22, 2013)

With all the basic functionality finished in the last post, now is the time to add the little features that make the experience better for the users.

The first thing we’re going to do is randomize the id’s for the room. The way we had it before, the primary key in the model was autoincrementing, which means that it would be pretty easy to guess what rooms had already been created. Since we’re not using password authentication, we need some little measure of security to help prevent people from guessing open rooms. The fix here is pretty simple. All we have to do is change the __init__ function for the Room model.

1
2
3
4
5
6
7
def __init__(self, name):
  check = not None
  while check is not None:
    poss_id = int(str(uuid.uuid4().int)[0:6])
    check = Room.query.get(poss_id)
  self.id = poss_id
  self.name = name

The loop here just obtains a random integer from the python module uuid (which also needs to be imported at the top of the file), and performs a check to see if we’ve already generated that key. Considering there are 10^6 different possibilities, for the id, the odds aren’t very good, but we should perform the check anyway. Fire up the server again and create a new room and check out the new random ids. One thing to note is that we don’t want it to be too large of an int since we have to share it with others, but six seems like a decent size.

Currently, if there are no songs in the unplayed queue, meaning that the player isn’t playing a video, we have no way of getting it to play unless we reload the page. This is because we only start a video on the signaled event that a video has ended. This is a really quick fix in the update function.

1
2
3
4
5
if (player != null) {
  if (player.getPlayerState() == -1 && unplayed_videos.length > 0) {
    player.loadVideoById(unplayed_videos[0]);
   }
}

The first check if to see if the player has been initialized. The we just check to see if anything is playing and if we have videos cued up, then play it if we do. This check should go at the bottom of the success function in update after we updated the queues.

The next thing we want to do is change the markup for the video title if we’ve played it or not. If you recall, in the update function, when we created the new element, we added an extra class — vid-played — if the song had already been played. Now we get to use that. In a css file, probably the one you’re using for the entire site (or you can just include it in the html file) you can add the following rule.

.vid-played {
color: #AAAAAA;
}

If you put this in a separate file, you also need to make sure that you include the link somewhere, generally in the head where the other css files are. This rule simply turns the text, which in this case is the title of the video gray. You need to reload the page to see the effects but you can skip to the end of the first video to see the effect.

Another issue we want to fix is to let the user know that we’re processing the request for url upload. We want a few things to happen, all of which will take effect before the ajax call, and will be resolved on success. The first is that we want to disable the upload button so we don’t have someone spamming the site until we finished with one. The next is we want to show them some information that we are working on their request. Something like a spinning wheel you sometimes see.

To get an animation of a spinner, do a google search along the lines of processing spinner to find a gif. We then want to add that that as an image next to the submit button in room.html like this.

1
2
<button type="submit">Upload</button>
<img src="/static/img/spinner.gif" style="visibility:hidden" />

Since we don’t want the spinner going constantly, we set the visibility to hidden. We then move on to the javascript portion, which will handle the turning on and off of the elements.

1
2
3
$(this).find("button").attr("disabled","disabled"); //hide the button
$(this).find("img").css("visibility","visible");
var form = $(this);

This piece of code should be put at before the ajax call in the submit override function. This disables the submit button and shows the spinner. We want to save $(this) in a variable so we can use is in the success function since the form goes out of scope at that point. Then, in the success function below, add

1
2
form.find("button").removeAttr("disabled"); //hide the button
form.find("img").css("visibility","hidden");

This is simply the reverse of the actions above and set the form back to its normal state. Refresh the page to get the updated js and you should see the animation!

The last thing we want to do is to error check the video upload, and return errors if there are any. First step is adding a span next to the image we just created. This span will hold the errors that we put in there from javascript.

1
<span style="color:red"></span>

Back in the views, we need to add checking for the two error we can likely expect. A url not from youtube, and a video not found. I’ll include the entire view for reference.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@app.route('/room/<rid>', methods=['GET''POST'])
def room(rid):
  room = Room.query.get(rid)
  if room is None:
    return abort(404)
  if request.method == 'POST':
    poss_url = request.form['youtube-url']
    parsed_url = urlparse.urlparse(poss_url)
    if parsed_url[1== YOUTUBE_URL:
      query_dict = cgi.parse_qs(parsed_url[4])
      try:
        video_key = query_dict['v'][0]
      except ValueError:
        errors = "Video not found"
        return jsonify(success=False, errors=errors)
      data_url = YOUTUBE_DATA_URL + video_key + YOUTUBE_DATA_PARAMS_URL
      resp = requests.get(data_url)
      if resp.status_code != 200:
        errors = "Video not found"
        return jsonify(success=False, errors=errors)
      video_info_dict = resp.json()
      title = video_info_dict['entry']['title']['$t']
      url = YTUrl(poss_url, title, video_key, rid)
      db.session.add(url)
      db.session.commit()
      return jsonify(success=True)
    errors = "Need Youtube url"
    return jsonify(success=False, errors=errors)
 queue = room.urls
 return render_template('room.html', queue=queue, room=room)

The errors here are passed back to the client with the keyword errors for easy use. The last step is to change the javascript to display the errors. The flow we want is to have an error be presented if there are errors, and have the message be removed on next upload attempt.

1
2
3
if (data.errors) {
  form.find(".vid-submit-errors").text(data.errors);
}

This snippet lives in the success function, just below where we changed the effects of the spinner and the upload button. It simply puts the text in that span we created.

1
$(this).find(".vid-submit-errors").text("");

This line lives below the effects we perform before the ajax call. It removes the text if there is any. And that’s it. A refresh and you can test out giving bad input to the server.

With all those little changes fixed, all that’s left for the mvp is making it look pretty and creating a welcome page that entices users.

Leave a comment