Monthly Archives: September 2012

RaspBerry Pi et afficheur LCD Hitachi HD44780 1602 [PART 2]

raspberrypi-lcd

Voici la suite du tutoriel sur l’intégration et l’utilisation d’un afficheur LCD HD44780 sur un RaspBerry Pi.

Pour ceux qui n’ont pas lu la première partie, je vous conseille de le faire.

Montage LCD sur RaspberryPi

Un schéma étant souvent plus évocateur que de longs discours, j’ai préparé tout le câblage sur grâce au logiciel fritzing.

Le montage suivant permet la connection d’un écran LCD HD44780 sur votre RaspBerry Pi. N’hésitez pas à cliquer sur l’image pour l’agrandir.

Le pin RW (Pin 5 du LCD) permet de spécifier à l’afficheur si on souhaite envoyer ou recevoir des données. Dans notre cas, on souhaite uniquement recevoir des données provenant du RaspBerry Pi, c’est pourquoi je l’ai relié directement à la masse.

De plus, il faut savoir que votre RaspBerry Pi ne supportera pas 5V en entrée sur les ports GPIO. Le fait de relier le pin RW à la masse m’assure donc que je n’enverrai pas du 5V à mon RaspBerry Pi, ce qui pourrait l’endommager.

Vous noterez également que le pin 3 de l’afficheur LCD est directement branché à la masse. Sa tension qui doit se situer entre 0V et 5V permet de définir le contraste de l’afficheur LCD. Comme expliqué dans la première partie du tutoriel, il est possible de connecter un potentiomètre afin de régler le contraste (le point suivant illustre cela).

Vous noterez également que le pin 15 de l’afficheur LCD HD44780 est relié à une résistance. Je l’ai mise car ce pin qui sert au backlight est alimenté en 5V et je n’ai voulu prendre aucun risque.

Vous voyez il est relativement simple de connecter un afficheur LCD HD44780 à votre RaspBerry Pi.

Montage LCD sur RaspberryPi avec Potentiomètre

L’utilisation d’un potentiomètre va vous permettre de régler le contraste en jouant sur le pin 3 de votre afficheur LCD. Le reste de la connectique est identique au schéma précédent.

Premier branchement du LCD sur votre RaspBerry Pi

Je vous conseille de souder déjà les cables avec votre afficheur LCD:

Vous remarquerez que j’ai utilisé exactement les mêmes coloris de câbles que dans mon schéma. C’est important pour éviter de se perdre dans les fils.

Vu que je suis sympa çà me gène pas si vous vous moquez de mes soudures (j’en avais pas refait depuis 12 ans :-)).

Ensuite effectuez toutes les connexions sur votre breadboard:


Vous pouvez ensuite effectuer les connexions vers votre RaspBerry Pi.

ATTENTION: Ne branchez que le pin d’alimentation 5V que si vous êtes sûr de votre montage.

Une fois votre afficheur LCD HD44780 correctement branché sur votre RaspBerry Pi, vous devriez avoir quelque chose comme çà:

Afficher du texte sur votre LCD HD44780

L’intérêt d’avoir un afficheur LCD connecté à votre RaspBerry Pi est d’y afficher du texte.

Pour pouvoir faire cela, nous allons utiliser le langage Python et la bibliothèque RPi-GPIO qui permet d’envoyer des commandes ou des données via les pins de votre RaspBerry Pi.

Installation de RPi-GPIO

Téléchargez RPi-GPIO:

wget http://pypi.python.org/packages/source/R/RPi.GPIO/RPi.GPIO-0.3.1a.tar.gz

Extraire l’archive puis naviguer dans le répertoire créé:

tar zxf RPi.GPIO-0.3.1a.tar.gz
cd RPi.GPIO-0.3.1a

Installez la bibliothèque RPi-GPIO puis supprimer les fichiers inutiles:

python setup.py install
cd ..
rm -rf RPi.GPIO-0.3.1a/

 Afficher du texte sur le LCD en Python

Voici le code Python qui me sert à afficher des informations systèmes sur mon afficheur LCD connecté à mon RaspBerry Pi:

#!/usr/bin/python
 
############################
# Imports                  #
############################
import threading
import RPi.GPIO as GPIO
from time import sleep
import subprocess
 
