diff options
Diffstat (limited to 'vipl')
-rwxr-xr-x | vipl | 161 |
1 files changed, 161 insertions, 0 deletions
@@ -0,0 +1,161 @@ +#!/usr/bin/perl + +use warnings; +use strict; +use File::Spec; +use File::Temp; +use Audio::MPD q{0.19.0}; +use MpdToys; +use Encode; + +=head1 NAME + +vipl - edit mpd playlist + +=head1 SYNOPSIS + +B<vipl> [host] + +=head1 DESCRIPTION + +vipl allows editing the mpd playlist using your text editor. The current +playlist will be brought up in the editor. Delete or rearrange songs as +desired using the editor. + +You can also enter the name of a playlist, or part of the name of an +album, artist, or song. Matching items will be added to the playlist. +Streaming urls can also be entered. + +(If the perl String::Approx module is available, it will be used to handle +typos, etc in the names you enter.) + +The currently playing song is marked with a ">" at the front. To change +which song is playing, just move the ">" to a different song. + +If the hostname is omitted, the MPD_HOST environment variable will be used. + +=head1 AUTHOR + +Copyright 2008 by Joey Hess <joey@kitenet.net> + +Licensed under the GNU GPL. + +=cut + +if (@ARGV) { + $ENV{MPD_HOST}=shift; +} +my $mpd=Audio::MPD->new(conntype => "reuse"); +my $current=$mpd->current; +my $pl=$mpd->playlist; +my %names=map { $_->as_string => $_ } $pl->as_items; +my @origlist=$pl->as_items; + +my $tmp=File::Temp->new(TEMPLATE => "plXXXXX", DIR => File::Spec->tmpdir); +open (OUT, ">".$tmp->filename) || die "$0: cannot create ".$tmp->filename.": $!\n"; +foreach my $song (@origlist) { + print OUT ">" if defined $current && $song->id eq $current->id; + print OUT encode_utf8($song->as_string); + print OUT "\n"; +} +close OUT || die "$0: cannot write ".$tmp->filename.": $!\n"; + +my @editor="vi"; +if (-x "/usr/bin/editor") { + @editor="/usr/bin/editor"; +} +if (exists $ENV{EDITOR}) { + @editor=split(' ', $ENV{EDITOR}); +} +if (exists $ENV{VISUAL}) { + @editor=split(' ', $ENV{VISUAL}); +} +my $ret=system(@editor, $tmp); +if ($ret != 0) { + die "@editor exited nonzero, aborting\n"; +} + +my $changed=0; +my @list; +open (IN, "<".$tmp->filename) || die "$0: cannot read ".$tmp->filename.": $!\n"; +my $playing; +my $num=0; +while (<IN>) { + chomp; + $_=decode_utf8($_); + + if (s/^>\s*//) { + $playing=$num; + } + + if (exists $names{$_}) { + push @list, $names{$_}; + if (! $changed && !($origlist[$#list] && + $names{$_}->file eq $origlist[$#list]->file)) { + $changed=1; + } + $num++; + } + else { + next if /^\s*$/; + + my @matches=MpdToys::findmatchingsongs($_, $mpd); + if (! @matches && MpdToys::canmatch_fuzzy()) { + @matches=MpdToys::findmatchingsongs_fuzzy($_, $mpd); + } + + if (! @matches) { + print STDERR "$0: no matches for \"$_\"\n"; + } + else { + foreach (@matches) { + push @list, $_; + } + $changed=1; + $num+=@matches; + } + } +} +close IN; +if ($#list != $#origlist) { + $changed=1; +} + +if (! $changed) { + # yay for optimisation! +} +elsif ($current && grep { $_->file eq $current->file } @list) { + # Avoid touching the currently playing song, while changing + # the playlist around it. + $pl->crop; + my $pos=0; + my $past=0; + foreach my $song (@list) { + if (! $past && $song->file eq $current->file) { + $past=1; + # move current song into right location + $pl->move(0, $pos); + } + else { + $pl->add($song->file); + } + $pos++; + } + $mpd->play if @list; +} +else { + $pl->clear; + foreach my $song (@list) { + $pl->add($song->file); + } +} + +if (defined $playing) { + if (! defined $mpd->status->song || $playing ne $mpd->status->song || + $mpd->status->state ne 'play') { + $mpd->play($playing); + } +} +else { + $mpd->play(0) if @list; +} |