Aller au contenu

Outil De Conversion Xmltv Vers Epg Videostation


Messages recommandés

Posté(e)

Lien envoyé par MP.

Merci pour ton aide

Lien vers script modifié envoyé en MP aussi

On continue les echanges en MP pour tester si c'est OK et une fois confirmé je diffuse cette version.

Posté(e) (modifié)

Merci à CoolRaoul pour son nouveau script pour les possesseur de HdHomerun.

Ca marche impec.

Je lui laisse le soin de diffuser celui-ci.

Attention, le HdHomerun possédant 2 tuners, verifier le nom des fichiers de configuration des chaines (0channels.conf et 1channels.conf normalement) et lancer le script pour chaque tuner afin que l'EPG apparaisse pour chacun d'eux.

Je laisse CoolRaoul vous dire (comme à moi) comment faire pour cela, je préfère qu'il n'y ait qu'un seul interlocuteur pour cela.

Encore Merci !

Modifié par aj13fr
Posté(e) (modifié)

Lien vers la dernière version ajouté à la fin du 1er post du fil.

Note: je rappelle qu'il est possible de spécifier où se trouve channels.conf sans modifier le script.

Suffit de donner si nécessaire ce chemin en paramètre lors de l'appel, comme cela:

xml2epg.pl -C <chemin du fichier channels.conf> <url du xml tv>

De façon similaire, le chemin du répertoire EPG est aussi parametrable, switch "-d":

xml2epg.pl -C <chemin du fichier channels.conf> -d <dossier EPG> <url du xml tv>
Modifié par CoolRaoul
  • 2 semaines après...
Posté(e) (modifié)

Merci CoolRAoul pour ton script et ce super Tuto,

J'ai passé mon samedi apm à mettre en place la programmation journalière de la récupération de l'EPG pour un synology DS209 et Dsm 4.2.

Tout semble bien fonctionner excepté les chaines avec des noms comportant des caractères accentués dans le fichier xml. (France Ô, La chaîne Parlementaire, ....)

J'ai beau essayer de modifier le fichier xml2epg.conf avec divers éditeurs sous Windows ou même directement sur le Synology avec l'editeur Nano rien n'y fait...

Modifié par philou42
Posté(e)

J'ai beau essayer de modifier le fichier xml2epg.conf avec divers éditeurs sous Windows ou même directement sur le Synology avec l'editeur Nano rien n'y fait...

Pour les accents, le fichier doit être encodé en UTF8.

Sous Windows, je te conseille d'utiliser Notepad++ configuré comme ceci:

gNy6K5k.png

  • 8 mois après...
  • 4 semaines après...
  • 2 ans après...
Posté(e) (modifié)

Bonjour,

Je viens de tomber sur ce post. Est-ce que ce script est toujours d'actualité ?

Pour l'instant j'utilise DVBLink sur un DS415Play avec un HDHomeRun.

J'ai choisi DVBLink car il gère l'import XMLTV de mon compte Kazer.org mais je ne dirais pas non à un retour vers Video Station ... avec prise en charge du XMLTV of course :smile:

Cordialement - Jean-Paul

Modifié par JPR
  • 8 mois après...
Posté(e)

Bonjour,  

J'utilise https://www.xmltvlistings.com/ pour obtenir mon xmltv et j'habite au Canada.  Je ne sais pas pour l'Europe mais ici (ATSC) le sevice ID n'est pas unique ce qui fait que le script ne mappais pas convenablement le contenu du XML aux fichiers epg.   J'ai donc du l'adapter pour utiliser un unique id à la place.  Cette version devrait fonctionner dans tous les scénario.  Désolé si le code n'utilise pas les best practice j'ai du faire un "crash course" sur perl en une soirée...

 

Voici la version modifié:

#! /usr/bin/perl
#
#
#	$Id: xml2epg.pl,v 2.14 2014/05/12 07:50:57 root Exp $
#
$ENV{PATH} = "/usr/syno/bin:/usr/syno/sbin:/bin:/sbin:/usr/bin:/usr/sbin";
use strict;
use warnings;
use File::Basename;
use File::Temp qw/ tempfile  /;
use Time::Local;
use Getopt::Long;
use POSIX qw(locale_h);
use IO::File;

my $progname=basename($0);

#+
#	use utf8 for terminal output if env specifies utf8 
#-
if (setlocale(LC_CTYPE) =~ /utf8/) {
    binmode STDOUT, ":encoding(UTF-8)";
    binmode STDERR, ":encoding(UTF-8)";
}

#+
#	ALL FileIO is utf8
#-
use open ':encoding(utf8)';