############################
# Class de controle du LCD #
############################
class HD44780(threading.Thread):
	######################
	# Variable Shared    #
	######################
	_PULSE = 0.00005
	_DELAY = 0.00005
 
	######################
	# Constructeur       #
	######################
	def __init__(self, pin_rs=7, pin_e=8, pins_db=[25, 24, 23, 18], lcd_width=16):
		self.message = ""
		self.currentmessage = "azertyuiop"
		self.stop = False
		self.lcd_width = lcd_width
		self.pin_rs = pin_rs
		self.pin_e = pin_e
		self.pins_db = pins_db
		GPIO.setmode(GPIO.BCM) 				# Use BCM GPIO numbers
		GPIO.setup(self.pin_e, GPIO.OUT)
		GPIO.setup(self.pin_rs, GPIO.OUT)
		for pin in self.pins_db:
			GPIO.setup(pin, GPIO.OUT)
 
		self.Clear()
		threading.Thread.__init__(self)
 
	######################
	# Demarrage du Thread# 
	######################
	def run(self):
		while self.stop == False:
			if self.message != self.currentmessage:
				self.currentmessage = self.message
				self.LcdMessage()
			sleep(1)
 
	######################
	# Arret du Thread    # 
	######################	
	def Stop(self):
		self.stop = True
 
	######################
	# Initialisation LCD # 
	######################
	def Clear(self):
		""" Blank / Reset LCD """
		self.LcdByte(0x33, False) # $33 8-bit mode
		self.LcdByte(0x32, False) # $32 8-bit mode
		self.LcdByte(0x28, False) # $28 8-bit mode
		self.LcdByte(0x0C, False) # $0C 8-bit mode
		self.LcdByte(0x06, False) # $06 8-bit mode
		self.LcdByte(0x01, False) # $01 8-bit mode
 
	######################
	#Execution sur le LCD# 
	######################
	def LcdByte(self, bits, mode):
		""" Send byte to data pins """
		# bits = data
		# mode = True  for character
		#        False for command
 
		GPIO.output(self.pin_rs, mode) # RS
 
		# High bits
		for pin in self.pins_db:
			GPIO.output(pin, False)
		if bits&0x10==0x10:
			GPIO.output(self.pins_db[0], True)
		if bits&0x20==0x20:
			GPIO.output(self.pins_db[1], True)
		if bits&0x40==0x40:
			GPIO.output(self.pins_db[2], True)
		if bits&0x80==0x80:
			GPIO.output(self.pins_db[3], True)
 
		# Toggle 'Enable' pin
		sleep(HD44780._DELAY)    
		GPIO.output(self.pin_e, True)  
		sleep(HD44780._PULSE)
		GPIO.output(self.pin_e, False)  
		sleep(HD44780._DELAY)      
 
		# Low bits
		for pin in self.pins_db:
			GPIO.output(pin, False)
		if bits&0x01==0x01:
			GPIO.output(self.pins_db[0], True)
		if bits&0x02==0x02:
			GPIO.output(self.pins_db[1], True)
		if bits&0x04==0x04:
			GPIO.output(self.pins_db[2], True)
		if bits&0x08==0x08:
			GPIO.output(self.pins_db[3], True)
 
		# Toggle 'Enable' pin
		sleep(HD44780._DELAY)    
		GPIO.output(self.pin_e, True)  
		sleep(HD44780._PULSE)
		GPIO.output(self.pin_e, False)  
		sleep(HD44780._DELAY) 	
 
	######################
	#Affichage sur le LCD# 
	######################	
	def LcdMessage(self):
		""" Send string to LCD. Newline wraps to second line"""
		self.Clear()
		text = self.currentmessage
		self.LcdByte(0x80, False)
		for c in text:
			if c == '\n':
				self.LcdByte(0xC0, False) # next line
			else:
				self.LcdByte(ord(c),True)
 
	######################
	#Definir le message  # 
	######################
	def LcdSetMessage(self, text):
		self.message = text.ljust(self.lcd_width," ")
 
