Deprecation notice: The API endpoints mentioned in this tutorial will be disabled during the spring 2021.

Playing a program

Information needed to complete the steps in this tutorial:

  • app_id and app_key credentials (you should’ve received these by email)
  • Media URL decryption secret - this should also be in your email
  • Programs: bash, curl, python, QuickTime

Step 1: Get program details by ID

First, we must find a program we want to watch or listen. To do this, use YLE Areena to find a program you like. For this little tutorial, we’ve chosen this episode to act as an example. Then, grab the ID from the URL - it’s the part that looks like 1-XXXXXXX. In our example, it’s 1-820561

Unfortunately at the moment we've had to limit access to some content (most notably DRM protected content). If you're having problems completing this tutorial, please try again with different program. Also see documentation on detecting media's availability.

Now, let’s make our first API request to retrieve the program details:

curl -v "https://external.api.yle.fi/v1/programs/items/1-820561.json?app_id=YOUR_APP_ID&app_key=YOUR_APP_KEY" \
     | python -mjson.tool

If this produced an error, please make sure that you actually got an active program ID from Areena, as described at Step 1.

Okay, yes, that’s quite large wall of text you have in front of you. No need to get scared, though. What’s important here for our purposes are the publication events. Programs come and go. Sometimes, a program might be available for a month and is then removed from Areena. After a year the same program might get published again. We store a new publication event each time a program is published.

Step 2: Finding the correct publication event

You can get the publication event history from the program’s publicationEvent array. This array contains all publication events the program has had, but for now, you should be concentrating only on the one with following properties:

temporalStatus == "currently" and type == "OnDemandPublication"

NOTE: (ScheduledTransmission type means the program is consumable from a TV or radio channel at the specified time.)

When these conditions match, it means the program is available from Areena and it’s media can be retrieved for watching.

In our example, the available publication event looks like this:

{
  "id": "4-4359156",
  "temporalStatus": "currently",
  "type": "OnDemandPublication",
  "startTime": "2015-04-28T10:00:00+03:00",
  "endTime": "2020-04-30T23:59:59+03:00",
  "region": "Finland",
  "duration": "P5Y2DT13H59M59S",
  "media": {
    "downloadable": false,
    "type": "VideoObject",
    "available": true,
    "contentProtection": [
      {
        "type": "ContentProtectionPolicy",
        "id": "22-1"
      }
    ],
    "duration": "PT1H26M",
    "id": "6-8e9d45c1221544f3be76394fa1a6a102"
  },
  "service": {
    "id": "yle-areena"
  },
  "publisher": [
    { "id": "yle-areena" }
  ]
}

Again, the relevant part here is the media Object and most notably it’s id. Also, if you aren’t watching the program from Finland, you should make sure region == "World". Only handful of videos are available for viewing outside of Finland for licensing reasons.

Step 3: Retrieving media URL

Now, using the combination of program id and media ID, you can retrieve URL to the media. You also must provide the protocol, in our example we use HLS (HTTP Live Streaming) but you can alternatively use HDS (HTTP Dynamic Streaming).

curl -v "https://external.api.yle.fi/v1/media/playouts.json?program_id=1-820561&media_id=6-8e9d45c1221544f3be76394fa1a6a102&protocol=HLS&app_id=YOUR_APP_ID&app_key=YOUR_APP_KEY" \
     | python -mjson.tool

If this produced an error, please make sure that you actually got an active program ID from Areena, as described at Step 1.

Unfortunately at the moment we’ve had to limit access to some content (most notably news content and DRM protected content). If you get a 403 error with correct app keys, please try again with different program. Also see documentation on detecting media’s availability.

Once again you get a json file containing information about the media such as width and height, but the most relevant part is the url field

WMkQT+/vPKp/IFGbv5vQfS/BzY0GEAy2SjZWNQHm4JWmzS438ffs++ntWPYeNDAUy4W/FuFA9PN2fS7BuAsuQfnJIBSv7PT2x8zxqI2mxjzQUSdCeoAblwn9Wy/weEi8LpnDyOsy+SmnfFRgeiQ98viY/bQZ5z6d06k86QuGrB2JB3M18uuiVUXWPuG/gz4s22A218tuvAEf5HMWDaY0xTIyqhk/CNXgX9e9KwsPnPisp3xomjJ7vNPlhXx/xLMK2I1Kxeq01ciolXks5SWJAsOAfRoWxWhivoUCniLsXxmcuUYhWqiKQJYgr52YGd0yyvtk85PaakVrTE/BOBhKyhKkEjYJX9Qf1UEiPFE6qBdSuBwGd4Ixdpg9m+mHS8D8/CfmMk1dPs/guHDXjAMshA==

NOTE: This URL won’t work for you, because it’s encrypted using different secret than you have. Please make the above REST call yourself when trying out the API.

Admittedly, that’s one strange looking url. The reason is that the url is encrypted (to protect illegal copying of the content) and we need to decrypt it to use it.

Step 4: Decrypting the media URL

You can use this handy tool below to decrypt the URL:

Encrypted URL: Secret:

Decrypted url

The JavaScript implementation is also available here as a reference implementation. But, for the sake of it, here’s the step-by-step instructions to decrypt the URL by yourself:

  1. First, base64 decode the encrypted url

  2. Then take the first 16 bytes of the decoded url as $IV and the rest as $MESSAGE

  3. Create a cipher with AES/CBC/PKCS7Padding (AES/CBC/PKCS5Padding in JVM based languages)

  4. Initialize the cipher in decrypt mode with your $SECRET and $IV

  5. Decrypt the $MESSAGE using the cipher

Here is an example in Java:

import javax.crypto.Cipher;
import javax.crypto.spec.*;
import java.util.Base64;
import java.util.Arrays;

public class Decrypt {

    public static void main(String args[]) throws Exception {
  	    String secret = "this_is_your_secret";
	    String data = "this_is_the_url";
	    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

	    byte[] baseDecoded = Base64.getDecoder().decode(data);
	    byte[] iv = Arrays.copyOfRange(baseDecoded, 0, 16);
	    byte[] msg = Arrays.copyOfRange(baseDecoded, 16, baseDecoded.length);

	    SecretKeySpec secretKeySpec = new SecretKeySpec(secret.getBytes("UTF-8"), "AES");
	    IvParameterSpec ivSpec = new IvParameterSpec(iv);

	    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
	    byte[] resultBytes = cipher.doFinal(msg);
	    System.out.println(new String(resultBytes));
	}
}

Step 5: Play the file!

Now, just copy paste the URL to QuickTime. (File -> Open Location on OS X) and hit Open to play the video.

Alternatively you can test HDS streams using Akamai test player

NOTE: If the video doesn’t work, try getting a new encrypted url and decrypt it again. The download link expires shortly after it’s created, if it isn’t consumed.

Step 6: Report your usage

Now we ask you to report your usage. Each time you or one of your users start playing a video, we’d like to know about that. There’s a simple way to do it:

curl -v "https://external.api.yle.fi/v1/tracking/streamstart?program_id=1-820561&media_id=6-8e9d45c1221544f3be76394fa1a6a102&app_id=YOUR_APP_ID&app_key=YOUR_APP_KEY"

Please call the API only the first time per each page load. If you pause, and then resume watching, you shouldn’t call the API again. But if the user refreshes the page and starts playing again, you should report a new stream start.

Step 7: What now?

Whew, now you’re finished with the tutorial. So, what now? Well, the rest is up to you.

Thanks for reading this tutorial!