0251 / 590 837 15
info@a-coding-project.de

Videos auswählen im Smart TV

Im heutigen Part des Smart TV Tutorials werden wir es möglich machen sich ein Video auszusuchen. Dafür müssen wir zunächst einmal das Menü erweitern, auf dem der Fokus die ganze Zeit liegt. Wir müssen feststellen, wann die Pfeiltaste nach rechts gedrückt wurde, damit wir in die Video-Auswahl switchen können.

Dazu legen wir einen neuen Callback in der MenuEntry.js an. Dieser heißt OnKeyRight:

function MenuEntry(){
	this.control    = false;     //Control
	this.title      = undefined; //Bezeichnung
	this.active     = false;     //Menüpunkt aktiv?
	this.OnActivate = false;     //Callback
	this.OnKeyRight = false,     //Callback wenn die Pfeiltaste rechts gedrückt wurde
	//Gekürzt
}

Es ist vorerst nur eine Variable, die später noch befüllt wird. Als nächstes müssen wir diese Funktion bei dem rechten Key auch noch aufrufen. Wir fragen ja bereits in der Menu.js die Keys ab. Diese Stelle müssen wir nun einfach um den Key nach rechts erweitern:

switch(keyCode)
{
	case tvKey.KEY_DOWN:
		//Pfeil nach unten,
		//also Menüpunkt weiter unten auswählen
		Menu.MoveDown();
		break;
	case tvKey.KEY_UP:
		//Pfeil nach oben, 
		//also Menüpunkt weiter oben auswählen
		Menu.MoveUp();
		break;
	case tvKey.KEY_RIGHT:
		//Position raussuchen und Callback ausführen
		var pos = Menu.GetPosition();
		Menu.entries[pos].OnKeyRight();
		break;
}

Nun müssen wir natürlich auch noch das erste Video markieren. Leider werden die Videos dort noch nicht als Objekt fest gespeichert, dass müssen wir nun erstmal nachholen:

function Category()
{
	this.title = "";
	this.tag = "";
	this.menuEntry = false;
	this.videos = false;
	
	//Lädt die Videos in den Screen
	this.loadVideos = function()
	{
		document.getElementById('videolist').innerHTML = "";
		
		this.GetVideos(function(videos){
			this.videos = videos;
			
			//Videos hinzufügen
			for(var i = 0;i<=videos.length;i++)
			{
				document.getElementById('videolist').innerHTML += videos[i].GetTeaserCode();
			}
		});
	};
	
	//... gekürzt ...
}

Jetzt müssen wir noch eine Fokus-Funktion in die Videos einbauen, die aufgerufen wird, wenn der Fokus auf selbiges trifft (Video.js):

function Video()
{
	this.title = "";
	this.author = "";
	this.previewImage = "";
	this.playUrl = "";
	
	/**
	 * Gibt den Code zurück,
	 * der den Video-Teaser anzeigt
	 */
	this.GetTeaserCode = function()
	{
		//ID wird an dem Titel festgemacht
		this.id = "video_" + escape(this.title.substring(0,15));

		return '<li id="' + this.id + '" class="video_teaser">'
				+ '<a href="' + this.playUrl + '" class="image" '
					+ 'style="background-image:url('' + this.previewImage + '')"></a>'
				+ '<h2>' + this.title + '</h2>'
				+ '<div class="author">' + this.author + '</div>'
			 + '</li>';
	};
	
	/**
	 * Wird aufgerufen, wenn der Fokus auf das Video springt:
	 */
	this.Focus = function()
	{
		alert(this.id);
	};
	
}

Die ID wollte ich erst an Hand der aktuellen Zeit generieren, durch das zu Schnelle Laden, kann es dabei aber passiert, dass Elemente mit gleichem Namen auftauchen. Deswegen habe ich mich für die ersten 15 Zeichen des Titels entschieden.

