Adobe DTM + Adobe Analytics Media Module + YouTube = ❤️

Today, I’m happy to present the first ever guest post on webanalytics for developers! Written by Stefan Schäfer, it deals with DTM, the Media Module, and Youtube video. Have fun!

If you use Adobe DTM together with the Adobe Analytics Media Module, and if you can completely rely on autoTrack=true, you’re good. But what if you have embedded YouTube-Videos and need to tag them manually?

We recently had a project with a customer who had exactly this challenge, found the article in the help section (Adobe Marketing Cloud Help: How to track youtube videos using DTM?), followed it, and was not happy. I studied the article, and, good things first: It works! The bad thing: There is some information missing, and I would suggest a different approach.

Why the help section article does not work

Well, I said it does, but you need to keep five things in mind, and change your DTM configuration – and page code – to make it work.

A) Step 1, 2 and 3 are related to code on the page. But what about the code in step 4? “Please add the following code in AppMeasurement or s_code file” – We’re using DTM, this article is about DTM – so we should not have an AppMeasurement or s_code file. So: put this code into your Adobe Analytics tool in the Customize Page Code section, right above the doPlugins method.

B) Ehm, what about the Media Module core code? Needed, of course! And you have to put this code into your Adobe Analytics tool in the Library Management part. That means, you have to move away from Managed by Adobe, choose Custom and add the core code right before (your Activity Module and) the AppMeasurement Core Code. I would really need to have a checkbox saying [_] Include Media Module, which would still let the code be Managed by Adobe (what I love!).

C) JavaScript errors everywhere! Even though you leave the Tracker Variable Name as “s”, it still says it is undefined? And that’s true, because this variable is only used in DTM – not outside, not on your HTML page where you call s.Media within your YouTube code. Given the code of step 4, you should define “s” in your Customize Page Code section in the very first line (right before s.loadModule("Media");).

D) Define “s” correctly. If you use var s = new AppMeasurement();, Adobe Analytics creates a new instance, which might look okay – but you will realize some downsides, e.g. exit link tracking on every link and double download link tracking, because you didn’t define it in the instance that existed before this line.

Rather, use var s = _satellite.getToolsByType('sc')[0].getS();. This takes your existing instance, and puts it into s. Nice!

Note: this relates to an array of Adobe Analytics (= SiteCatalyst = ‘sc’) tools. It will always take the first one, so this code causes trouble if you have more than one Adobe Analytics tool (the order could change, and you cannot control it).

E) While all of the things above could be done in DTM, there is one requirement you need to keep in mind. The Adobe Analytics tool, and thus the definition of your variable “s”, which you need on your page, is guaranteed to be available on onload. If the YouTube stuff on your page loads quicker, even only for some users, the result is an JavaScript error. You might not be able to reproduce it, but you have to control your YouTube code on your page, and let it wait until onload.

A cleaner approach: Event Based Rules

I guess describing the downsides of the help section’s article were as boring for you as they were for me, so here is a smarter way, with some nice advantages:

  • Keep control of the Media tracking calls in DTM, the page code only provides triggers
  • You can deactivate the Adobe Analytics tool / the DTM property and your users won’t get JS errors
  • No need to let your YouTube player “wait” until onload. Users hate this (and so do I).
  • Compatibility with multiple Adobe Analytics tools in your property.
  • No need to explain what s.Media.open(), play(), stop() and close() is and when it should be set. Four small objects for your on-page implementation.

Let’s get this party started!

1. Code on your page: Drop the s.Media functions and use the Data Layer!

Remove all the s.Media stuff you had on your site, and implement those four JavaScript objects instead:

Video Opened

Set the variable when the video starts to play for the very first time. Set those variable as well, if the video ended completely and is replayed by the user.

digitalData.event.video.opened = {
    videoName: "myvideoname",
    totalTime: "120.00",
    playerName: "YouTube Player"
};

The videoName is the name of the video, the totalTime is the total duration of the video in seconds (example: 2 minutes), and the playerName is the name of your video player.

The object’s name relies to the W3C standard for a data layer. If you don’t know how to set such variables, you could also use a something easier like video_opened = { ... }; etc.

Video Paused

Set those variables when your opened video is paused by the user.

digitalData.event.video.paused = {
    videoName: "myvideoname",
    currentTime: "10.00"
};

The currentTime is the time when the user paused it. In this case, the video is paused after 10 seconds.

Video Resumed

Set those variables when the previously paused video is resumed.

digitalData.event.video.resumed = {
    videoName: "myvideoname",
    currentTime: "30.00"
};

In this example, a user paused the video after 10 seconds, slided to second 30, and resumed it then.

Video Closed

Set those variables when the video finally ends – when it reached 100%, or if the user navigates away from your page.

digitalData.event.video.closed = {
    videoName: "myvideoname",
    currentTime: "120.00"
};

The current time will most probably match the totalTime you set in the Opened variable. In some special cases, you might have the chance to set this object when the user leaves the page playing a video.

