Dec
03
2010

Inserting a Featured Video into a Playlist

OK, I’m back after a long hiatus and am going to be (hopefully) posting a number of entries discussing some tricks and features in the Brightcove players that developers might not be aware of. This particular post comes from a customer request for a piece of functionality not built into the players by default.

The issue to address is that a “playlist player” delivered by Brightcove expects all of its content to be within a playlist (makes sense, right?), but sometimes an old link to a playlist player is requesting a video that is no longer a part of the playlist. We’ve had a lot of internal discussions around this, and the current behavior is that a featured video will only appear in the player if it exists in the playlist, as opposed to a) loading the featured video within the video window without giving a way to navigate back to it if a new video is selected, since the video is not the playlist UI, or b) inserting the video into the playlist so that it can be played and navigated to like any other video in the playlist. Although b) might seem an obvious solution (I think a) is just potentially a bad user experience), it was felt that making an assumption that a video should be inserted into a playlist was not a good thing — the playlist might be something completely different and the video being included within it could be anything from a humorous grouping to an offensive inclusion.

However, it has still been a common request from publishers. So here I’ll offer a simple plugin solution that will perform this behavior, checking to see if a featured video was requested via the link and, if it was and the video was not loaded, requesting the video from the backend and inserting it into the loaded playlist. Here is a player that just includes a playlist of my Bobblehead help videos. But the link references the video ID for a logo animation video. The default player behavior would be to ignore this video, but with the plugin the logo animation is prepended to the playlist.

Featured Video Plugin Test

First off, you will want to download the Player API SWC if you don’t have a copy. Then fire up Flash Builder or whatever IDE you use and start a new ActionScript project, making sure to include the API SWC. Alter the main application to extend CustomModule as opposed to Sprite, and you’ll start with something like this:

1
2
3
4
5
6
7
8
9
package {

    import com.brightcove.api.CustomModule;

    public class FeaturedVideoPlugin extends CustomModule {

    }

}

The first thing to do in the class, just as whenever using CustomModule, is to override the protected inititalize() method. Within this we will first check to see if we have either a List or a TileList to deal with, and exit if not. Then we can use getPlayerParameter() to see if a video id was included with the link.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
override protected function initialize():void {
    _experienceModule = player.getModule(APIModules.EXPERIENCE) as ExperienceModule;

    // first, see if we have a List component
    _list = _experienceModule.getElementByID("videoList") as List;
    // if not, try a TileList component
    if (!_list) {
        _list = _experienceModule.getElementByID("videoList") as TileList;
    }
    // no list, so nothing to do
    if (!_list) return;

    // check to see if a video was requested in the link
    var featuredVideo:String = _experienceModule.getPlayerParameter("@videoPlayer");
    // no video, so nothing to do
    if (!featuredVideo) return;
    // more to come...
}

If a featured video is found, we can go through the data in the list, using the ListWidget’s getData() and see if the video can be found, by either the id or referenceID, depending on what was included in the link.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// see if we have a referenceID or a a regular id
var referenceID:String;
var id:Number;
if (featuredVideo.substr(0, 4) == "ref:") {
    referenceID = featuredVideo.substr(4);
} else {
    id = Number(featuredVideo);
    if (isNaN(id)) {
        referenceID = featuredVideo;
    }
}
var useID:Boolean = !isNaN(id);

// now go through the videos to see if the featured video was included
_videos = _list.getData();
for each (var video:VideoDTO in _videos) {
    if (useID) {
        if (video.id == id) {
            return;
        }
    } else {
        if (video.referenceId == referenceID) {
            return;
        }
    }
}

// more to come...

If the video is not found in the list, we must request it through the ContentModule. Also, we’ll set up a listener to stop the video that was loaded on player load.

1
2
3
4
5
6
7
8
9
10
11
12
13
// the video player will start playing the first in the list,
// so make sure to stop the video if that happens
_videoPlayer = player.getModule(APIModules.VIDEO_PLAYER) as VideoPlayerModule;
_videoPlayer.addEventListener(MediaEvent.BEGIN, onMediaBegin);

// now request the featured video and wait for the response
_contentModule = player.getModule(APIModules.CONTENT) as ContentModule;
_contentModule.addEventListener(ContentEvent.MEDIA_LOAD, onMediaLoad);
if (useID) {
    _contentModule.getMediaAsynch(id, "id");
} else {
    _contentModule.getMediaAsynch(referenceID, "referenceId");
}

The two handlers are pretty simple. onMediaBegin() stops playback of the video that was loaded with the player. onMediaLoad() inserts the featured video, once loaded, at the start of the list’s array of videos, then passes that data back to the list for rendering.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Pauses the video if it starts before featured video is returned.
*/