my $debug=0;
my $quiet=0;
my $progress=0;
my $notafter;
my $vsdir_path = "/usr/syno/etc/packages/VideoStation";

#	default EPG directory
my $epgdir_path;
for my $d (qw (EPGs/0EPG EPG)) {
    $epgdir_path = "$vsdir_path/$d";
    last if -d $epgdir_path;
}

#	default channels.conf path
my $channels_conf_path;
for my $c (qw (channels/0channels.conf channels.conf)) {
    $channels_conf_path = "$vsdir_path/$c";
    last if -r $channels_conf_path;
}

my $myconf_path = "/usr/local/etc/xml2epg.conf";
my $now = time();

sub info  {
    print STDERR "$progname: @_.\n" unless $quiet;
}

sub debug {
    my $level=shift;
    print STDERR "DEBUG: @_\n" if $debug >= $level;
}

#+
#	format "YYYYMMDDhhmmss[ <utc offset>] string to binary time
#-
sub str2time {
    my $string=shift;

    my ( $year, $mon, $mday, $hour, $min, $sec, $sign_off, $hr_off, $min_off) = 
      ($string =~ /^
		   (\d{4})
		   (\d{2})
		   (\d{2})
		   (\d{2})
		   (\d{2})
		   (\d{2})
		   \s*
		   (?:([+-])?(\d{2})(\d{2}))?$/x);

    if (defined $hr_off) {
      #	time is "UTC <offset>"
      my $offset = ($hr_off * 60 + $min_off) * 60; # seconds
      $offset = - $offset if $sign_off eq "-";
      return  timegm($sec, $min, $hour, $mday, $mon-1, $year) - $offset;
    } else {
      #	time is local
      return  timelocal($sec, $min, $hour, $mday, $mon-1, $year) ;
    }

    
}

#================================================================================

sub usage {
  my $status=shift;
  print STDERR <<EOF;
Arguments:
          --debug|-D 
          --quiet|-q
          --progress|-p
          --epgdir|-d <epg output directory>
          --conf|-s <config file path>
          --chanconf|-C <channels.conf path>
EOF
  exit $status;
}
#+
#	command switches
#-
my $help;
Getopt::Long::Configure ("bundling");
GetOptions (
	    'debug|D+' => \$debug,
	    'quiet|q+' => \$quiet,
	    'epgdir|d=s' => \$epgdir_path,
	    'progress|p' => \$progress,
	    'conf|c=s' => \$myconf_path,
	    'chanconf|C=s' => \$channels_conf_path,
	    'maxdays|m=s' => sub {$notafter = $now + ($_[1] * 60 * 60 * 24)},
	    'help|?' => \$help,
	   ) or usage(1);

usage(0) if $help;

my $arg = shift or do {
  info "missing argument";
  usage(1);
};

-d $epgdir_path or do {
  info  "invalid dir $epgdir_path ($!)";
  exit(1);
};



sub quote {
    my $string = shift;
    $string =~ s/"/\\"/g;
    return "\"" . $string . "\"";
}

my %xml2confchname = ();

#+
#	parse my conf
#-
{
    info ("parsing $myconf_path");
    open my $myconf_h, "<", "$myconf_path" or die "$! opening $myconf_path\n";
    while (<$myconf_h>) {
	s/#.*$//;		# uncomment;
	next if /^\s*$/;	# skip blank lines;
	my ($verb, $text) = /^\s*(\S*)\s+(.*\S)\s*$/;
	defined($verb) or do {
	  info "$myconf_path:$.:syntax error";
	  exit(1);
	} ;
	if ($verb eq "defname") {
	    #	trailing spaces have already been suppressed
	    my ($xmlname, $confname) = ($text =~ /^(\S+)\s+(.*)$/);
	    die "syntax error:\n$_" unless defined $xmlname && defined $confname;
	    $xml2confchname{lc $xmlname} = $confname;
	    debug(1,"mapping \"$xmlname\" -> \"^$confname.*\"")
	} else {
	    die "unknown $verb : $verb\n";
	}
    }
    info ("done");
}