################################
# Class Informations Systeme   #
################################
class SysInfo(threading.Thread):
	######################
	# Constructeur       #
	######################
	def __init__(self):
		self.count = 0
		self.lcd = HD44780()
		self.stop = False
		threading.Thread.__init__(self)
 
	######################
	# Demarrage du Thread# 
	######################
	def run(self):
		self.lcd.start()
		while self.stop == False:
			if self.count == 0: #UPTIME
				self.lcd.LcdSetMessage("Uptime:\n"+self.SysUptime())
			elif self.count == 1: #LOAD
				self.lcd.LcdSetMessage("Load:\n"+self.SysLoadAvg())
			elif self.count == 2: #CPU
				self.lcd.LcdSetMessage("Cpu:\n"+self.SysCpu())
			elif self.count == 3: #MEMORY
				self.lcd.LcdSetMessage("Memory:\n"+self.SysMemory())
			elif self.count == 4: #DRIVE
				self.lcd.LcdSetMessage("Disk Root:\n"+self.SysDisk())
			else:
				self.lcd.LcdSetMessage("ZeM.fr    ZeM.fr\nRoxXxXxXxXxXxXxX")
			self.count += 1
			if self.count == 6:
				self.count = 0
			sleep(5)
 
	######################
	# Arret du Thread    # 
	######################
	def Stop(self):
		self.lcd.Stop()
		self.stop = True	
 
	######################
	# Memory             # 
	######################		
	def SysMemory(self):
		try:
			command="free -h"
			process=subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
			stdout_list=process.communicate()[0].split('\n')
			for line in stdout_list:
			  data=line.split()
			  try:
				  if data[0]=="Mem:":
					  total=str(data[1])
					  used=str(data[2])
					  free=str(data[3])
			  except IndexError:
				  continue	
		except:
			return "Error"
		return str(used)+ " / "+str(total)
 
	######################
	# Disk              # 
	######################		
	def SysDisk(self):
		try:
			command="df -H /"
			process=subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
			stdout_list=process.communicate()[0].split('\n')
			for line in stdout_list:
			  data=line.split()
			  try:
				  if data[len(data)-1]=="/":
					  total=str(data[1])
					  used=str(data[2])
			  except IndexError:
				  continue	
		except Exception, e:
			print e
			return "Error"
		return str(used)+ " / "+str(total)
 
	######################
	# CPU                # 
	######################	
	def SysCpuGetTimeList(self):
		statFile = file("/proc/stat", "r")
		timeList = statFile.readline().split(" ")[2:6]
		statFile.close()
		for i in range(len(timeList))  :
			timeList[i] = int(timeList[i])
		return timeList
	def SysCpu(self):
		try:
			x = self.SysCpuGetTimeList()
			sleep(2)
			y = self.SysCpuGetTimeList()
			for i in range(len(x)):
				y[i] -= x[i]
			cpuPct = 100 - (y[len(y) - 1] * 100.00 / sum(y))
		except:
			return "Error"
		return str('%.2f' %cpuPct)+"%"
 
	######################
	# Load Average       # 
	######################
	def SysLoadAvg(self):
		try:
		 f = open( "/proc/loadavg" )
		 contents = f.read().split()
		 f.close()
		except:
			return "Error"
		return contents[0]+" "+contents[1]+" "+contents[2];		
 
	######################
	# Systeme Uptime     # 
	######################
	def SysUptime(self):
		try:
			f = open( "/proc/uptime" )
			contents = f.read().split()
			f.close()
			total_seconds = float(contents[0])
			MINUTE  = 60
			HOUR    = MINUTE * 60
			DAY     = HOUR * 24
			days    = int( total_seconds / DAY )
			hours   = int( ( total_seconds % DAY ) / HOUR )
			minutes = int( ( total_seconds % HOUR ) / MINUTE )
			seconds = int( total_seconds % MINUTE )
			string = ""
			if days > 0:
				string += str(days) + "d "
			if len(string) > 0 or hours > 0:
				string += str(hours) + "h "
			if len(string) > 0 or minutes > 0:
				string += str(minutes) + "m "
			string += str(seconds) + "s "
		except:
			return "Error"
		return string;	
 
######################
# MAIN STARTER       # 
######################	
if __name__ == '__main__':
 
	si = SysInfo()
	si.start()
	q = str(raw_input('Press ENTER to quit program'))
	si.Stop()

Ce script Python contient 2 classes:

  • la classe HD44780 qui permet de contrôler l’afficheur LCD
  • la classe SysInfo qui permet de récupérer certaines informations propres au système

Le script contient également un lanceur afin d’exécuter le main.

Voici une vidéo montrant le résultat produit par ce script:

Scripts d’import en Javascript pour Mediatomb

mediatomb-scripts

Après avoir activer le support du javascript dans Mediatomb, il est intéressant de scripter les fichiers common.js et import.js afin d’avoir une hiérarchisation de vos médias qui vous conviendra.

