API: YouTube-Player (Part 2)

Da ging etwas daneben beim Einbinden. :(
0:00


Referenzen

HTML

Das HTML-Grundgerüst. Das erste div wird mit JavaScript dann durch den YouTube-API-Flashplayer ersetzt. Im anderen div befinden sich die Kontrollelemente.

<div id="ytplayergoeshere">Einbinden ging daneben. :(</div>
<div class="ytcontrol">
	<span id="yttimebg">
		<span id="yttimeload"></span>
		<span id="yttime"></span>
	</span>
	<span id="yttimeinfo">0:00</span>
	<input id="ytplay" type="button" value="Play" onclick="playYTVideo()" />
	<input id="ytstop" type="button" value="Stop" onclick="stopYTVideo()" />
	<select id="ytquality"></select>
</div>

JavaScript

Ich könnte jetzt einige Absätze schreiben und alles beschreiben, aber der Code ist kommentiert und ich hoffe dadurch bereits verständlich. Nachfragen zu irgendwelchen Abschnitten sind natürlich herzlich willkommen. Für eine einfachere Handhabung habe ich jQuery verwendet. Gotta love it.

width = 600; // Breite des Videos


/**
 * Formatiert Sekunden in eine "Minuten:Sekunden"-Angabe.
 */
function timeInfoFormat( seconds ) {
	var time = "0:";
	var minutes = 0;
	if( seconds >= 60 ) {
		minutes = Math.floor( seconds / 60 );
		seconds -= ( 60 * minutes );
		time = minutes.toString() + ":";
	}
	time += ( seconds < 10 ) ? "0" + seconds.toString() : seconds.toString();
	return time;
}

/**
 * Trägt die zur Wahl stehenden Auflösungen ein.
 * Falls nicht schon geschehen.
 */
function qualityOptions() {
	if( ytplayer && $( "#ytquality" ).children().size() <= 0 ) {
		var aql = ytplayer.getAvailableQualityLevels();
		var resolution = "";
		var isSelected = "";
		for( var key in aql ) {
			switch( aql[key] ) {
				case "small": resolution = "240p"; break;
				case "medium": resolution = "360p"; break;
				case "large": resolution = "480p"; break;
				case "hd720": resolution = "720p"; break;
				case "hd1080": resolution = "1080p"; break;
				default: resolution = aql[key];
			}
			isSelected = "";
			if( aql[key] == ytplayer.getPlaybackQuality() ) {
				isSelected = ' selected="selected"';
			}
			$( "#ytquality" ).append(
				'<option value="' + aql[key] + '"' + isSelected + '>'
				+ resolution + '</option>'
			);
		}
	}
}

/**
 * Zeigt an, wie weit das Laden des Videos bereits fortgeschritten ist
 * und wie weit es bereits abgespielt hat anhand zweier breiter-werdender
 * HTML-Elemente (#yttimeload und #yttime).
 */
function updateBars() {
	if( ytplayer && ytplayer.getDuration && ytplayer.getCurrentTime() > 0 ) {
		var wTime = ytplayer.getCurrentTime() / ytplayer.getDuration();
		wTime *= width;
		$( "#yttime" ).css( "width", wTime+"px" );

		var wLoad = ytplayer.getVideoStartBytes()
		wLoad += ytplayer.getVideoBytesLoaded();
		wLoad = ( wLoad /  ytplayer.getVideoBytesTotal() ) * width;
		$( "#yttimeload" ).css( "width", wLoad + "px" );
		
	}
}

/**
 * Reagiert auf das Event, dass sich der Status ändert.
 * Ändert die Beschriftung des Play/Pause-Buttons.
 */
function onYtStateChanged( newState ) {
	switch( newState ) {
		case 1:
		case 3:
			$( "#ytplay" ).attr( "value", "Pause" );
			break;
		default:
			$( "#ytplay" ).attr( "value", "Play" );
	}
}


/**
 * Startet oder pausiert das Video.
 * getPlayerState() kann sein:
 * 	-1 – unstarted
 * 	 0 – ended
 * 	 1 – playing
 * 	 2 – paused
 * 	 3 – buffering
 * 	 5 – video cued
 */
function playYTVideo() {
	if( ytplayer ) {
		if( ytplayer.getPlayerState() != 1 ) {
			ytplayer.playVideo();
		}
		else {
			ytplayer.pauseVideo();
		}
	}
}

/**
 * Stoppt das Abspielen und Laden des Videos.
 * Setzt die Ansicht zurück.
 */
function stopYTVideo() {
	if( ytplayer ) {
		ytplayer.stopVideo();
		ytplayer.clearVideo();
		$( "#yttime" ).css( "width", "1px" );
		$( "#yttimeload" ).css( "width", "1px" );
	}
}


/**
 * Wird in einem festen Intervall aufgerufen.
 * Updated HTML-Elemente, die etwas zum Status aussagen.
 */
function updatePlayerInfo() {
	updateBars();
	qualityOptions();
}

/**
 * Wenn der YouTube-API-Player geladen wurde, geht es hier weiter.
 * Startet das periodische Aufrufen der Update-Funktion.
 * Übergibt die ID des zu ladenden Videos.
 * Verbindet Events mit Funktionen.
 * Verbindet noch ein paar HTML-Elemente mit Funktionalitäten.
 */
function onYouTubePlayerReady( playerid ) {
	ytplayer = document.getElementById( "ytplayer" );
	setInterval( updatePlayerInfo, 500 );
	ytplayer.cueVideoById( "CJheUeVCAyw" );
	ytplayer.addEventListener( "onStateChange", "onYtStateChanged" );

	$( "#yttimeinfo" ).hide();

	/* Balken unter dem Video.
	 * Betritt die Maus den Bereich, wird die Zeitinfo sichtbar.
	 * Verlässt die Maus den Breich, wird die Zeitinfo versteckt.
	 * 
	 * Bewegt sich die Maus in dem Bereich, wird die Zeitinfo mit der
	 * Zeit aktualisiert, die der x-Position auf dem Balken entspricht.
	 * 
	 * Klickt man auf eine Stelle dort, springt das Video an die – der
	 * Sekundenzahl entsprechenden – Stelle.
	 */
	$( "#yttimebg" ).mouseenter( function() {
		$( "#yttimeinfo" ).show();
	} ).mouseleave( function() {
		$( "#yttimeinfo" ).hide();
	} ).mousemove( function( e ) {
		if( ytplayer ) {
			var x = e.pageX - this.offsetLeft;
			var xInfo = x + 26;
			var perCent = x / width;
			var seconds = Math.round( ytplayer.getDuration() * perCent );
			$( "#yttimeinfo" ).css( "margin-left", xInfo + "px" );
			$( "#yttimeinfo" ).text( timeInfoFormat( seconds ) );
		}
	} ).click( function( e ) {
		if( ytplayer ) {
			var x = e.pageX - this.offsetLeft;
			while( x < 0 ) { x++; }
			var perCent = x / width;
			var seconds = ytplayer.getDuration() * perCent;
			ytplayer.seekTo( Math.round( seconds ), true );
		}
	} );

	/* Wählt man eine andere Qualität aus der select-Box, wird dies
	 * dem Player übergeben.
	 */
	$( "#ytquality" ).change( function() {
		if( ytplayer ) {
			var qualityString = $( "#ytquality option:selected" ).val();
			if( qualityString != ytplayer.getPlaybackQuality() ) {
				ytplayer.setPlaybackQuality( qualityString );
			}
		}
	} );
}

/**
 * Ersetzt ein HTML-Element einer bestimmten ID mit dem YouTube-API-Player.
 * Wichtige Funktion.
 */
function loadPlayer() {
	var params = { allowScriptAccess: "always" };
	var atts = { id: "ytplayer" };
	swfobject.embedSWF(
		"http://youtube.com/apiplayer?enablejsapi=1&playerapiid=player1",
		"ytplayergoeshere", width, "340", "8", null, null, params, atts
	);
}

// Los geht's.
loadPlayer();

CSS

Was die Bedienelemente hübsch macht. Einige CSS3-Eigenschaften sind darunter, wie box-shadow, border-radius und die Verwendung von rgba für Farbangaben. Der -moz/-webkit-Präfix für Firefox bzw. Safari und Chrome nervt mittlerweile, hoffentlich entfallen die bald mal.

#yttimebg {
	cursor: pointer;
	background-color: #ffffff;
	box-shadow: #f0f0f0 0 -8px 4px inset;
	-moz-box-shadow: #f0f0f0 0 -8px 4px inset;
	-webkit-box-shadow: #f0f0f0 0 -8px 4px inset;
	display: block;
	height: 16px;
	margin-bottom: 4px;
	width: 600px;
}

#yttimeload {
	background-color: #f02020;
	display: block;
	height: 16px;
	opacity: 0.3;
	position: absolute;
	width: 1px;
	z-index: 2;
}