Im dritten Code-Snippet des Beitrages haben wir die Videos markiert. Hier habe ich ein bisschen rätseln müssen, warum das Ganze noch nicht funktioniert. Es hat sich herausgestellt, dass das this in dem Sinne gar nicht passt, weil die Funktion als Callback übergeben wird und somit der Kontext geändert wurde. Nun habe ich dort einen zweiten Parameter Namens category eingebaut, mit dem wir wieder auf das Category-Objekt zugreifen können.

Außerdem rufe ich nun die Focus-Funktion des ersten Videos auf:

function Category()
{
	this.title = "";
	this.tag = "";
	this.menuEntry = false;
	/*this.videos = false;*/
	
	
	//Lädt die Videos in den Screen
	this.loadVideos = function()
	{
		document.getElementById('videolist').innerHTML = "";
		
		this.GetVideos(function(videos, category){
			category.videos = videos;

			//Videos hinzufügen
			for(var i = 0;i<=videos.length;i++)
			{
				document.getElementById('videolist').innerHTML += videos[i].GetTeaserCode();
			}
		});
	};
	
	//Liefert den Menüeintrag zurück
	this.GetMenuEntry = function()
	{
		if(!this.menuEntry)
		{
			//Nachladen
			this.menuEntry= new MenuEntry();
			this.menuEntry.tag = this;
			this.menuEntry.OnKeyRight = function(){
				//alert(this.tag.videos);
				if(this.tag.videos.length > 0)
				{
					this.tag.videos[0].Focus();
				}
			};
			this.menuEntry.OnActivate = function(){ this.tag.loadVideos(); };	
		}
		
		//Titel kann sich geändert haben, also immer neu setzen
		this.menuEntry.title = this.title;
		
		//Weg damit
		return this.menuEntry;
	};
	
	this.GetVideos = function(callback)
	{
		var res = new Array();
		
		var category = this;
		
		 var xmlhttp = null;
		 // Mozilla
		 if (window.XMLHttpRequest)
		 {
		     xmlhttp = new XMLHttpRequest();
		 }
		 // IE
		 else if (window.ActiveXObject)
		 {
		     xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
		 }
	
		 //Kategorie abfragen
		 xmlhttp.open("GET", 'http://smart-tv.stevieswebsite.de/' + this.tag + '.php', true);
		 xmlhttp.onreadystatechange = function()
		 {
		     if(xmlhttp.readyState == 4 && xmlhttp.status == 200)
		     {
		    	 //Abfrage erfolgreich. Parsen wir das JSON!
		     	obj = JSON.parse(xmlhttp.responseText);
		     	for(var i = 0;i < obj.length; i++)
		     	{
					//Videos erstellen
					var video = new Video();
					video.title = obj[i].title;
					video.author = obj[i].author;
					video.previewImage = obj[i].thumb;
					
					res[res.length] = video;
		     	}
		     	
		     	//Callback aufrufen
				callback(res,category);
		     }
		 };
		 
		 //Abfrage starten
		 xmlhttp.send(null);
	};

}

Wird mal Zeit, dass man auch etwas von unserer Arbeit sieht, oder? Nun färben wir das aktuell ausgewählte Video rot. Das wird über die CSS-Klasse current gemacht. So kann man später, ohne den JavaScript Code zu bearbeiten, das Verhalten ganz schnell anpassen. Zur besseren Übersicht habe ich auch noch eine Funktion gebastelt, die das HTML-Teaserelement zurückgibt. Die neue Video.js:

function Video()
{
	this.title = "";
	this.author = "";
	this.previewImage = "";
	this.playUrl = "";
	
	//... gekürzt ...
	
	/**
	 * Liefert das Teaser-Element zurück.
	 */
	this.GetTeaserElement = function ()
	{
		return document.getElementById(this.id);
	};
	
	/**
	 * Wird aufgerufen, wenn der Fokus auf das Video springt:
	 */
	this.Focus = function()
	{
		var element = this.GetTeaserElement();
		element.className = "video_teaser current";
	};
	
};