Je n’utilise Mediatomb que pour mes films et mes séries. Ceux-ci sont disponibles dans /zfilms et /zseries.

Les films sont organisés dans des répertoires par genres et les séries sont organisées par saisons.

Évidemment mes scripts ne sont données qu’à titre d’exemple et devront être adaptés à votre organisation. Ils permettent une visualisation par genre et par classement alphabétique.

Voici mon fichier Common.js

//Fonction qui supprime le underscore devant un nom
function StripFirst(name, c)
{
	if (c == undefined) { c = "_"; }
	ret = name;
	if ((name != undefined) && (name != ""))
	{
		if (name.substring(0,c.length) == c)
		{
			ret = name.substring(c.length);
		}
	}
	return ret;
}
 
function CreateSerieArray(chain, location)
{
	for (i = 2; i < location.length -1; i++)
	{
		chain.push(StripFirst(location[i]));
	}
	return chain;
}
 
function escapeSlash(name)
{
    name = name.replace(/\\/g, "\\\\");
    name = name.replace(/\//g, "\\/");
    return name;
}
 
function createContainerChain(arr)
{
    var path = '';
    for (var i = 0; i < arr.length; i++)
    {
        path = path + '/' + escapeSlash(arr[i]);
    }
    return path;
}
 
function getYear(date)
{
    var matches = date.match(/^([0-9]{4})-/);
    if (matches)
        return matches[1];
    else
        return date;
}
 
function getPlaylistType(mimetype)
{
    if (mimetype == 'audio/x-mpegurl')
        return 'm3u';
    if (mimetype == 'audio/x-scpls')
        return 'pls';
    return '';
}
 
function getLastPath(location)
{
    var path = location.split('/');
    if ((path.length > 1) && (path[path.length - 2]))
        return path[path.length - 2];
    else
        return '';
}
function getSeriePath(location)
{
    var path = location.split('/');
    if (path.length > 2)
        return path[2];
    else
        return '';
}
 
function getRootPath(rootpath, location)
{
    var path = new Array();
 
    if (rootpath.length != '')
    {
        rootpath = rootpath.substring(0, rootpath.lastIndexOf('/'));
 
        var dir = location.substring(rootpath.length,location.lastIndexOf('/'));
 
        if (dir.charAt(0) == '/')
            dir = dir.substring(1);
 
        path = dir.split('/');
    }
    else
    {
        dir = getLastPath(location);
        if (dir != '')
        {
            dir = escapeSlash(dir);
            path.push(dir);
        }
    }
 
    return path;
}
 
function abcbox(stringtobox, boxtype, divchar)
{
	// get ascii value of first character 
	var intchar = stringtobox.toUpperCase().charCodeAt(0);
 
	// check for numbers
	if ( (intchar >= 48) && (intchar <= 57) )
	{
		return divchar + '0-9' + divchar;
	}
	// check for other characters
	if ( !((intchar >= 65) && (intchar <= 90)) )
	{
		return divchar + '^\&#\'' + divchar;
	}
	// all other characters are letters
 
	// definition of box types, adjust to your own needs
	// as a start: the number is the same as the number of boxes, evenly spaced ... more or less.
	switch (boxtype)
	{
	case 1:
		var boxwidth = new Array();
		boxwidth[0] = 26;                             // one large box of 26 letters
		break;
	case 2:
		var boxwidth = new Array(13,13);              // two boxes of 13 letters
		break;
	case 3:
		var boxwidth = new Array(8,9,9);              // and so on ...
		break;
	case 4:
		var boxwidth = new Array(7,6,7,6);
		break;
	case 5:
		var boxwidth = new Array(5,5,5,6,5);
		break;
	case 6:
		var boxwidth = new Array(4,5,4,4,5,4);
		break;
	case 7:
		var boxwidth = new Array(4,3,4,4,4,3,4);
		break;
	case 9:
		var boxwidth = new Array(5,5,5,4,1,6);        // When T is a large box...
		break;
	default:
		var boxwidth = new Array(5,5,5,6,5);
		break;
	}
 
	// check for a total of 26 characters for all boxes
	charttl = 0;
	for (cb=0;cb<boxwidth.length;cb++) { charttl = charttl + boxwidth[cb]; }
	if (charttl != 26) {
		print("Error in box-definition, length is " + charttl + ". Check the file common.js" );
		// maybe an exit call here to stop processing the media ??
		end;
	}
 
	// declaration of some variables
	boxnum=0;                         // boxnumber start
	sc=65;                            // first ascii character (corresponds to 'A')
	ec=sc + boxwidth[boxnum] - 1;     // last character of first box
 
	// loop that will define first and last character of the right box
	while (intchar>ec)
	{
		boxnum++;                         // next boxnumber
		sc = ec + 1;                      // next startchar
		ec = sc + boxwidth[boxnum] - 1;   // next endchar
	}
 
	// construction of output string
	output = divchar;
	for (i=sc;i<=ec;i++) {
		output = output + String.fromCharCode(i);
	}
	output = output + divchar;
	return output;
}

 

Et voilà mon fichier import.js:

function addVideo(obj)
{
	const _GENRE_ACTION_POLICIER = "__ACTION_POLICIER"
	const _GENRE_ANIMATION = "__ANIMATION"
	const _GENRE_COMBAT = "__COMBAT"
	const _GENRE_AVENTURE_SF = "__AVENTURE_SF"
	const _GENRE_GUERRE_PEPLUM_WESTERN = "__GUERRE_PEPLUM_WESTERN"
	const _GENRE_COMEDIE = "__COMEDIE"
	const _GENRE_ROMANCE_DRAME = "__ROMANCE_DRAME"
	const _GENRE_HORREUR = "__HORREUR"
	const _GENRE_MUSICAL = "__MUSICAL"
 
	var genres = new Array(_GENRE_ACTION_POLICIER, _GENRE_ANIMATION, _GENRE_COMBAT, _GENRE_AVENTURE_SF, _GENRE_GUERRE_PEPLUM_WESTERN, _GENRE_COMEDIE, _GENRE_ROMANCE_DRAME, _GENRE_HORREUR, _GENRE_MUSICAL);
	var location = obj.location.split('/');
 
	var chain = new Array();
 
	if (location[1].toLowerCase() == "zfilms")
	{
		var title = StripFirst(getLastPath(obj.location));
 
		/*****************
		* ALPHABETIQUE
		*****************/
		//Ajout dans la section ALL
		chain = new Array('-MOVIES-', '-ALL-', title);
		addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER);
 
		//Ajout dans la bonne lettre
		chain = new Array('-MOVIES-', '-ALPHABETICAL-', abcbox(title, 6, '-'), title);
		addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER);
 
		/*****************
		* GENRE
		*****************/
		var locationU = obj.location.toUpperCase();
		for (i=0; i < genres.length;i++)
		{
			if (locationU.indexOf(genres[i], 0) != -1)
			{
				var g = StripFirst(genres[i] ,"__");
				chain = new Array('-MOVIES-', '-GENRES-', g, title);
				addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER);
			}
		}
 
		/*****************
		* TRILOGIE
		*****************/
	}
	else if (location[1].toLowerCase() == "zseries")
	{
		var title = getSeriePath(obj.location);
		var titleclean = StripFirst(getSeriePath(obj.location));
		/*****************
		* ALPHABETIQUE
		*****************/
		//Ajout dans la section ALL
		chain = new Array('-SERIES-', '-ALL-');
		chain = CreateSerieArray(chain, location);
		addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER);
 
		//Ajout dans la bonne lettre
		chain = new Array('-SERIES-', '-ALPHABETICAL-', abcbox(titleclean, 6, '-'));
		chain = CreateSerieArray(chain, location);
		addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER);
 
		/*****************
		* COMPLET
		*****************/
		if (title.substring(0, 1) == "_")
		{
			chain = new Array('-SERIES-', '-COMPLET-');
			chain = CreateSerieArray(chain, location);
			addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER);
		}
 
		/*****************
		* INCOMPLET
		*****************/
		else
		{
			chain = new Array('-SERIES-', '-INCOMPLET-');
			chain = CreateSerieArray(chain, location);
			addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER);		
		}
	}	
}
 