my %chan_conf_name2sid = ();
my %sid2suffix = ();
my %uniqueid = ();
#+
#	parse_channel_conf
#-
{
    my $chaconf_h = new IO::File;
    info ("parsing $channels_conf_path");
    open $chaconf_h, "<", $channels_conf_path or die "$! opening $channels_conf_path\n";
    my $prefix;
    $chaconf_h->read ($prefix,2) == 2 or die "$! reading prefix\n";
    $chaconf_h->seek(0,0);          # rewind
    my $chname;
    my $sid;
    my $suffix;
    if ( $prefix eq '[{') {
      #	hdhomerun format
      my $chaconf_s = $chaconf_h->getline();

      while ($chaconf_s =~ /{(.*?)}/g) {
          # sample chaconf_s: "frequency":778000000,"service_id":513,"title":"D8"
          foreach (split /,/, $1) {
              my ($name, $value) = /^"?(.*?)"?:"?(.*?)"?$/;
              $chname = $value if $name eq "title";
              $suffix = $value if $name eq "frequency";
              $sid = $value if $name eq "service_id";
          }
          debug(1, "SID=$sid, NAME=$chname, SUFFIX= $suffix");
          $chan_conf_name2sid{$chname} = $sid;
		  $uniqueid{$chname} = $sid . "@" . $suffix;
          $sid2suffix{$sid} = $suffix;
      }
    } else {
      while (<$chaconf_h>) {
        next unless /:/;
        s/\s*$//;			# remove trailing cr nl and spacing
        my @fields = split(/:/);
        $chname=$fields[0];
        $sid=$fields[-1];
        $suffix=$fields[1];
        $chan_conf_name2sid{$chname} = $sid;
		$uniqueid{$chname} = $sid . "@" . $suffix;
        $sid2suffix{$sid} = $suffix;
        debug(1, "SID=$sid, NAME=$chname, SUFFIX= $suffix");
      }
    }
    info ("done");
}

#+
#	arg can be "http://<file>" | "file.zip" | "file.xml"
#-

my $xmlfile;

if ($arg  =~ /^http/) {
    my $zipfile;
    (undef , $zipfile) = tempfile(DIR => "/tmp", 
				  TEMPLATE => "epg-XXXXXX",  
				  SUFFIX => ".zip" , 
				  UNLINK => 1);
    info "fetching $arg";
    die "wget error\n" if system("wget -nv -O $zipfile  $arg") > 0;
    $arg = $zipfile;
    info "fetched";
}

if ($arg  =~ /.zip$/) {
    (undef , $xmlfile) = tempfile(DIR => "/tmp", 
				  TEMPLATE => "epg-XXXXXX", 
				  SUFFIX => ".xml", 
				  UNLINK => 1);
    info "unzipping $arg";
    die "unzip error\n" if  system("unzip -p $arg > $xmlfile") > 0;
    info  "unzipped";
} else {
    $xmlfile = $arg;
}

### Poor man XML parse
info ("parsing XMLTV file");
open my $xml_h, "<", $xmlfile or die "$! opening $xmlfile\n";

my $channel_id;	      		 
my $cur_chaname;		# display name 
my $curprog;			# current TV program struct (ref to hash)
my %progs = ();			# $progs{<channel_id>} : ref to array of "progs"
my %id2name = ();

while (<$xml_h>) {
	if (/<channel id="([^"]*?)">/) {
		# if (/<channel id="(.*)">/) {
		$channel_id=$1;
		next;
	}

	if (/<display-name>(.*)<\/display-name>/) {
		$id2name{$channel_id} = $1;
		next;
	}

	if (/^\s+<(title|desc|sub-title).*?>(.*)<\/\1/) {
		$curprog->{$1} = $2;
		next;
	} 

	if (my ($start_s, $stop_s, $chan) = 
	(/^
		\s*<programme\s+start="(.*?)".*
		\s+stop="(.*?)".*
		\s+channel="(.*?)"/x
	)) {
		my $start=str2time($start_s);
		my $stop=str2time($stop_s);
		$cur_chaname = $id2name{$chan};
		$curprog = {
			duration =>   ($stop - $start) ,
			start_time => $start,
			text_name => "",
		};

		next;
	}

	if (/^\s+<\/programme>/) {
		print STDERR  "." if  $progress;

		my $start_time = $curprog->{start_time};
		my $duration = $curprog->{duration};
		next if ($start_time + $duration < $now); # too late
		next if (defined($notafter) && ($start_time > $notafter));

		push @{$progs{$cur_chaname}}, $curprog ;
	}
}


if ($debug) {
    foreach (keys %id2name) {
		debug (1,"XMLNAME $_ -> $id2name{$_}");
    } 
}
	


print STDERR  "\n" if $progress;
info ("parsed");

chdir $epgdir_path or die "chdir $epgdir_path, $!\n";

#+
#	in: channel_name (from xml)
#	out: unique service ids array
#-

