Name is required.
Email address is required.
Invalid email address
Answer is required.
Exceeding max length of 5KB

Adding custom buttons to the control bar


I'm working on a plugin that creates a tabbed interface within the player with viral sharing capabilities, such as emailing videos to friends, displaying embed codes, and links to social bookmarking sites. I originally developed it using the 3.x player and I'm currently porting it to 4.x using the fantastic new plugin interface.

My problem is, the sharing pane I'm developing is enabled/disabled with a custom button in the control bar, between the fullscreen and position bar. In the 3.x player, I achieved this by adding the button to mediaplayer.fla. I'd like to do the same, but if possible, avoid having to modify any of the "core" files such as Controlbar.as.

That way, we can safely upgrade to future versions of the 4.x player without having to reproduce all the additional graphics and extending of classes, which was required for the 3.x version.

Is this possible to do from an external plugin, or do I have to edit Controlbar.as to add the button?

36 Community Answers

Pablo

JW Player Support Agent  
0 rated :

It’s not the easiest thing to do in the world, but yes, it is possible to add a button to the control bar. You can access the control bar’s movie clip through the view.skin[‘controlbar’] property, manipulate its children and add new children. For example, here’s some code that inserts a new button to the right of the control bar:

public function initializePlugin(vie:AbstractView) : void {
	view = vie;
			
	var button : MovieClip = this.customButton;
	this.removeChild(button);

	var controlbar = view.skin['controlbar'];
	controlbar.addChild(button);

	controlbar['timeSlider'].width = controlbar['timeSlider'].width - button.width - 5;
			
	button.x = controlbar['timeSlider'].x + controlbar['timeSlider'].width;
	button.y = controlbar['timeSlider'].y;

}



If you do this however, you'll notice that the icon doesn't resize with the player.  In order to do that, you'll need to use the com.jeroenwijering.utils.Stacker class to resize the control bar containing your new button:
public function initializePlugin(vie:AbstractView) : void {
...
	stacker = new Stacker(controlbar);
	view.addControllerListener(ControllerEvent.RESIZE, resizeHandler);
}

private function resizeHandler(evt:ControllerEvent=null):void {
	var wid : Number = view.config['width'];
	trace(wid);
	stacker.rearrange(wid);
}

JW Player

User  
0 rated :

An alternative possibility is to make your own skin, and add the button to the controlbar in your skin. In that case, you still need to include a function similar to the Controlbar's setButtons function in your plugin, to set up events for your button. (which is also required in PabloS's solution) Should you want to hide your button, then you can do so in your plugin and call a stacker's rearrange function:
bc.. bar = view.skin['controlbar'];
if (bar['yourButton']) bar['yourButton'].visible = false;//hide yourButton
var stacker = new Stacker(bar);
stacker.rearrange(view.config['width']);


@PabloS
In the code as it is now, the stacker analyses the clip passed to it only when its constructor function is called. So, when I want to add buttons to the controlbar (as I think many developers will want to do when developing plugins), this is what happens when a resize event is fired:
- the controlbar's stacker's rearrange function is called, but the its stack does not contain the custum controlbar button, so it rearranges the controlbar, not taking this new button into account.
- a resizeHandler in the plugin also has to call stacker.rearrange(); this stacker's stack does contain the custom button, so now the controlbar is rearranged again, but now with the new button.
This means that for every button added to the controlbar, a rearrange function has to be called upon resize.

Wouldn't it be an idea to call the stacker's analyse() function everytime rearrange is called? In that case, the controlbar's stacker would examine the controlbar clip everytime, find any newly added buttons, and take them into account during the rearrangement.
When adding a new button to the controlbar as you have decribed above, then you only have to call the stacker.arrange once, and there won't be any need to add a resizeHandler to your plugin.

Nevertheless I'm really happy with your code sample above. I had postponed looking into this because I thought it would be difficult, but it seems really straightforward. Thanks!
Just one minor detail: in your code above I think 5 more pixels should be added to button's x property, otherwise it will be placed directly next to de slider.
bc.. button.x = controlbar['timeSlider'].x + controlbar['timeSlider'].width + 5;

Pablo

JW Player Support Agent  
0 rated :

@Jaron -

You’re exactly right about calling analyze on each resize event. That would eliminate the need to explicitly call rearrange a second time from within the plugin. I wanted to suggest a solution that would work without customizing the player though, so we have to make the redundant call. In either case, this solution is something of a hack – a potential problem could be if the Controller executes the plugin’s resize handler before the control bar’s handler (in which case, the control bar would be arranged correctly first, and then scrambled after).