Undefine the variables in some cases
  • When the video is closed, check that digitalData.event.video.opened is set to undefined.
  • Vice versa: When the video is opened again (replayed), check that digitalData.event.video.closed is set to undefined.
  • When the video is resumed, check that digitalData.event.video.paused is set to undefined.
  • Vice versa: When the video is paused, check that digitalData.event.video.resumed is set to undefined.

2. DTM: Add the Media Module and load it

Follow the steps A) and B) above, we still need this.

  1. Add the Media Module core code to the Library Management
  2. and load it in the Customize Page Code section with the code from step 4 of the help section.

3. DTM: Improve the loading of the Media Module

This is nice to have, but as we want the clean approach:

  1. Switch from console.log("message"); to _satellite.notify("message"); to keep your console clean. You will still see this in debug mode.
  2. Put the s.Media.monitor stuff, which is only for debugging, into a condition to check if (_satellite.settings.isStaging == true) { ... };

4. DTM: Create your Data Elements, so that DTM can watch the change

Create four Data Elements as JS Object, with Name and Path equal and no Default Value:

  • digitalData.event.video.opened
  • digitalData.event.video.paused
  • digitalData.event.video.resumed
  • digitalData.event.video.closed
bildschirmfoto-2017-01-19-um-16-56-00
Data Element: Video Opened

5. Make DTM react to changes

For the last step, we need four Event Based Rules. I simply call them “Video Opened”, “Video Paused”, “Video Resumed” and “Video Closed”.

Condition: Choose dataelementchanged as Event Type, and watch the previously created Data Element (digitalData.event.video.opened for your Video Opened rule, and so on).

Adobe Analytics: We need to put our code in the Custom Page Code > Open Editor section, as you can’t use instance “s” in the JavaScript Tag part. DTM ignores the code if you switch Tracking to “disabled”, so I choose s.tl(); and give it a link name like “video_opened”. We will stop the link tracking call from firing, so the link name is just for debugging if something breaks.

Now leave everything else blank, and add the code:

Video Opened
/* Required Syntax:
digitalData.event.video.opened.videoName
digitalData.event.video.opened.totalTime
digitalData.event.video.opened.playerName
*/

if (typeof s === "object" && typeof s.Media === "object")
{
    if (typeof _satellite.getVar("digitalData.event.video.opened") === "object")
    {
        var mediaName = _satellite.getVar("digitalData.event.video.opened").videoName;
        var mediaLength = _satellite.getVar("digitalData.event.video.opened").totalTime;
        var mediaPlayerName = _satellite.getVar("digitalData.event.video.opened").playerName;
        var mediaOffset = "0"; // Assume, video starts at the beginning

        if (typeof mediaName === "string" && typeof mediaLength === "string" && typeof mediaPlayerName === "string" && typeof mediaOffset === "string")
        {
            s.Media.open(mediaName,mediaLength,mediaPlayerName);
            _satellite.notify("Adobe Analytics: s.Media.open(\""+mediaName+"\",\""+mediaLength+"\",\""+mediaPlayerName+"\");");
            s.Media.play(mediaName,mediaOffset);
            _satellite.notify("Adobe Analytics: s.Media.play(\""+mediaName+"\",\""+mediaOffset+"\");");
        } else {
            _satellite.notify("Adobe Analytics: s.Media.open failed - digitalData.event.video.opened does not have the sub-elements videoName, totalTime, playerName as strings",4);
        }
    } else {
        _satellite.notify("Adobe Analytics: s.Media.open failed - digitalData.event.video.opened is no object",4);
    }
} else {
    _satellite.notify("Adobe Analytics: Media Module failed - not yet loaded",4);
}

s.abort = true; // Discard link tracking

The code assumes that the video is started at second 0. You can easily add this to the JS variable if you need to.

Video Paused
/* Required Syntax:
digitalData.event.video.paused.videoName
digitalData.event.video.paused.currentTime
*/

if (typeof s === "object" && typeof s.Media === "object")
{
    if (typeof _satellite.getVar("digitalData.event.video.paused") === "object")
    {
        var mediaName = _satellite.getVar("digitalData.event.video.paused").videoName;
        var mediaOffset = _satellite.getVar("digitalData.event.video.paused").currentTime;

        if (typeof mediaName === "string" && typeof mediaOffset === "string")
        {
            s.Media.stop(mediaName,mediaOffset);
            _satellite.notify("Adobe Analytics: s.Media.stop(\""+mediaName+"\",\""+mediaOffset+"\");");
        } else {
            _satellite.notify("Adobe Analytics: s.Media.stop failed - digitalData.event.video.paused does not have the sub-elements videoName, currentTime as strings",4);
        }
    } else {
        _satellite.notify("Adobe Analytics: s.Media.stop failed - digitalData.event.video.paused is no object",4);
    }
} else {
    _satellite.notify("Adobe Analytics: Media Module failed - not yet loaded",4);
}

s.abort = true; // Discard link tracking
Video Resumed
/* Required Syntax:
digitalData.event.video.resumed.videoName
digitalData.event.video.resumed.currentTime
*/