Fehlt noch der Inhalt der Main.css. Folgendes wird einfach angehangen:

.video_teaser.current .image{
	border-color:#b00000;
}

Zwischen den ersten beiden Klassen ist übrigens kein Leerzeichen, da beide zum gleichen Element gehören.

Und das kommt nun dabei heraus:

Wie gesagt, durch das CSS könnt ihr das Ganze natürlich so gestalten, wie ihr es gerne hättet. Cool wäre vielleicht, wenn das Preview-Bild gleichzeitig auch noch vergrößert wird.

Im nächsten Schritt müssen wir die Navigation zwischen den einzelnen Videos steuern. Das mache ich bald über die Category.js, da wir da die Liste mit allen Videos haben. Bei den einzelnen Videos kann dann hinterlegt werden, was bei welchen Tastenschlag passieren soll. Das sieht dann so aus (Video.js):

function Video()
{
	this.title = "";
	this.author = "";
	this.previewImage = "";
	this.playUrl = "";
	this.onKeyRight = null;
	this.onKeyLeft = null;
	this.onKeyDown = null;
	this.onKeyUp = null;
	
	//... gekürzt ...
	
	/**
	 * Wird aufgerufen, wenn der Fokus auf das Video springt:
	 */
	this.Focus = function()
	{
		var element = this.GetTeaserElement();
		element.className = "video_teaser current";
		element.children[0].focus();
		element.tag = this;
		element.onkeyup = function(){
			var keyCode = event.keyCode;

			switch(keyCode)
			{
				case tvKey.KEY_DOWN:
					if(this.tag.onKeyDown)
					{
						this.tag.onKeyDown();
					}
					break;
				case tvKey.KEY_UP:
					if(this.tag.onKeyUp)
					{
						this.tag.onKeyUp();
					}
					break;
				case tvKey.KEY_RIGHT:
					if(this.tag.onKeyRight)
					{
						this.tag.onKeyRight();
					}
					break;
				case tvKey.KEY_LEFT:
					if(this.tag.onKeyLeft)
					{
						this.tag.onKeyLeft();
					}
					break;
			}
		};
	};
	
};

Im nächsten Schritt möchten wir nun endlich zwischen den Videos hin- und herschalten. Zum Start möchte ich erstmal nur die Navigation zwischen links und rechts aktivieren, wer mag kann es später auch die anderen Pfeiltasten beachten.

Der Code der Category.js hat sich jetzt so geändert:

function Category()
{
	//... gekürzt ...
	
	this.GetVideos = function(callback)
	{
		
		//... gekürzt ...
	
		 //Kategorie abfragen
		xmlhttp.open("GET", 'http://smart-tv.stevieswebsite.de/' + this.tag + '.php', true);
		xmlhttp.onreadystatechange = function()
		{
			if(xmlhttp.readyState == 4 && xmlhttp.status == 200)
			{
				//Abfrage erfolgreich. Parsen wir das JSON!
				obj = JSON.parse(xmlhttp.responseText);
				for(var i = 0;i < obj.length; i++)
				{
					//Videos erstellen
					var video = new Video();
					video.title = obj[i].title;
					video.author = obj[i].author;
					video.previewImage = obj[i].thumb;
					
					res[res.length] = video;
				}

				for(var i = 0;i < res.length; i++)
				{
					var video = res[i];
					if(i > 0)
					{
						res[i-1].nextVideo = video;
						res[i-1].onKeyRight = function(){
							this.nextVideo.Focus();
							this.UnFocus();
						};
					}
					if(i < res.length -1)
					{
						res[i+1].prevVideo = video;
						res[i+1].onKeyLeft = function(){
							this.prevVideo.Focus();
							this.UnFocus();
						};
					}
				}
				
				//Callback aufrufen
				callback(res,category);
			}
		};
		 
		//Abfrage starten
		xmlhttp.send(null);
	};

}