It would be nice to have a more elegant, built-in solution for making changes to the control bar without having to manipulate the movie clips directly. Something like, “view.controlbar.addButton(buttonName, buttonIcon, clickHandler);” It would be really nice if this worked with any skin. Something to think about..

JW Player

User  
0 rated :

@pablo: I didn't mean to suggest that people should change the core code themselves. I thought you are one of the developers from longtail, so it was more like a suggestion for you/longtail to change in a next release.

I have just implemented pabloS's method, and it has some flaws:
1. in the resizeHandler, when switching to fullscreen, the margin is not taken into account, so the controlbar may be positioned to far to the right (the margin-property is not available outside Controlbar, but this can be circumvented by retrieving controlbar.x, which will be equal to margin in fullscreen mode). this can be solved like this:
bc.. var margin = controlbar.x;
var wid = view.config['width'] - 2*margin;
stacker.rearrange(wid);

2. there are more objects that may call stacker.rearrange. for example, the controlbar's itemHandler function also does this, and so does Playlist's playlistHandler. this means you have to add listeners in your plugin for *every *event which has any other listener whose eventHandler calls stacker.rearrange.
in the basic player setup, without any other plugins, it works for me when I add listeners for the ITEM and PLAYLIST ControllerEvents:
bc.. view.addControllerListener(ControllerEvent.ITEM,itemHandler);
view.addControllerListener(ControllerEvent.PLAYLIST,itemHandler);

But even then, it's still very well possible that other objects call their rearrange function on their own, not triggered by any event. This is sure to cause problems, so people at longtail, *please add the analyze function to stacker.rearrange!*

For the time being, I would recommend adding your buttons directly to the controlbar in a separate skin.

JW Player

User  
0 rated :

When I went to compile my plugin with the following modified code from PabloS:

bc.. public function initializePlugin(v:AbstractView):void {
view = v;
view.addControllerListener(ControllerEvent.RESIZE, resizeHandler);
view.addModelListener(ModelEvent.STATE, stateHandler);

// Add a custom button to the control bar
var button:MovieClip = this.shareButton;
this.removeChild(button);
var controlbar = view.skin['controlbar'];
controlbar.addChild(button);

controlbar['timeSlider'].width = controlbar['timeSlider'].width - button.width - 5;
button.x = controlbar['timeSlider'].x + controlbar['timeSlider'].width + 5;
button.y = controlbar['timeSlider'].y;

// Position the share panel
resizeHandler();
}



I got the following error:

bc.. 1119: Access of possibly undefined property shareButton through a reference with static type com.civicactions.sharepanel:SharePanel.


I did include a movie clip called *shareButton* in my plugin FLA.

@juaron
Because the player I'm developing is embedded on many websites, I have to do as much internally as possible. Is there much of a difference between developing on a skin, and editing the original FLA? Both seem pretty much the same, in terms of having to reproduce work when an upgrade is available.

JW Player

User  
0 rated :

this error may be triggered by this.shareButton (as that is the only reference I see to shareButton). Does your plugin's stage contain an mc called shareButton?
Furthermore, when you add a MovieClip using cotrolbar.addChild(button), the button mc is accessible through cotrolbar["button"]. I think it would be better to call it shareButton.

As to editing the original fla, you should avoid that as much as possible. When you develop a plugin or a skin, an the original player has an update, you only have to upload the new player.swf, and you shouldn't have to change anything about your skins or plugins. On the other hand when you do modify the original player.fla, you'll have to re-edit your code at every update.

Pablo

JW Player Support Agent  
0 rated :

@juaron -

Yes, I’m a LongTail developer, and absolutely, your suggestion to move analyze into Stacker’s rearrange function is a good one. My attempt here was to find a solution in the current player, so developers have a workaround while we’re waiting for the next release of the player (it’s coming – I promise!). Good find on the ITEM and PLAYLIST events, and the margin issue in full screen.

JW Player

User  
0 rated :

Yes, the stage contains an MC called shareButton. I was able to fix the error by changing the top line to refer to *clip*, a private reference to the main plugin MC, instead of *this*, like so:

bc.. var button:MovieClip = clip.shareButton;


I'm aware of the pitfalls of editing the core player.fla, that's the whole point of this thread... see my description at the top. I'm looking into creating a custom skin to go along with the plugin, since I'm finding a few issues working with PabloS' solution, such as the button overlapping the totalTime text field.