#yttime {
	background-color: #f02020;
	box-shadow: rgba( 255, 255, 255, 0.2 ) 0 8px 2px inset;
	-moz-box-shadow: rgba( 255, 255, 255, 0.2 ) 0 8px 2px inset;
	-webkit-box-shadow: rgba( 255, 255, 255, 0.2 ) 0 8px 2px inset;
	display: block;
	height: 16px;
	position: absolute;
	width: 1px;
	z-index: 3;
}

#yttimeinfo {
	background-color: #ffffff;
	box-shadow: #606060 0 0 3px;
	-moz-box-shadow: #606060 0 0 3px;
	-webkit-box-shadow: #606060 0 0 3px;
	border-radius: 6px;
	-moz-border-radius: 6px;
	-webkit-border-radius: 6px;
	display: inline-block;
	font-size: 80%;
	margin-top: -7px;
	padding: 0 6px;
	position: absolute;
	z-index: 4;
}

#ytplay, #ytstop {
	border-radius: 20px;
	-moz-border-radius: 20px;
	-webkit-border-radius: 20px;
	cursor: pointer;
	padding: 5px 12px;
}

#ytquality {
	border-radius: 4px;
	-moz-border-radius: 4px;
	-webkit-border-radius: 4px;
	float: right;
	width: 80px;
}


Abschluss für all euch, die bis zum Ende gescrollt haben.

Den YouTube-Player mal selbst mit einer Bedienung auszustatten und dann zu gestalten, hat wirklich Spaß gemacht. Am liebsten würde ich jetzt alle YouTube-Videos so einbinden, hehe. Was leider keine gute Idee wäre, denn sonst müsste jeder Besucher den Batzen JavaScript mitladen. Eine Möglichkeit für Fullscreen habe ich auch noch nicht gefunden. Von der nicht-vorhandenen Gewissheit, ob es auch in allen Browsern immer klappt, mal ganz zu schweigen. Aber ja, ein cooles, kleines Experiment.

Eine Seite, die sich mit der API eine eigene Bedienung gebastelt hat, ist zum Beispiel Blip.fm.

Der Anime in dem Video ist To Aru Majutsu no Index – durchaus zu empfehlen – remixt mit dem Lied „Sand Canyon“ aus irgendeinem Kirby-Spiel. Solche Remixes haben es vor einiger Zeit schon zur Meme geschafft.