The Caribbean Weblog

"This blog is continuing @ http://christophemaximin.com "

Aller au contenu | Aller au menu | Aller à la recherche

mardi 4 avril 2006

ClassTags, pour gérer les tags id3 et vorbis (id3 surtout)

Pour les besoins du site sur lequel je travaille, j'ai fait cette petite classe, qui permet de "gérer" les tags id3v1, id3v2 (ce dernier étant prioritaire) et vorbis des fichiers mp3 et ogg.
Mais en fait, je m'inquiétais surtout pour sa sécurité, surtout au niveau de la méthode tag(), et je n'ai inclus aucun système d'anti-dépassement (éventuel) de buffer, vu que je ne m'y connais pas du tout.
Si un (ou deux) de vos yeux avisés remarquait un trou à ce niveau, ailleurs, ou quelquechose de codé par les pieds, je serais (très) heureux d'en être averti, afin de pouvoir le recharcuter comme il faut.

<?php
/* 
** only support mp3 and ogg tags
** software needed in $PATH : tagtool, id3v2
** methods : getTags( file.[ogg|mp3] ), 
** convertToid3v2( file.[ogg|mp3] ), 
** tag( field , newvalue , file.mp3 , [format)
*/
class ClassTags {
	
	public $status, $file, $result;
	public $tag = array();
	public $taggableF = array(); // formats we can tag
	
	// the three functions following are only about grabbing the tags
	
	function __construct(){
		$this->taggableF = array('mp3');
	}
	
	public function getTags($arg){
		if(!is_file($arg)){
			$this->status = 'NoFile';
			return;
		}
		
		$this->file = escapeshellarg($arg);
		$this->result = shell_exec('tagtool --dump '.$this->file);
		// for avoid to create a notice error when one of these fields is empty and we try to access it 
		$this->tag['title'] = ''; $this->tag['artist'] = '';
		$this->tag['album'] = ''; $this->tag['year'] = '';
		
		if(shell_exec('file -ib '.$this->file) == "application/ogg\n")
			$this->status = $this->getVorbis($this->result);
		else { // if it isn't an ogg, we assumes that's mp3
			$this->result = utf8_decode($this->result);
			if(substr_count($this->result,'---- ID3 v2 ----') 
			&& preg_match("/ID3 v2 ----\n(.*)/s",$this->result,$matches))
				$this->status = $this->getId3($matches[1]);
			elseif(substr_count($this->result,'---- ID3 v1 ----') 
			&& preg_match("/ID3 v1 ----\n(.*)/s",$this->result,$matches)){
				$this->status = $this->getId3($matches[1]);
				// doesn't deserve anything now, but we know that id3v2 is cooler than id3v1 :)
				$this->convertToid3v2($this->file); 
			} elseif(substr_count($this->result,'No ID3 tags.'))
				$this->status = 'NoTags';
			else 
				$this->status = 'UnknowErr';
		}
	}
	
	private function getId3($str){ 
		if(preg_match("/^TIT2 [^\n]*?\n\s+(.*)$/m",$str,$temp))
			$this->tag['title'] = $temp[1];
		if(preg_match("/^TPE1 [^\n]*?\n\s+(.*)$/m",$str,$temp))
			$this->tag['artist'] = $temp[1];
		if(preg_match("/^TALB [^\n]*?\n\s+(.*)$/m",$str,$temp))
			$this->tag['album'] = $temp[1];
		if(preg_match("/^TYER [^\n]*?\n\s+(.*)$/m",$str,$temp))
			$this->tag['year'] = $temp[1];
		if(preg_match("/^TRCK [^\n]*?\n\s+(\d+)\/.*$/m",$str,$temp))
			$this->tag['track'] = $temp[1];
		return true;
	}
	
	private function getVorbis($str){
		if(preg_match("/^TITLE=([^\n]*)$/m",$str,$temp))
			$this->tag['title'] = $temp[1];
		if(preg_match("/^ARTIST=([^\n]*)$/m",$str,$temp))
			$this->tag['artist'] = $temp[1];
		if(preg_match("/^ALBUM=([^\n]*)$/m",$str,$temp))
			$this->tag['album'] = $temp[1];
		if(preg_match("/^DATE=([^\n]*)$/m",$str,$temp)) 
			$this->tag['year'] = $temp[1]; 
			// as you can see, it's not really the year, but we'll use that name in 
		return true;
	}
	
	// it can be (?? don't remember why i wrote this)
	public function convertToid3v2($arg){ // assumes that's a valid mp3 file with NO id3v2 tags AND id3v1 ones
		if(preg_match("/\.\.\. converted\n/",shell_exec('id3v2 --convert '.$this->file)))
			return true;
		else
			return 'UnknowErr';
	}
	
	// set tags
	public function tag($field,$value,$file,$format='mp3'){ // field ISN'T protected
		$escval = escapeshellarg($value);
		$file = escapeshellarg($file);
		if($format == 'mp3'){ // we tag only in id3v2
			if(preg_match("/^(album|year|artist|comment)$/",$field))
				$opt = $field;
			elseif($field == 'title')
				$opt = 'song';
				
			shell_exec("id3v2 --$opt $escval $file");
		}
	}
}
 
?>


