Watching Videos

Our rumbl application will allow us to add messages to videos in real time. We’ll do some groundwork to make this process more convenient when the time comes. We’ll tweak our views to make it easy to watch videos. Then, we’ll create a new controller explicitly for watching a video, along with its view and template. Next, we’ll tweak the router to pick up our new routes. Finally, we’ll add some JavaScript to plug in to YouTube’s API. You’ll work through these features quickly, because they don’t involve much new ground.

Let’s let the user watch a video. First let’s enhance our layout header with a link to My Videos for the current user in lib/rumbl_web/templates/layout/app.html.eex:

 <nav role=​"navigation"​>
  <ul>
 <%=​ ​if​ @current_user ​do​ ​%>
  <li>​<%=​ @current_user.username ​%>​</li>
  <li>​<%=​ link ​"​​My Videos"​, ​to:​ Routes.video_path(@conn, ​:index​) ​%>​</li>
  <li>
 <%=​ link ​"​​Log out"​,
 to:​ Routes.session_path(@conn, ​:delete​, @current_user),
 method:​ ​"​​delete"​ ​%>
  </li>
 <%​ ​else​ ​%>
  <li>​<%=​ link ​"​​Register"​, ​to:​ Routes.user_path(@conn, ​:new​) ​%>​</li>
  <li>​<%=​ link ​"​​Log in"​, ​to:​ Routes.session_path(@conn, ​:new​) ​%>​</li>
 <%​ ​end​ ​%>
  </ul>
 </nav>

Clicking My Videos routes a logged-in user directly to VideoController.index action.

This action is restricted to the current user, thanks to our scoping rules in the controller. In fact, there’s no public URL we can share with our friends when it comes to watching videos. Let’s address this by creating a WatchController for watching user videos, available to any user. Create a new lib/rumbl_web/controllers/watch_controller.ex file and key this in:

 defmodule​ RumblWeb.WatchController ​do
 use​ RumblWeb, ​:controller
 
  alias Rumbl.Multimedia
 
 def​ show(conn, %{​"​​id"​ => id}) ​do
  video = Multimedia.get_video!(id)
  render(conn, ​"​​show.html"​, ​video:​ video)
 end
 end

Now, let’s create a new template directory for the controller in lib/rumbl_web/templates/watch and add a new show.html.eex template file with these contents:

 <div class=​"row"​>
  <div class=​"column column-60"​>
  <h1>​<%=​ @video.title ​%>​</h1>
 <%=​ content_tag ​:div​, ​id:​ ​"​​video"​,
 data:​ [​id:​ @video.id, ​player_id:​ player_id(@video)] ​do​ ​%>
 <%​ ​end​ ​%>
  </div>
 
  <div class=​"column annotations"​>
  <h3>Annotations</h3>
 
  <div id=​"msg-container"​>
  </div>
 
  <div>
  <textarea id=​"msg-input"
  rows=​"3"
  placeholder=​"Comment..."​></textarea>
 
  <button id=​"msg-submit"​ class=​"button column"
  type=​"submit"​>
  Post
  </button>
  </div>
  </div>
 </div>

The template is mostly markup, with the exception of the title and the video div, which includes the id, data-id, and data-player-id attributes. We extract the player ID from the video url field by a function aptly named player_id. Since templates are just functions in the view module, the view is the perfect place to define such a function.

Create a new lib/rumbl_web/views/watch_view.ex and make it look like this:

 defmodule​ RumblWeb.WatchView ​do
 use​ RumblWeb, ​:view
 
 def​ player_id(video) ​do
 ~​r{^.*(​?:​youtu.be/|w+/|v=)(​?<​id>[^​#&?]*)}
  |> Regex.named_captures(video.url)
  |> get_in([​"​​id"​])
 end
 end

Unfortunately, YouTube URLs come in a variety of formats. We need a regular expression to extract the video ID from the URL. Regular expressions are beyond the scope of this book, but here are the basics. A regular expression[26] uses patterns to match specific patterns within strings. We’re naming a pattern called id and then piping our expression into a function called named_captures, which extracts the id field given our URL name. Then, we build a map that returns the id key with its value.

Finally, let’s add an entry to our router’s :browser pipeline to the new WatchController:

 scope ​"​​/"​, RumblWeb ​do
  pipe_through ​:browser​ ​# Use the default browser stack
 
  get ​"​​/"​, PageController, ​:index
  resources ​"​​/users"​, UserController, ​only:​ [​:index​, ​:show​, ​:new​, ​:create​]
  resources ​"​​/sessions"​, SessionController, ​only:​ [​:new​, ​:create​, ​:delete​]
  get ​"​​/watch/:id"​, WatchController, ​:show
 end

Now let’s change the link for each entry in the My Videos page to point to watch instead of show. Open up lib/rumbl_web/templates/video/index.html.eex and replace show with this:

 <table>
  <thead>
  <tr>
  <th>Title</th>
  <th></th>
  <th></th>
  <th></th>
  </tr>
  </thead>
  <tbody>
 <%=​ for video <- @videos ​do​ ​%>
  <tr>
  <td>​<%=​ video.title ​%>​</td>
  <td>​<%=​ link ​"​​Edit"​, ​to:​ Routes.video_path(@conn, ​:edit​, video) ​%>​</td>
  <td>
 <%=​ link ​"​​Delete"​, ​to:​ Routes.video_path(@conn, ​:delete​, video),
 method:​ ​:delete​,
 data:​ [​confirm:​ ​"​​Are you sure?"​] ​%>
  </td>
  <td>
 <%=​ link ​"​​Watch"​, ​to:​ Routes.watch_path(@conn, ​:show​, video),
 class:​ ​"​​button"​ ​%>
  </td>
  </tr>
 <%​ ​end​ ​%>
  </tbody>
 </table>

First, we changed the table header to simplify the listing. We trimmed the headings to only the title with three empty headings for our edit, delete, and watch links. To match, we removed our url and description columns. We used the Routes.watch_path helper generated by the new route.

Not much exciting is happening here but this early preparation will lead to a great fireworks show later. Now, things will start to get a little more interesting. Let’s add the JavaScript required to let us watch videos.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.232.11