Dimanche, 20 novembre 2011
Wanting to convert mp3's in a K3B project file to an m3u? I got this need this week-end, so here it goes (I got a little bit lazy and have a dependency on the "mp3info" command).
This script is quite easy to extend to other file formats but ok, mp3 is always all around no?
Enjoy.
#!/usr/bin/perl
use strict;
use diagnostics;
use Archive::Extract;
use POSIX qw/EXIT_SUCCESS EXIT_FAILURE strftime/;
use File::Temp qw/tempdir/;
use XML::Simple;
use File::Spec;
use File::Basename;
use IO::File;
use IO::Handle;
use Getopt::Long;
my @k3b = ();
my @output = ();
my $help = 0;
my $verbose = 0;
my $debug = 0;
my $stdout = 0;
GetOptions ("k3b=s" => @k3b,
"output=s" => @output,
"help" => \$help,
"stdout" => \$stdout,
"verbose" => \$verbose,
"debug" => \$debug);
if ($help || (! @k3b && ! @ARGV)) {
print STDERR "Usage: $0 {options} [projecfile1.k3b [projectfile2.k3b [..]]\n";
print STDERR "\n";
print STDERR "where options can be:\n";
print STDERR "\t--k3b K3B project file(s). Can be repeated.\n";
print STDERR "\t--output M3U output file(s). Can be repeated. Value '-' means STDOUT.\n";
print STDERR "\t--help This help.\n";
print STDERR "\t--verbose Verbose mode\n";
print STDERR "\t--debug Debug mode (give the list of sound files found)\n";
print STDERR "\n";
print STDERR "\nif there not enough m3u output file, the default is to guess the m3u output file from the k3b project file, unless stdout option is given in which case output will go to stdout for all remaining project files\n";
print STDERR "\n";
print STDERR "Any additional argument will be considered as K3B project file, except if the very last argument is '-', considered as a forced output to STDOUT\n";
print STDERR "\n";
print STDERR "Examples:\n";
print STDERR "$0 .k3b\n";
print STDERR "$0 -v .k3b\n";
print STDERR "$0 -k /tmp/file1.k3b -k /tmp/file2.k3b /tmp/file3.k3b --o /tmp/out1.m3b\n";
print STDERR "$0 /tmp/file1.k3b -\n";
exit($help ? EXIT_SUCCESS : EXIT_FAILURE);
}
my $nfailure = 0;
if (@ARGV && $ARGV[-1] eq '-') {
#
## --stdout equivalent
#
verb("Taking last argument '-' as --stdout\n");
$stdout = 1;
pop(@ARGV);
}
$verbose = 1 if ($debug);
foreach (@k3b, @ARGV) {
my $k3b = $_;
my $nsound = 0;
verb("==> Processing %s\n", $k3b);
#
## Get output file handle
# ----------------------
my $outfile = shift(@output) || '';
my $out = undef;
if (! $outfile) {
if (! $stdout) {
my($filename, $directories, $suffix) = fileparse($k3b, qr/\.[^.]*/);
$outfile = File::Spec->catfile($directories, "$filename.m3u");
verb("Output file guessed to be %s\n", $outfile);
$out = IO::File->new($outfile, "w");
} else {
verb("Output file forced to be STDOUT\n", $outfile);
$out = *STDOUT;
}
} elsif ($outfile eq '-') {
verb("Output file is given and is STDOUT\n", $outfile);
$out = *STDOUT;
} else {
verb("Output file is given and is %s\n", $outfile);
$out = IO::File->new($outfile, "w");
}
if (! defined($out)) {
print STDERR "Cannot open output " . ($outfile || '') . ", $!\n";
++$nfailure;
next;
}
autoflush $out 1;
#
## Extract the k3b maindata.xml in a tempdir
# -----------------------------------------
verb("Getting a temporary directory\n");
my $dir = tempdir(CLEANUP => 1);
if (! defined($dir)) {
print STDERR "Cannot get a temporary dir, $!\n";
++$nfailure;
next;
}
verb("Temporary directory: %s\n", $dir);
verb("Opening archive $k3b\n");
my $ae = Archive::Extract->new(archive => $k3b, type => 'zip');
if (! defined($ae)) {
print STDERR "Cannot open $k3b, $!\n";
++$nfailure;
next;
}
verb("Extracting as a zip archive $k3b to $dir\n");
if (! $ae->extract(to => $dir)) {
print STDERR "Cannot extract $k3b to $dir" . $ae->error . "\n";
++$nfailure;
next;
}
my $xml = File::Spec->catfile($dir, 'maindata.xml');
verb("Opening $xml\n");
my $ref = undef;
eval {$ref = XMLin($xml, NoAttr => 1); 1;};
if (! defined($ref)) {
print STDERR "Cannot open $xml, $@\n";
++$nfailure;
next;
}
if (! defined($ref->{files})) {
print STDERR "No files in $k3b\n";
next;
}
my $files = $ref->{files};
if (ref($files) ne 'HASH') {
print STDERR "Expecting a hash for files\n";
next;
}
#
## Get the files
# --------------
if (! defined($files->{file})) {
print STDERR "No sound file in $k3b\n";
next;
}
my $filesp = undef;
if (ref($files->{file}) ne 'ARRAY') {
#
## A single file ?
#
if (ref($files->{file}) ne 'HASH') {
print STDERR "Expecting an ARRAW for files->file, got " . ref($files->{file}) . "\n";
next;
} else {
$filesp = [ $ref->{files}->{file} ];
}
} else {
$filesp = $ref->{files}->{file};
}
verb("Writing output\n");
print $out "#EXTM3U\n";
my $i = 0;
foreach (@{$filesp}) {
++$i;
if (ref($_) ne 'HASH') {
print STDERR "Expecting a hash for element No $i in files\n";
next;
}
my $file = $_->{url} || '';
if (! $file) {
print STDERR "No url in element No $i in files\n";
next;
}
my $time = `mp3info -p %S "$file"`;
my $song = `mp3info -p "%a - %t" "$file"`;
$song =~ s/\.mp3$//i;
print $out "#EXTINF:$time,$song\n";
print $out "$file\n";
deb("... Time: %d, Song: %s, File: %s\n", $time, $song, $file);
++$nsound;
}
verb("Got %d sound file%s in %s\n", $nsound, $nsound > 1 ? 's' : '', $k3b);
}
my $rc = $nfailure > 0 ? EXIT_FAILURE : EXIT_SUCCESS;
verb("Exiting with status $rc\n");
exit($rc);
sub verb {
return if (! $verbose);
return(mylog(@_));
}
sub deb {
return if (! $debug);
return(mylog(@_));
}
sub mylog {
my $format = shift;
my $now = strftime "%a %b %e %H:%M:%S %Y", gmtime;
my $string = sprintf($format, @_);
printf STDERR "%s %s", $now, $string;
}
|