Vous aurez remarqué qu'il n'y a rien pour éditer les tags vorbis (je n'ai que très peu cherché en vérité) et que oui, j'utilise un plugin kisenbon.
Donc le tout s'utilise comme ceci :

$file = 'Natsukawa Rimi - SINGLE COLLECTION Vol.1 (Album)/12 famureuta ( komoriuta ).mp3';
// pour le get
$tagg = new ClassTags;
$tagg->getTags($file);
// et vlan, on peut faire un var_dump($leson) pour constater que l'on a accès à $leson->tag['artist'], etc...

// pour le put
$tagg->tag('title','ふぁむれうた(こもりうた)',$file);
$tagg->tag('artist','夏川りみ',$file);

Vous noterez que id3v1 qui ne supporte pas l'unicode va crier à la mort, tout comme geshi, mais tant pis pour lui, vu que l'on taggue id3v2 aussi.

lundi 20 février 2006

"Using prototype"

Vous l'avez cherché, vous en avez rêvé (moi si en tous cas), la voici la voilà : Using prototype.js vx

C'est à vue de nez, la meilleure ressource online sur le sujet.

EDIT: Comme je le pensais, mon maitre l'avais déjà posté ici, la semi-traduction française en tous cas : http://www.electrolinux.com/traductions/prototype.js.html

lundi 13 février 2006

Ajax, Prototype, PHP, GET/POST et HTTP

Parce qu'a force d'avoir des tests à effectuer en live sur vos pages avec prototype, vous avez surement créé un "RequestHandler(.php)", je veux dire par là : un fichier spécialement fait pour traiter les requetes Ajax.

La situation

Sur ce fichier donc, vous envoyez les tests avec Ajax.Request par exemple, vous obtennez le résultat, vous avisez en fonction du résultat retourné de votre fonction onSuccess:

Le problème

Le mieux serait de donner un exemple : Vous avez un formulaire, qui vérifie onBlur sur le champ pseudo que le pseudo tapé n'existe pas dans la base de donnée. On appelle donc Ajax.Request('/RequestHandler.php', method: 'post', postBody: action=verifPseudo&pseudo=pseudo.value); plus un callback onSuccess: par exemple, qui avertira l'internaute par innerHTML qu'il y a un soucis.
Jusqu'ici pas de soucis, l'user peut ignorer le message, notre bon PHP vérifiera encore à la validation. Et c'est bien là que ça commence à devenir ennuyeux :

pourquoi retaper le code PHP qui vérifie l'existence du pseudo dans la bdd ?

Dans notre cas, ça n'est pas important vu que le code tiens tranquillement sur une ligne, mais dans d'autres cas, serait-ce vraiment justifié de faire ainsi ?

La solution (j'ai pas trouvé mieux en tous cas)

L'idée serait donc de faire comme Ajax.Request (et Ajax.Updater), et d'envoyer la requete en post (ou get) à notre RequestHandler. Évidemment on en fera une fonction, vu que le code correspondant en php est (un peu) long :

<?php
function sendData($url, $method, $data){

	$pUrl = parse_url($url);
	$port = ($pUrl['port']) ? $pUrl['port'] : 80;
	
	
	$sock = fsockopen($pUrl['scheme'].$pUrl['host'], $port, $errno, $errstr, 10); 
	if(!$sock)
		die("$errstr ($errno)\n");
	
	fwrite($sock, strtoupper($method)." ".$pUrl['path']." HTTP/1.0\r\n");
	fwrite($sock, "Host: ".$pUrl['host']."\r\n");
	fwrite($sock, "Content-type: application/x-www-form-urlencoded\r\n");
	fwrite($sock, "Content-length: ".strlen($data)."\r\n");
	fwrite($sock, "Accept: */*\r\n");
	fwrite($sock, "\r\n");
	fwrite($sock, $data."\r\n");
	fwrite($sock, "\r\n");
	
	while ($str = trim(fgets($sock, 4096)))
	$headers .= "$str\n";
	
	while (!feof($sock))
	$body .= fgets($sock, 4096);
	
	fclose($sock);
	
	return $body;
} 
?>
  • $url devrait être exprimé comme ceci : unmei.domtomconnection.com/headers/RequestHandler.php
  • Le format de $data est le suivant 'variable1='.urlencode($_POST['variable1']).'&var2='.urlencode($_GET['var2'])
  • N'oubliez pas que si vous faites une requête sur un serveur sécurisé, le port n'est plus 80 mais 443
  • Dans ce cas, on utilise un timeout de 10s pour la connexion
  • Le mieux serait de faire disparaitre le paramètre $url et le rendre interne à la fonction (ou utiliser une var globale valable pour cette fonction et les requests Ajax)

Donc vous faites une requete sur votre RequestHandler.php comme vous l'avez fait avec Ajax, et vous faites ce qu'il y a à faire en fonction du résultat.

(comment ça "inutile" ??)

mardi 7 février 2006

PHP et les calculs

Pour le fun, je voulais mettre un plugin de calcul mathématique à mihiro (mon bot IRC, basé sur Botan), et j'étais naturellement confronté au souci de sécurité, étant donné que le calcul se fait par simple eval("\$resultat = ($calc);");... et pouf :

$calcul_presque_propre = preg_replace("/[^0-9+-\/*]/","",$texte);

Ceci supprime donc tout ce qui n'est pas chiffre, +, -, * ou /.

Mais quelle ne fut pas ma surprise de tenir une telle conversation (alors que tout le monde sait bien que 0318894273>0312547955) :

< Christophe971> !>0318894273-0312547955
< mihiro> 0318894273-0312547955 => -103758

...avant de découvrir que :

< Christophe971> !>42
< mihiro> 42 => 42
< Christophe971> !>042
< mihiro> 042 => 34

... et me voilà confronté à la terrible vérité : PHP traite tous les nombres commençant par zéro en base octale.

Et plutôt que de s'amuser à preg_match_all et à base_converter tout ce qu'il nous plait pas, un simple preg_replace répare ce terrible mal :

$calc = preg_replace("/([+-\/*]*)([0]+)([0-9]+)/","\\1\\3",$calcul_presque_propre);

Et voilà.

Pages: << Page précédente - 1 - 2 - 3 -