private function onMediaBegin(event:MediaEvent):void {
    _videoPlayer.removeEventListener(MediaEvent.BEGIN, onMediaBegin);
    _videoPlayer.pause(true);
}

/**
* Adds featured video to playlist.
*/

private function onMediaLoad(event:ContentEvent):void {
    _videoPlayer.removeEventListener(MediaEvent.BEGIN, onMediaBegin);
    _contentModule.removeEventListener(ContentEvent.MEDIA_LOAD, onMediaLoad);

    // if we have a video, add it to the beginning of the playlist
    var video:VideoDTO = event.media as VideoDTO;
    if (video) {
        _videos.unshift(video);
        _list.setData(_videos);
        _list.setSelectedIndex(0);
    }
}

Here is the entirety of the class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package {

    import com.brightcove.api.APIModules;
    import com.brightcove.api.CustomModule;
    import com.brightcove.api.components.List;
    import com.brightcove.api.components.ListWidget;
    import com.brightcove.api.components.TileList;
    import com.brightcove.api.dtos.VideoDTO;
    import com.brightcove.api.events.ContentEvent;
    import com.brightcove.api.events.MediaEvent;
    import com.brightcove.api.modules.ContentModule;
    import com.brightcove.api.modules.ExperienceModule;
    import com.brightcove.api.modules.VideoPlayerModule;

    public class FeaturedVideoPlugin extends CustomModule {

        private var _experienceModule:ExperienceModule;
        private var _contentModule:ContentModule;
        private var _videoPlayer:VideoPlayerModule;
        private var _list:ListWidget;
        private var _videos:Array;
       
        override protected function initialize():void {
            _experienceModule = player.getModule(APIModules.EXPERIENCE) as ExperienceModule;

            // first, see if we have a List component
            _list = _experienceModule.getElementByID("videoList") as List;
            // if not, try a TileList component
            if (!_list) {
                _list = _experienceModule.getElementByID("videoList") as TileList;
            }
            // no list, so nothing to do
            if (!_list) return;

            // check to see if a video was requested in the link
            var featuredVideo:String = _experienceModule.getPlayerParameter("@videoPlayer");
            // no video, so nothing to do
            if (!featuredVideo) return;

            // see if we have a referenceID or a a regular id
            var referenceID:String;
            var id:Number;
            if (featuredVideo.substr(0, 4) == "ref:") {
                referenceID = featuredVideo.substr(4);
            } else {
                id = Number(featuredVideo);
                if (isNaN(id)) {
                    referenceID = featuredVideo;
                }
            }
            var useID:Boolean = !isNaN(id);

            // now go through the videos to see if the featured video was included
            _videos = _list.getData();
            for each (var video:VideoDTO in _videos) {
                if (useID) {
                    if (video.id == id) {
                        return;
                    }
                } else {
                    if (video.referenceId == referenceID) {
                        return;
                    }
                }
            }

            // the video player will start playing the first in the list,
            // so make sure to stop the video if that happens
            _videoPlayer = player.getModule(APIModules.VIDEO_PLAYER) as VideoPlayerModule;
            _videoPlayer.addEventListener(MediaEvent.BEGIN, onMediaBegin);

            // now request the featured video and wait for the response
            _contentModule = player.getModule(APIModules.CONTENT) as ContentModule;
            _contentModule.addEventListener(ContentEvent.MEDIA_LOAD, onMediaLoad);
            if (useID) {
                _contentModule.getMediaAsynch(id, "id");
            } else {
                _contentModule.getMediaAsynch(referenceID, "referenceId");
            }
        }
       
        /**
        * Pauses the video if it starts before featured video is returned.
        */

        private function onMediaBegin(event:MediaEvent):void {
            _videoPlayer.removeEventListener(MediaEvent.BEGIN, onMediaBegin);
            _videoPlayer.pause(true);
        }

        /**
        * Adds featured video to playlist.
        */

        private function onMediaLoad(event:ContentEvent):void {
            _videoPlayer.removeEventListener(MediaEvent.BEGIN, onMediaBegin);
            _contentModule.removeEventListener(ContentEvent.MEDIA_LOAD, onMediaLoad);

            // if we have a video, add it to the beginning of the playlist
            var video:VideoDTO = event.media as VideoDTO;
            if (video) {
                _videos.unshift(video);
                _list.setData(_videos);
                _list.setSelectedIndex(0);
            }
        }
       
    }

}

Once it is compiled, you can just include it as a plugin through the player settings in the Publishing module of the studio. I’ve also included a zip here of the whole project which includes the compiled SWF. If you don’t need to tweak it, all you have to do is host it and include it as a plugin for your player.

  • Share/Bookmark

Leave a Reply