A Better YouTube App Using LTI

K, so I promised to revisit the LTI workflow and how I think it's broken -- also how we fixed it in Canvas. Here's that revisit, as promised.

This is backwards, like LTI. Also like LTI, it's crafty in the middle.

In standard LTI there are two possible workflows:

1. Admin-installed, reusable launch. In this case someone high up installs an LTI app that teachers can use in their courses. The launch URL is generic, so when teachers add links to the app within their course material they then have to launch the app and go through an initial selection process.

This is backwards from what most people want to do. Nobody says, "hey, I want to add YouTube to my course! I'll figure out which video later…" It makes a lot more sense to pick the specific YouTube video you want and add *that* to your content.

Which brings us to…

2. Admin/teacher-installed, specific launch. The alternative is to specify when adding the resource what URL to launch. LTI allows for domain-level matching, so you can still have the admin install the app with a valid key and secret, and then teachers need to insert content-specific links that match this domain.

This is also an awkward workflow, albeit a little better. Essentially this is saying, "hey, I want to add a YouTube video to my course! I'll go over to YouTube, find the right video, copy the link and paste it here". Still not terribly elegant.

Which is why we've built some content extensions to LTI to support The Right Workflow. Using our extensions, the admin installs the app with a key and secret, and when the teacher goes to add a link to content from that app, a selection dialog appears that lets them browse/search content within that app, pick it, and have the linking part automatically taken care of. Now we can say "hey, I want to add a YouTube video to my course! I'll just open up this handy YouTube picker and find the video I want".

Here's how it works.

LTI launches come standard with a parameter I haven't mentioned before called launch_presentation_return_url. Basically when the LTI app has done doing its thing it can redirect the iframe to this URL and the LMS should know at that point that the app is done and do something about it (probably redirect to another page or close down the iframe).

All I've done is define some additional parameters which can be added to this return redirect that will tell the LMS that the user picked some content (we also send over a couple additional parameters to give the app some hints about what the user is expecting). The docs are here, but basically you look for selection_directive, and we have shortcuts for easily embedded links, images, lti launches and iframes, and for everything else it uses oembed.

As far as an implementation goes, we're talking about this:

# Handle POST requests to the endpoint "/lti_launch"
post "/lti_launch" do
  # do magical YouTube-y stuff  
  if params['selection_directive']
    # all the JavaScript needs to know is the redirect URL, it can take care of the rest
    render('selection_specific_page', params['launch_presentation_return_url'])
    return "This app doesn't support standard LTI launches, only content selection launches"

for the server. The JavaScript code changes as well to support the selection redirect:

// Remember the return_url so we know where to send the user once selection is complete
var returnUrl = decodeURIComponent(location.href.match(/return_url=([^&#]+)/)[1]);
if(!returnUrl) {
  returnUrl = "http://www.example.com";
  alert("No valid return URL provided! This tool will not work correctly!");
function returnVideo(video_id) {
  var url = "https://www.youtube.com/watch?v="  + video_id;
  returnUrl = returnUrl + (returnUrl.match(/\?/) ? "&" : "?") + "embed_type=url&title=Video&text=Video&url=" + encodeURIComponent(url);
  location.href = returnUrl;

Now it doesn't even require an LTI launch for the student to access the data (which seemed silly for YouTube anyway), just an LTI launch by the teacher at setup-time, then the tool returns a YouTube iframe and the LMS takes care of inserting it in the right place. We use this approach in Canvas to add buttons to the WYSIWYG editor, add options for homework submissions, and as an option in the module builder, but we'll be adding it more places in the future.

You may not have noticed, but we're now ignoring the LTI credentials like we did in the first example. It turns out that if your app only does content selection and doesn't support standard LTI, there's nothing that needs to be persisted to the database, and there's nothing to protect for an open app like a YouTube searcher. If you're still allowing standard LTI launches or if your content is protected, you should definitely still confirm the LTI signature using a preset key and secret.


Stephen said…
Thanks for this. I have a "video picker' placing a video from my streaming server onto a course page.
However as you state there is no LTI launch once the iframe src is set. Can you think of a way that the current viewer's login_id could be passed? My videos sometimes have viewing permissions set or we might want to track a users viewing time. Thanks.
Brian Whitmer said…
Not as an inline embed, unfortunately. If your page were added as an LTI link instead of an embedded iframe it would get the information, but I don't know of a way to get user information when they access an iframe generated via LTI.
Unknown said…
When I was trying to create an activity with Youtube, it was not showing any results for searches. It was stuck on Loading. So I hope this post can help me.
Emma Gamer Girl

Popular Posts