if (typeof s === "object" && typeof s.Media === "object")
{
    if (typeof _satellite.getVar("digitalData.event.video.resumed") === "object")
    {
        var mediaName = _satellite.getVar("digitalData.event.video.resumed").videoName;
        var mediaOffset = _satellite.getVar("digitalData.event.video.resumed").currentTime;

        if (typeof mediaName === "string" && typeof mediaOffset === "string")
        {
            s.Media.play(mediaName,mediaOffset);
            _satellite.notify("Adobe Analytics: s.Media.play(\""+mediaName+"\",\""+mediaOffset+"\");");
        } else {
            _satellite.notify("Adobe Analytics: s.Media.play failed - digitalData.event.video.resumed does not have the sub-elements videoName, currentTime as strings",4);
        }
    } else {
        _satellite.notify("Adobe Analytics: s.Media.play failed - digitalData.event.video.resumed is no object",4);
    }
} else {
    _satellite.notify("Adobe Analytics: Media Module failed - not yet loaded",4);
}

s.abort = true; // Discard link tracking
Video Closed
/* Required Syntax:
digitalData.event.video.closed.videoName
digitalData.event.video.closed.currentTime
*/

if (typeof s === "object" && typeof s.Media === "object")
{
    if (typeof _satellite.getVar("digitalData.event.video.closed") === "object")
    {
        var mediaName = _satellite.getVar("digitalData.event.video.closed").videoName;
        var mediaOffset = _satellite.getVar("digitalData.event.video.closed").currentTime;

        if (typeof mediaName === "string" && typeof mediaOffset === "string")
        {
            s.Media.stop(mediaName,mediaOffset);
            _satellite.notify("Adobe Analytics: s.Media.stop(\""+mediaName+"\",\""+mediaOffset+"\");");
            s.Media.close(mediaName);
            _satellite.notify("Adobe Analytics: s.Media.close(\""+mediaName+"\");");
        } else {
            _satellite.notify("Adobe Analytics: s.Media.close failed - digitalData.event.video.closed does not have the sub-elements videoName, currentTime as strings",4);
        }
    } else {
    _satellite.notify("Adobe Analytics: s.Media.close failed - digitalData.event.video.closed is no object",4);
    }
} else {
    _satellite.notify("Adobe Analytics: Media Module failed - not yet loaded",4);
}

s.abort = true; // Discard link tracking

Finally – you’re done!

Phew. That definitely took some time to set things up, but most of this is copy&paste work and you can reproduce it easily on all your sites. Media Module in the Adobe Analytics tool, four Data Elements, four Event Based Rules. Well, the downside of not being able to use the core code “Managed by Adobe” remains, but that’s given in every combination of Adobe DTM with the Adobe Analytics Media Module, as long as Adobe hasn’t implemented checkbox to add the core code.

I hope this approach helps you improve your implementation and makes it easier to set the code on the page. If you have some more ideas to add, put them in the comment section! 🙂

7 thoughts on “Adobe DTM + Adobe Analytics Media Module + YouTube = ❤️

  1. I tried finding some information on tools to identify debugging tags in general and it’s a little tricky- but was curious, does anybody know of a tool or code snippet out there to identify all 3rd party tracking pixel implementations within a site? To really nail it though – I am looking for some way that is less manual (rather than going through every page template through the site of about 30 different template styles, and that is just ONE site, whereas we have about 18)…. Basically I’m trying to track down all of the old, unused tags, or tracking pixels that are bogging down the page load, taking up unnecessary space, and that are not needed anymore. I’ve looked into observe point, tagtician, among others – but is there any out there that are specialized for this in particular that anybody knows about? If it has the ability to crawl/extract would be great (but I dont think this is ideal or effective since some may load at different times if I were guessing). We have to have anywhere from 30-40 pixels/tags for various vendors whether old or new and I’m simply curious if anyone knows of the best way to get this. Any information would be helpful, because I apparently need to get filenames of them and to try and identify pages that they are living on and they vary from certain pages like homepage, category, product page and cart… Thanks in advance!

    Like

    1. I figured this out, disregard please. I also was able to install maven, git, and phantomjs and ran my first test and am playing with the online simulator/editor for the test description file!!! Now, to get it onto the rasberry pi. Super easy to setup! Great guide.

      Like

  2. Hm, apart from not having an “s” object I don’t see the advantage of this approach, at least because the YT plugin is working quite smoothly and your approach leaves the client dvelopers needing to detect the YT video events.
    The YT plugin just requires the “id” attribute in the iframe tag and the GET param “enablejsapi=true” and then ou’re done! With a little tewak you can even get the total video time into the eVar.
    My latest approach was to have just one video eVar in this format:
    |||
    Sample:
    “YouTube|ABC123|myVideoName|120
    The video module is even capable to track multiple video interactions like video #1 start, video #2 start, video #1 milestone #1, video #1 milestone #2, video #2 milestone #1 etc.

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.