function addAudio(obj)
{
 
    var desc = '';
    var artist_full;
    var album_full;
 
    // first gather data
    var title = obj.meta[M_TITLE];
    if (!title) title = obj.title;
 
    var artist = obj.meta[M_ARTIST];
    if (!artist) 
    {
        artist = 'Unknown';
        artist_full = null;
    }
    else
    {
        artist_full = artist;
        desc = artist;
    }
 
    var album = obj.meta[M_ALBUM];
    if (!album) 
    {
        album = 'Unknown';
        album_full = null;
    }
    else
    {
        desc = desc + ', ' + album;
        album_full = album;
    }
 
    if (desc)
        desc = desc + ', ';
 
    desc = desc + title;
 
    var date = obj.meta[M_DATE];
    if (!date)
    {
        date = 'Unknown';
    }
    else
    {
        date = getYear(date);
        desc = desc + ', ' + date;
    }
 
    var genre = obj.meta[M_GENRE];
    if (!genre)
    {
        genre = 'Unknown';
    }
    else
    {
        desc = desc + ', ' + genre;
    }
 
    var description = obj.meta[M_DESCRIPTION];
    if (!description) 
    {
        obj.meta[M_DESCRIPTION] = desc;
    }
 
// uncomment this if you want to have track numbers in front of the title
// in album view
 
/*    
    var track = obj.meta[M_TRACKNUMBER];
    if (!track)
        track = '';
    else
    {
        if (track.length == 1)
        {
            track = '0' + track;
        }
        track = track + ' ';
    }
*/
    // comment the following line out if you uncomment the stuff above  :)
    var track = '';
 
    var chain = new Array('-MUSICS-', 'All Audio');
    obj.title = title;
    addCdsObject(obj, createContainerChain(chain));
 
    chain = new Array('-MUSICS-', 'Artists', artist, 'All Songs');
    addCdsObject(obj, createContainerChain(chain));
 
    chain = new Array('-MUSICS-', 'All - full name');
    var temp = '';
    if (artist_full)
        temp = artist_full;
 
    if (album_full)
        temp = temp + ' - ' + album_full + ' - ';
    else
        temp = temp + ' - ';
 
    obj.title = temp + title;
    addCdsObject(obj, createContainerChain(chain));
 
    chain = new Array('-MUSICS-', 'Artists', artist, 'All - full name');
    addCdsObject(obj, createContainerChain(chain));
 
    chain = new Array('-MUSICS-', 'Artists', artist, album);
    obj.title = track + title;
    addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC_ALBUM);
 
    chain = new Array('-MUSICS-', 'Albums', album);
    obj.title = track + title; 
    addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC_ALBUM);
 
    chain = new Array('-MUSICS-', 'Genres', genre);
    addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC_GENRE);
 
    chain = new Array('-MUSICS-', 'Year', date);
    addCdsObject(obj, createContainerChain(chain));
}
 