my %ignoring=();
sub cha2uid {
	my $xml_chaname = shift;

	my @uids = ();
	return @uids if ($ignoring{$xml_chaname});

	my $squeezed_xml_chaname = $xml_chaname;
	$squeezed_xml_chaname  =~ s/\s//g;	# remove spacing
	#my ($xmlname, $confname) = ($text =~ /^(\S+)\s+(.*)$/);
	my $vs_chaname_prefix=$xml2confchname{lc($squeezed_xml_chaname)};

	if (! defined($vs_chaname_prefix)) {
		info("mapping for channel $xml_chaname not found!");
		info("add \"defname $squeezed_xml_chaname <channel.conf name>\" in config file");
		info ("ignoring \"$xml_chaname\"");
		$ignoring{$xml_chaname}++;
		return @uids;
	} else {
		debug(1, "xml_chaname=$xml_chaname, squeezed_xml_chaname=$squeezed_xml_chaname, vs_chaname_prefix=$vs_chaname_prefix")
	}

	foreach my $vs_chaname (keys %chan_conf_name2sid) {
		if ($vs_chaname =~ /^$vs_chaname_prefix/i) {
			debug(2, "vs_chaname=$vs_chaname, vs_chaname_prefix=$vs_chaname_prefix, chan_conf_name2sid=$chan_conf_name2sid{$vs_chaname}");
			push @uids, $uniqueid{$vs_chaname}
		}
	}
	debug(1,"uids for $xml_chaname: @uids");
	return @uids;
}

foreach my $chaname (keys %progs) {
	my @sidlist = cha2uid($chaname);


	foreach my $uid  ( @sidlist) {
		#my $unique_id= $sid . "@" . $sid2suffix{$sid};
		my $unique_id=$uid;

		my $epgfile="epg." . $unique_id;

		info "creating $epgfile ($chaname)";
		unlink $epgfile;
		open my $epg_h, ">", "$epgfile" or die "$! opening $epgfile\n";

		$epg_h->print("{\n");
		$epg_h->print("    \"eventdata\" : [\n");

		my $eventid=1;

		my @proglist = sort {$a->{start_time} <=> $b->{start_time}} @{$progs{$chaname}} ;
		my $lastprog = $proglist[-1];
		foreach my $prog (@proglist) {
			my $event_name = $prog->{title};
			$event_name  .= " - " . $prog->{"sub-title"} if exists $prog->{"sub-title"};
			#	remplacer les virgules par des espaces dans le titre
			$event_name  =~ s/,/ /g;
			my $text_name = exists($prog->{desc}) ?  $prog->{desc} : "";
			my $islatest = $prog == $lastprog ;
			debug (2, sprintf ("Chan=%s, id=%s, event=%s",$chaname,$uid, $event_name));
			$event_name .= " - " . $prog->{subtitle} if exists $prog->{subtitle};
			$epg_h->print("      {\n");
			$epg_h->print("         \"duration\" : ", $prog->{duration}, ",\n");
			$epg_h->print("         \"event_id\" : ", $eventid++, ",\n");
			$epg_h->print("         \"event_name\" : ", quote($event_name), ",\n");
			$epg_h->print("         \"finalprogram\" : ", ( $islatest  ? "true" : "false") , ",\n");
			$epg_h->print("         \"start_time\" : ", $prog->{start_time}, ",\n");
			$epg_h->print("         \"text_name\" : ", quote($text_name), "\n");
			$epg_h->print("      }", ($islatest ? "" : ",") , "\n");
		}

		$epg_h->print("   ],\n");
		$epg_h->print("   \"unique_id\" : \"", $unique_id, "\"\n");
		$epg_h->print("}\n");

		$epg_h->close();
	}
}

1;
# Local Variables:
# mode: cperl
# cperl-indent-level: 4
# End:

J'espère qu'elle vous sera utile.

Rejoindre la conversation

Vous pouvez publier maintenant et vous inscrire plus tard. Si vous avez un compte, connectez-vous maintenant pour publier avec votre compte.

Invité
Répondre à ce sujet…

×   Collé en tant que texte enrichi.   Coller en tant que texte brut à la place

  Seulement 75 émoticônes maximum sont autorisées.

×   Votre lien a été automatiquement intégré.   Afficher plutôt comme un lien

×   Votre contenu précédent a été rétabli.   Vider l’éditeur

×   Vous ne pouvez pas directement coller des images. Envoyez-les depuis votre ordinateur ou insérez-les depuis une URL.

×
×
  • Créer...

Information importante

Nous avons placé des cookies sur votre appareil pour aider à améliorer ce site. Vous pouvez choisir d’ajuster vos paramètres de cookie, sinon nous supposerons que vous êtes d’accord pour continuer.