Ich durchlaufe die Videos und ändere die Key-Funktionen, der Videos vor und/oder nach dem aktuellen Video. In meinen Tests hatte ich ab und an ein paar Probleme im Emulator. Wenn das bei euch auch so ist, müsst ihr etwas langsamer auf die Tasten der Fernbedienung drücken.

Was jetzt noch fehlt, ist dass der Fokus wieder zurück ins Menü springt, wenn man vom ersten Video aus nach links klickt. Dazu müssen wir als erstes auch eine Focus-Funktion in das Menü bringen. Das geht über die Menu.js:

var Menu = {};

/**
 * Funktion wird nach dem laden der Seite aufgerufen
 */
Menu.Load = function(){
	//... gekürzt ...
	
	//Den Fokus zurück auf das Menü setzen
	this.Focus = function()
	{
		document.getElementById("menu_focus").focus();
	};
	
	var seo = new Category();
	seo.title = "SEO";
	seo.tag = "seo";
	this.AddEntry(seo.GetMenuEntry());
	
	//... gekürzt ...
};

Das Ganze müssen wir jetzt nur noch in der Category.js aufrufen und unsere Steuerung ist vorerst komplett:

function Category()
{
	//.. gekürzt ...
	
	this.GetVideos = function(callback)
	{
		//.. gekürzt ..
	
		//Kategorie abfragen
		xmlhttp.open("GET", 'http://smart-tv.stevieswebsite.de/' + this.tag + '.php', true);
		xmlhttp.onreadystatechange = function()
		{
			if(xmlhttp.readyState == 4 && xmlhttp.status == 200)
			{
				//Abfrage erfolgreich. Parsen wir das JSON!
				obj = JSON.parse(xmlhttp.responseText);
				for(var i = 0;i < obj.length; i++)
				{
					//Videos erstellen
					var video = new Video();
					video.title = obj[i].title;
					video.author = obj[i].author;
					video.previewImage = obj[i].thumb;
					
					res[res.length] = video;
				}

				for(var i = 0;i < res.length; i++)
				{
					var video = res[i];
					if(i > 0)
					{
						res[i-1].nextVideo = video;
						res[i-1].onKeyRight = function(){
							this.nextVideo.Focus();
							this.UnFocus();
						};
					}
					else
					{
						res[i].onKeyLeft = function(){
							Menu.Focus();
							this.UnFocus();
						};
					}
					if(i < res.length -1)
					{
						res[i+1].prevVideo = video;
						res[i+1].onKeyLeft = function(){
							this.prevVideo.Focus();
							this.UnFocus();
						};
					}
				}
				
				//Callback aufrufen
				callback(res,category);
			}
		};
		 
		//Abfrage starten
		xmlhttp.send(null);
	};

}

Wie gesagt, ihr könnt ja mal versuchen es auch zu ermöglichen innerhalb der Videos von oben nach unten zu Navigieren. Aber Achtung: Bei anderen Auflösungen können auch mehr Bilder in einer Reihe stehen.

Meinen aktuellen Stand bekommt ihr hier.

Kommentare

Rookee schrieb am 28.12.2012:

Interessant wäre auch die Veröffentlichung im App Store des Smart TV

Stefan Wienströer schrieb am 28.12.2012:

Kommt ja noch ;-)

Ulli schrieb am 11.03.2013:

Die Seite: http://blog.stevieswebsite.de/2012/12/youtube-im-smart-tv-abspielen-smart-tv-app/ tut es derzeit nicht (access denied) Gruß Ulli

Stefan Wienströer schrieb am 11.03.2013:

Hallo Ulli, danke für den Hinweis. Ich komme aber auf die Seite drauf. Kannst du es noch einmal probieren? War vielleicht nur ein Timeout. Gruß Stefan