Playing with YouTube from Mathematica


Today I’m gonna quickly show off something I made for a friend (and about which I’d been thinking for some time). We’re gonna use the YouTube API in Mathematica to do a have some fun (but not too much fun, mind you!).

Installing the YouTube Connection

My connection to the YouTube API is built on Mathematica’s ServiceConnect framework. I’ve talked about these before in many different contexts but today we’ll just use it. To install we pull the paclet off my server :

PacletInstall["ServiceConnection_YouTube",
  "Site"->
    "http://www.wolframcloud.com/objects/b3m2a1.paclets/PacletServer/"
  ]

(*Out:*)

post22-8529256040837869960

Authenticating with YouTube

One installed, we connect to the service as usual:

$so = ServiceConnect["YouTube"]

This will open a dialog:

post22-2534758878895670558

We’ll click the sign in button, pick and account (I’ll use my new personal account steve.wolfraum@gmail.com ):

post22-6891487155877384696

When we click “Allow” either we’ll be taken to a nicely formatted local page with an access code running via HTTPHandling`StartWebServer or if that’s being glitchy as it was right now, we’ll copy the part of the URL starting with "code=4/..." :

post22-5755985516881850876

Finally, we’re connected:

(*Out:*)

post22-6049558537553480280

Getting Data

The majority of the currently supported methods have to do with getting data about YouTube:

$so["Requests"]

(*Out:*)

{"Authentication","DeleteVideo","ID","Information","LastRequest","ListCaptions","ListChannelActivity","ListChannels","ListChannelSections","ListComments","ListCommentThreads","ListPlaylistItems","ListPlaylists","ListSubscriptions","ListVideos","Name","RawRequests","RequestData","RequestParameters","Search","UpdateVideo","UploadVideo","VideoURL"}

We’ll start with a search, maybe. First let’s see what parameters we have to work with:

$so["RequestParameters", "Request"->"Search"]

(*Out:*)

<|"Parameters"->{"q","part","forContentOwner","forDeveloper","forMine","relatedToVideoId","channelId","channelType","eventType","location","locationRadius","maxResults","onBehalfOfContentOwner","order","pageToken","publishedAfter","publishedBefore","q","regionCode","relevanceLanguage","safeSearch","topicId","type","videoCaption","videoCategoryId","videoDefinition","videoDimension","videoDuration","videoEmbeddable","videoLicense","videoSyndicated","videoType"},"Required"->{"part"}|>

Then we’ll search for something bland, like “Steve Wolfraum”. The YouTube API requires this "part" parameter to know what to return to use. I like to get the "id" and "snippet" parts, but you can pick for yourself which parts you want. Just comma-separate your values and you can get multiple different ones:

mySearch = $so["Search", "q"->"Steve Wolfraum", "part"->"id,snippet"]

(*Out:*)

post22-4316213154658629458

We see there are tons of results for “my” name but we only get them five at a time. We can use the "nextPageToken" to get the next page of results, though. We can bump up the number we get with the "maxResults" parameter, but I see no need for that right now. Let’s look at the first result here. I’ll drill straight into that snippet parameter, as that’s the most interesting part:

mySearch["items", 1, "snippet"]

(*Out:*)

post22-498969337448306280

And we see, sadly, that instead of getting results for my name, Steve Wolfraum, we get them for some random dude name Stephen Wolfram. Not to be deterred, let’s at least get something out of this and see a thumbnail for all our results. Tragically, we can’t download videos with the YouTube API, but at least we get thumbnails…hooray? To make things more fun, we’ll also add a filter to all these results, as the raw thumbnails really aren’t all that interesting.

ImageAdjust@LaplacianGaussianFilter[Import[#], 3]&/@
  Normal@mySearch["items", All, "snippet", "thumbnails", "high", "url"]

(*Out:*)

post22-8416895123856568403

Cool. There’s some data. We can also pull out statistics and things, say for different videos broadcast on the Wolfram Research channel. First we’ll get their channel ID:

wriChannel=
  $so["Search", "q"->"Wolfram Research", "type"->"channel",
    "part"->"id", "maxResults"->"1"]["items", 1, "id", "channelId"]

(*Out:*)

"UCJekgf6k62CQHdENWf2NgAQ"

Now we’ll use this to extract the 50 most popular videos:

wriVids=$so["Search", "channelId"->wriChannel, "type"->"video", 
  "order"->"viewCount", "part"->"id,snippet", "maxResults"->"50"]

(*Out:*)

post22-5403531027782801587

And now we’ll get view counts:

wriStats=$so["ListVideos", 
  "id"->StringRiffle[Normal@wriVids["items", All, "id", "videoId"], ","], 
  "part"->"statistics"
  ]

(*Out:*)

post22-4181911101890639189

And then we’ll make a WordCloud of titles vs viewcounts:

WordCloud@
  AssociationThread[
    Normal@wriVids["items", All, "snippet", "title"],
    ToExpression@Normal@wriStats["items", All, "statistics", "viewCount"]
    ]

(*Out:*)

post22-948349109694102953

And we see as far as YouTube cares, there’s really only one video WRI has made. But dropping that one we might see some more interesting structure:

WordCloud@
  AssociationThread[
    Rest@Normal@wriVids["items", All, "snippet", "title"],
    Rest@ToExpression@Normal@wriStats["items", All, "statistics", "viewCount"]
    ]

(*Out:*)

post22-978266547142309807

And it seems that people really like the basics and…really like seismic waves?

I’ll embed the first of those so we can see how good the really are:

$so["EmbedIFrame", "id"->wriVids["items", 3, "id", "videoId"]]

Uploading Videos

A YouTube API wouldn’t be much of a YouTube API if it couldn’t upload videos, of course, so I cooked that in as well.

Here’s a sample of that. We’ll first pull an animation off the Manipulate docs:

anim =
  Export[
    FileNameJoin@{$TemporaryDirectory, "plot3d.flv"},
    Manipulate[
      Plot3D[Sin[n x y], 
        {x, 0, 3}, {y, 0, 3}, 
        ViewPoint -> Dynamic[{2, v, 2}], SphericalRegion -> True, Ticks -> None,
        PerformanceGoal->"Quality"
        ], 
      {n, 1, 4}, 
      {v, -2, 2}
      ]
    ];

Then we’ll upload the video we made:

upload = $so["UploadVideo", "part"->"id", "BodyContent"->anim];

Then we attach a title and things in a second request:

$so["UpdateVideo", "id" -> upload["id"],
 "part" -> "snippet",
  "Title" -> "Mathematica Examples: Plot 3D",
 "CategoryID" -> "22"
 ]

(*Out:*)

post22-4324612576805415951

And then now we’ll embed that video we just made:

$so["EmbedIFrame", "id"->upload["id"]]