My question was, when I looked at the skinning tutorial at http://www.jeroenwijering.com/?item=Skinning_the_JW_Player and scrolled to the bottom to *Pre-compiling a skin* (because I can't edit all the embed codes to add the skin, I need it compiled into the swf), I can't see much of a difference between editing the *player* MC in a separate skin and copy-n-pasting it into player.fla, and just editing the *player* MC that's already in player.fla. Is there any difference in this case?

JW Player

User  
0 rated :

Ah. As I understand it, you already have a player embedded on the websites of other people, and now you want to upgrade to version 4.1, but you have no control over the embedcode people have used on their own pages.
The approach we usually take is to specify a configfile in the embedcode, and create that configfile dynamically. that way, you can keep control over the parameters of players embedded outside of your own websites. In your case though, I take it that in those embedcodes all parameters are specified as seperate flashvars?

I think I you have three possibilities: directly modify the player.fla, copy and paste your skin into the player.fla, or, in the defaults object in Player.as, you can set the skin property to your skin. Since I suppose you'll already have to change the plugins property of the defaults object, I would choose that approach instead of copying and pasting the skin. That will probably be the least work, and you stay as close to the usual skinning approach as possible.

Also, you mention that your button overlaps with the totalTime textfield. I encountered the same problem: when I added the button dynamically and called the staker's rearrange function all looked fine as long as the video wasn't started. When it started, the button and the totalTime overlapped. This is happens because at the starting of the video a ControllerEvent.ITEM is triggered, and the controlbars handler for that event rearranges the controlbar again. When I added a listener for that in my own plugin, it was fixed (see my post above).

JW Player

User  
0 rated :

First, thank you both SO MUCH for your help so far. It's been of great help and I wouldn't be making as much progress as I am without your comments.

At this stage, due to time constraints, I've added the buttons into player.fla and created a custom Player.as and Controlbar.as file that extends the base classes, in order to have as much control while keeping as close to the original codebase as possible. Things are working out for the first plugin, but now I have to add a total of 4 buttons to the controlbar, each as a separate plugin.

As I mentioned, I'm adding the buttons directly into the player MC in player.fla, not using the dynamic method, as I couldn't quite get that working (again, time constraints). I'm now running into the problem where I have the time slider overlapping several of the buttons, even though I'm calling stacker.rearrange() from every resizeHandler in every plugin.

At first, I thought I could simply resize the timeSlider MC, but I've found that resizing it by even a few pixels (either by scaling the MC, or modifying the individual shapes for the mark and rail MC's inside of timeSlider), the timeSlider shrinks to about half the width it should be! Furthermore, resizing timeSlider makes it stop resizing when the player is scaled. I searched through all the code and can't figure out why timeSlider only functions properly when it's 126 pixels wide.

Any thoughts?

JW Player

User  
0 rated :

The Stacker class which takes care of the rearrangement of controlbar buttons, checks every mc in the controlbar. When the element's width > playerwidth/3 (where I afaik the element's width is the width is has in your skin file and playerwidth is the width specified in your params), the element is scaled, otherwise it is not. What has worked for me, is to make my skin wider. That way I could fit in more buttons, but still make sure that the slider's width was still > playerWidth/3.

JW Player

User  
0 rated :

@ SamLerner: Any chance to see this plugin in action? Do you plan to share you viral plugin? Thanks in advance.



JW Player

User  
0 rated :

@ SamLerner: Any chance to see this plugin in action? Do you plan to share you viral plugin? Thanks in advance. +1

JW Player

User  
0 rated :

The plugin is for a client who's planning a big launch soon, I'll post a URL as soon as it looks presentable.

As for sharing it, the code is unfortunately very customized -- I had to modify some core AS files to get it working on time and on budget -- so I don't think it will be of much use to anyone at this point. Hopefully in the future I can contribute something, as I'm developing some pretty widely-requested functionality (searching for videos, related content, and video information in addition to the viral panel).

JW Player

User  
0 rated :

I think I'm not understanding something here. There is already an "initializePlugin" function in Controlbar.as, so I would assume you would not want to create another, but rather add to it. However, the code posted here shows the function terminating without any of the existing code. What's up with that?

JW Player

User  
0 rated :

Hi,

I try to use the player with the accessibility plugin, and I would like to display the buttons (audio, caption show/hide), but there's an error in the Controlbar addButton function now:
"TypeError: Error #1009: Cannot access a property or method of a null object reference." at Draw.clear(btn.icon);