function addWeborama(obj)
{
    var req_name = obj.aux[WEBORAMA_AUXDATA_REQUEST_NAME];
    if (req_name)
    {
        var chain = new Array('-Online Services-', '-Weborama-', req_name);
        addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_PLAYLIST_CONTAINER);
    }
}
 
function addImage(obj)
{
    var chain = new Array('-Photos-', '-All Photos-');
    addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER);
 
    var date = obj.meta[M_DATE];
    if (date)
    {
        var dateParts = date.split('-');
        if (dateParts.length > 1)
        {
            var year = dateParts[0];
            var month = dateParts[1];
 
            chain = new Array('-Photos-', '-Year-', year, month);
            addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER);
        }
 
        chain = new Array('-Photos-', '-Date-', date);
        addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER);
    }
 
    var dir = getRootPath(object_root_path, obj.location);
 
    if (dir.length > 0)
    {
        chain = new Array('-Photos-', '-Directories-');
        chain = chain.concat(dir);
 
        addCdsObject(obj, createContainerChain(chain));
    }
}
 
function addYouTube(obj)
{
    var chain;
 
    var temp = parseInt(obj.aux[YOUTUBE_AUXDATA_AVG_RATING], 10);
    if (temp != Number.NaN)
    {
        temp = Math.round(temp);
        if (temp > 3)
        {
            chain = new Array('-Online Services-', '-YouTube-', '-Rating-', 
                                  temp.toString());
            addCdsObject(obj, createContainerChain(chain));
        }
    }
 
    temp = obj.aux[YOUTUBE_AUXDATA_REQUEST];
    if (temp)
    {
        var subName = (obj.aux[YOUTUBE_AUXDATA_SUBREQUEST_NAME]);
        var feedName = (obj.aux[YOUTUBE_AUXDATA_FEED]);
        var region = (obj.aux[YOUTUBE_AUXDATA_REGION]);
 
        chain = new Array('-Online Services-', '-YouTube-', temp);
 
        if (subName)
            chain.push(subName);
 
        if (feedName)
            chain.push(feedName);
 
        if (region)
            chain.push(region);
 
        addCdsObject(obj, createContainerChain(chain));
    }
}
 