(I've tried to debug in Flash CS4, but this is the only thing i got)
Please help!

JW Player

User  
0 rated :

The actual addButton method in AS3 is not working, because of the MovieClip cloning problem.
The work around: I've created a button movieclip manually (not with Draw.clone method) and set everything manually. It's not compatible with skins, but the origin player is working now.

JeroenW

JW Player Support Agent  
0 rated :

what exactly is going wrong with the movieclip cloning? Is that a Flash CS4 problem? It seems to work fine in CS3 for the plugins I used to add buttons to the stage.

JW Player

User  
0 rated :

Hi Jeroen,

Yes, it may be a CS4 problem, becuz i'm using it, and i have an error in the addButton function.

The movieclips are not cloneable, the cloned linkButton is an empty movieclip without width, height, etc...

JeroenW

JW Player Support Agent  
0 rated :

I’ll download a copy of CS4 and see what’s the issue with the cloning…

JW Player

User  
0 rated :

Will there be a tutorial or examples that will demonstrate adding new buttons and associated new functions to the control bar - especially when dealing with skins.

After reading the ticket #272 it would seem that it is now a little easier to do this.

JW Player

User  
0 rated :

Need help in customizing the controbar.

1.Can we add custom images to the control bar in 4.3 release?

2.Is there a way to modify the controlbar images other than modifying the skins or the player? Like adding images using flashvars?

Thanks..

JW Player

User  
0 rated :

i've been trying to implement a Rewind plugin, placing it just before the left side of the time slider (near play/pause/stop). I ended up changing the Controlbar.addButton( ) function call to allow for a specific button to place the plugin button before, instead of it being hard coded to 'linkButton'.

bc.. public function addButton(icn:DisplayObject,nam:String,hdl:Function,loc:String="linkButton"):void {
...
stacker.insert(btn,bar[loc]);
}


The only problem with this is that linkButton is the only clip with the 'back' clip inside of it, and most skins keep the vertical divider on only one side of that button, so adding the button on the left side of the controlbar creates design flaws ( double divider on left side, none on right ).

It would be nice if in addition to adding the 'loc' override, to add a 'dockSide' variable (='right' or 'left') and have two blank clips ( rightDockButton and leftDockButton ) in the player.fla (and subsequent skins) that would be blank (no icon), but have the dividers on the right or left side.

I've made it work with my own skins/player.fla, but it'd be nice if the core system supported this and I could add this plugin for all to use.

just my two cents.
.dm

JW Player

User  
0 rated :

having same problems in cs3... addButton doesn't work.

JW Player

User  
0 rated :

How to diaplay the playlist in player with small image of video and when the imageis clicked particular video should be play.

JeroenW

JW Player Support Agent  
0 rated :

@ dmac: I’ll take a look at making the extra button placement a little more flexible.

@Mark B: The youtube plugin tutorial on the developer site has a section about adding custom controlbar buttons:

http://developer.longtailvideo.com/trac/wiki/YousearchTutorial

JW Player

User  
0 rated :

Thanks for reply.

from where i found full source of yousearch ?

yousearch plugin requres more space in width i just want to put playlist within player like above the controlbar.


example of playlist in player see below link.

http://www.tsplayer.com/tsvideo-demo

thank you

JW Player

User  
0 rated :

@Vasim,

All source code is available here:

*http://developer.longtailvideo.com/trac*

There are instructions on the page referenced above for installling a Subversion client to obtain the source code from the repository.

JW Player

User  
0 rated :

how to create time slider and some control buttons for flash video in flash cs4

JW Player

User  
0 rated :

Hi,

I wanted to add Download Link button in my JWplayer PLAYLIST, which is auto generated by XPSF playlist of my MP3 files. Following code is used to generate the PlayList

bc.. <?
/**
* Produce an XPSF playlist on the fly from files in a given folder
*
* Code by Lewis Smith www.lasmit.com
* Code released under GNU General Public Licence - http://www.gnu.org/licenses/gpl.html
*
* If you use this software and find it useful please drop me an email - lewis@lasmit.com
* I'd like to build a list of people using the script which I'll publish on my site, so please do get in touch.

* Code uses snippets from http://getid3.sourceforge.net/source2/demo.browse.dhtml.phps
* Using getID3 library from http://getid3.sourceforge.net/
*/
$base_url = str_replace(basename($_SERVER['PHP_SELF']), '', $_SERVER['PHP_SELF']);
// Definitions
define("ROOT", $_SERVER["HTTP_HOST"]); // This is your domain name, can probably be left
define("PATH", $_SERVER["DOCUMENT_ROOT"]); // This is the full physical root to your folder, can probably be left
define("MP3_PATH", "$base_url/mp3"); // This is the virtual path to the folder containing your MP3s,
// e.g. if your files are at http://www.me.com/files/mp3s then this should
// be set to files/mp3s

require_once('../getid3/getid3.php'); // Include the getId3 library

// Create ID3 object
$getid3 = new getID3;
$getid3->encoding = 'ISO 8859-1';

$dir = PATH . "/" . MP3_PATH;
// Read directory
if (is_dir($dir)) {
if ($dh = opendir($dir)) {
while (($file = readdir($dh)) !== false) {
$full_name = "$dir/$file";
if (is_file($full_name)) {
$files[$full_name] = $file;
}
elseif ($file[0] != '.') {
$dir[$full_name] = $file;
}
}
closedir($dh);
}
}

// Produce output
if ($files) {

print <<< END_HEADER
<?xml version="1.0" encoding="UTF-8"?>
<playlist version="1" xmlns="http://xspf.org/ns/0/">
<!-- Playlist generated automatically using script from www.lasmit.com -->

<trackList>

END_HEADER;

if ($_REQUEST['shuffle']=="true") {
shuffle($files);
} else {
asort($files);
}

$counter = $i = 0;
foreach ($files as $full_name => $short_name) {

$getid3->Analyze($dir . "/" . $short_name);

$artist = $title = '';
if (@$getid3->info['tags']) {
foreach ($getid3->info['tags'] as $tag => $tag_info) {
if (@$getid3->info['tags'][$tag]['title']) {
$artist = @$getid3->info['tags'][$tag]['artist'][0];
$title = @$getid3->info['tags'][$tag]['title'][0];
$album = @$getid3->info['tags'][$tag]['album'][0];
break;
} else {
$title = basename($short_name, ".mp3");
}
}
} else {
$title = basename($short_name, ".mp3");
}

$url = ROOT . "/" . MP3_PATH . "/" . $short_name;

if (strpos($url, "http://") === false) {
$url = "http://" . $url;
}

$info = ROOT;

if (strpos($info, "http://") === false) {
$info = "http://" . $info;
}

print <<< END_TRACK
<track>
<location>{$url}</location>

<!-- artist or band name -->
<creator>{$artist}</creator>

<!-- album name -->
<album>{$album}</album>

<!-- name of the song -->
<title>{$title}</title>
</track>

END_TRACK;
}
print <<< END_FOOTER
</trackList>
</playlist>
END_FOOTER;
}
?>





Appreciate help.
Simon

Ethan Feldman

JW Player Support Agent  
0 rated :

@Simon – Unfortunately it is not possible to add a download button without modifying the source, or using a CMS that has this feature already (ex – PrismoTube).

JW Player

User  
0 rated :

i need to add a costume button to my control bar is any way to make it without use a plug-in? actually i need to add two buttons one for bookmark and the other one to change the quality of my video (hi, nomal, low) any help?

JW Player

User  
0 rated :

thanx
do you have a tutorial to make plugins step by step ? i am new in this :S

JW Player

User  
0 rated :

thanx i am on it C:

JW Player

User  
0 rated :

i am trying to add a button i made the plugin but i don't get anything on my player here is the code:

public class Bookmark extends Sprite implements IPlugin {

[Embed(source="../../../../../assets/bButton.png")]
private const bButton:Class;


/** constructor **/
public function Bookmark(){
};

private function addButton():void {
var button:DisplayObject = _player.skin.getSkinElement("bookmark", "bButton");
if (button == null) { button = new bButton(); }
_player.controls.controlbar.addButton(button, "bookmark", bookFun);

}

public function get id():String {
return "sharing";
};

public function initPlugin(player:IPlayer, config:PluginConfig):void {
_player = player;
_config = config;
addButton();
_player.addEventListener(PlaylistEvent.JWPLAYER_PLAYLIST_ITEM,test);
};

public function test(evt:PlaylistEvent):void{
trace("Hello World");
};

public function resize(wid:Number, hei:Number):void {
};

private function bookFun(evt:MouseEvent):void {
trace("wiiiiiii");
};

}
}

this is the link: http://radioglobalstudios.com/mariel/player/index.html

Pablo

JW Player Support Agent  
0 rated :

@Mariel -

In the link you provided, you do not appear to be loading your plugin at all. Your example is loading the hd and sharing plugins from the LongTail plugin repository.

To load your own plugin, you must the path to your plugin in the player configuration block:

plugins: {
        '/path/to/your/plugin.swf': {
              option1: 'value',
              option2: 'another value'
        }
  }

This question has received the maximum number of answers.