function addTrailer(obj)
{
    var chain;
 
    chain = new Array('-Online Services-', '-Apple Trailers-', '-All Trailers-');
    addCdsObject(obj, createContainerChain(chain));
 
    var genre = obj.meta[M_GENRE];
    if (genre)
    {
        genres = genre.split(', ');
        for (var i = 0; i < genres.length; i++)
        {
            chain = new Array('-Online Services-', '-Apple Trailers-', '-Genres-',
                              genres[i]);
            addCdsObject(obj, createContainerChain(chain));
        }
    }
 
    var reldate = obj.meta[M_DATE];
    if ((reldate) && (reldate.length >= 7))
    {
        chain = new Array('-Online Services-', '-Apple Trailers-', '-Release Date-',
                          reldate.slice(0, 7));
        addCdsObject(obj, createContainerChain(chain));
    }
 
    var postdate = obj.aux[APPLE_TRAILERS_AUXDATA_POST_DATE];
    if ((postdate) && (postdate.length >= 7))
    {
        chain = new Array('-Online Services-', '-Apple Trailers-', '-Post Date-',
                          postdate.slice(0, 7));
        addCdsObject(obj, createContainerChain(chain));
    }
}
 
// main script part
 
if (getPlaylistType(orig.mimetype) == '')
{
    var arr = orig.mimetype.split('/');
    var mime = arr[0];
 
    // var obj = copyObject(orig);
 
    var obj = orig; 
    obj.refID = orig.id;
 
    if (mime == 'audio')
    {
        if (obj.onlineservice == ONLINE_SERVICE_WEBORAMA)
            addWeborama(obj);
        else
            addAudio(obj);
    }
 
    if (mime == 'video')
    {
        if (obj.onlineservice == ONLINE_SERVICE_YOUTUBE)
            addYouTube(obj);
        else if (obj.onlineservice == ONLINE_SERVICE_APPLE_TRAILERS)
            addTrailer(obj);
        else
            addVideo(obj);
    }
 
    if (mime == 'image')
    {
        addImage(obj);
    }
 
    if (orig.mimetype == 'application/ogg')
    {
        if (orig.theora == 1)
            addVideo(obj);
        else
            addAudio(obj);
    }
}

[JS] Supprimer le cache de TableSorter jQuery

jquery-tablesorter

TableSorter est une extension javascript de jQuery qui permet d’utiliser des fonctionnalités de filtre et de tri sur les tables HTML.

Simple à utiliser, je l’utilise dans quasiment tous mes projets Web mais aujourd’hui j’ai été confronté à un problème.

En effet, j’ai une page HTML dans laquelle j’ai une table qui utilise TableSorter.
Les lignes de cette table sont mises à jour chaque minute via une requête ajax.

Le problème est que le plugin TableSorter met en cache chaque ligne (<tr>…</tr>). Du coup si je clique sur une colonne pour la trier après avoir eu 5 rafraichissements, je me retrouve avec toutes les lignes depuis 5 minutes, ce qui rend la table inutilisable.

La solution est donc de supprimer le cache de TableSorter. Malheureusement, cette fonctionnalité n’est pas disponible dans la version originale. Il nous faut donc la modifier.

Pour celà, ouvrez le fichier jquery.tablesorter.js et remplacer:

$this.bind("update", function () {
	var me = this;
	setTimeout(function () {
		// rebuild parsers.
		me.config.parsers = buildParserCache(me, $headers);
		// rebuild the cache map
		cache = buildCache(me);
	}, 1);

par le code suivant:

$this.bind("update", function (e) {
	var me = this;
	setTimeout(function () {
		// rebuild parsers.
		me.config.parsers = buildParserCache(me, $headers);
		// rebuild the cache map
		cache = buildCache(me);
		if(e.resort||e.re_sort) { $(me).trigger("sorton", [config.sortList]); }
		if(e.callback) { e.callback.call(me, config); }
	}, 1);

Ensuite à chaque fois que vous mettez à jours les lignes de votre table, il vous suffit d’appeler:

$("#matable").trigger({type:'update', resort:true});

Et voilà, le cache est désormais supprimé et vous pouvez trier votre table correctement.

Si vous souhaitez vous pouvez télécharger la version modifiée jquery.tablesorter.js