Репозиторий Sisyphus
Последнее обновление: 1 октября 2023 | Пакетов: 18631 | Посещений: 37534558
en ru br
Репозитории ALT
S:0.24-alt1.git20131103
5.1: 0.20-alt1
www.altlinux.org/Changes

Группа :: Звук
Пакет: mpdtoys

 Главная   Изменения   Спек   Патчи   Исходники   Загрузить   Gear   Bugs and FR  Repocop 

pax_global_header00006660000000000000000000000064112600163550014511gustar00rootroot0000000000000052 comment=7c666e022d0ad7077f95beb165fc13e3dd87045c
mpdtoys-0.20/000075500000000000000000000000001126001635500131275ustar00rootroot00000000000000mpdtoys-0.20/Makefile000064400000000000000000000012271126001635500145710ustar00rootroot00000000000000progs=mpstore mprev mpgenplaylists mpfade sats mpskip mptoggle mprand \
mpinsert vipl mprompt

PERLLIBDIR=$(shell perl -e 'use Config; print $$Config{installvendorlib}')

build:

install:
install -d $(DESTDIR)/usr/bin/
install $(progs) $(DESTDIR)/usr/bin/

install -d $(DESTDIR)/usr/share/man/man1/
for prog in $(progs); do \
pod2man -c $$prog $$prog > $(DESTDIR)/usr/share/man/man1/$$prog.1; \
done

for link in mpload mpcp mpmv mpswap; do \
ln -sf mpstore $(DESTDIR)/usr/bin/$$link; \
ln -sf mpstore.1 $(DESTDIR)/usr/share/man/man1/$$link.1; \
done

install -d $(DESTDIR)/$(PERLLIBDIR)
install -m 0644 MpdToys.pm $(DESTDIR)/$(PERLLIBDIR)
mpdtoys-0.20/MpdToys.pm000064400000000000000000000021641126001635500150670ustar00rootroot00000000000000#!/usr/bin/perl
package MpdToys;

sub findmatchingsongs {
my $term=shift;
my $mpd=shift;

# pass urls through
if ($term=~/^(\w+):\/\//) {
return Audio::MPD::Common::Item->new(file => $term);
}

my $coll=$mpd->collection;

my $exactmatch=$coll->song($term);
if ($exactmatch) {
return $exactmatch;
}

my @matches = (
$coll->songs_from_album_partial($term),
$coll->songs_by_artist_partial($term),
$coll->songs_with_title_partial($term),
);

return dedupsongs(@matches);
}

sub canmatch_fuzzy {
eval q{use String::Approx 'amatch'};
return ! $@;
}

sub findmatchingsongs_fuzzy {
my $term=shift;
my $mpd=shift;

my $coll=$mpd->collection;
my @matches = (
(map { $coll->songs_from_album($_) }
amatch($term, ['i'], $coll->all_albums())),
(map { $coll->songs_by_artist($_) }
amatch($term, ['i'], $coll->all_artists()))
);

# very slow, only try if nothing else matched
@matches = amatch($term, ['i'], $coll->all_songs())
if ! @matches;

return dedupsongs(@matches);
}

sub dedupsongs {
my %seen;
my @ret;
foreach my $song (@_) {
push @ret, $song unless $seen{$song->file}++;
}
return @ret;
}

1
mpdtoys-0.20/TODO000064400000000000000000000003411126001635500136150ustar00rootroot00000000000000mprm: delete the currently playing file, once it finishes playing
sats: If mpd has one song in the playlist, and has it on repeat, sats will
never stop it playing, since it waits for mpd to move to the next
song.
mpdtoys-0.20/debian/000075500000000000000000000000001126001635500143515ustar00rootroot00000000000000mpdtoys-0.20/debian/changelog000064400000000000000000000107251126001635500162300ustar00rootroot00000000000000mpdtoys (0.20) unstable; urgency=low

* Work around bug #548305.

-- Joey Hess <joeyh@debian.org> Sun, 27 Sep 2009 22:14:06 -0400

mpdtoys (0.19) unstable; urgency=low

* Factored out matching code from mprompt and vipl.
* mprompt: Enable fuzzy matching by default, if the String::Approx module
is available. The -f flag is deprecated.
* vipl: Also enable fuzzy matching by default.
* vipl: Fix inserting/preservation of streaming urls in the playlist.
* mprompt, vipl: If the entered value exactly matches a file in the
mpd collection, use it without doing further matching.
* mpinsert: Enable partial/whole album/artist/fuzzy matching using
same code used for mprompt and vipl.

-- Joey Hess <joeyh@debian.org> Tue, 22 Sep 2009 21:40:06 -0400

mpdtoys (0.18) unstable; urgency=low

* mprompt: Configure getopt to not ignore case.

-- Joey Hess <joeyh@debian.org> Wed, 03 Jun 2009 17:52:32 -0400

mpdtoys (0.17) unstable; urgency=low

* mprompt: Rename terse switch to -T, -t was already taken.

-- Joey Hess <joeyh@debian.org> Mon, 18 May 2009 18:31:48 -0400

mpdtoys (0.16) unstable; urgency=low

* mpinsert: Don't insert multiple items in reverse order when
not playing.
* mpinsert: Only display number of first item added with -n.

-- Joey Hess <joeyh@debian.org> Sat, 09 May 2009 20:23:17 -0400

mpdtoys (0.15) unstable; urgency=low

* mprompt: Add -t switch, enabling a terse output mode where the output
is intended to be piped to a speech synth such as esound.
* mpinsert: Add -n switch, which prints the playlist position of
added items. This is useful if you want to insert an item and then jump
to it.

-- Joey Hess <joeyh@debian.org> Sat, 09 May 2009 17:18:08 -0400

mpdtoys (0.14) unstable; urgency=low

* mprompt: Add -f parameter that enables fuzzy matching of entered
search terms. (Needs String::Approx perl module.)

-- Joey Hess <joeyh@debian.org> Sat, 11 Apr 2009 14:23:34 -0400

mpdtoys (0.13) unstable; urgency=low

* mprompt: Add -t parameter, to allow clearing partially entered
lines after a timeout.

-- Joey Hess <joeyh@debian.org> Thu, 09 Apr 2009 20:15:06 -0400

mpdtoys (0.12) unstable; urgency=low

* mprompt: Support exit on EOF/ctrl-d.
* vipl, mprompt: Smarter searching, in particular:
- Remove duplicates from result list.
- Search for whole albums or artists before individual songs,
so that songs with the same name do not appear out of order.

-- Joey Hess <joeyh@debian.org> Mon, 02 Mar 2009 18:43:55 -0500

mpdtoys (0.11) unstable; urgency=low

* mprompt: prompt-based mpd client, designed for headless machines

-- Joey Hess <joeyh@debian.org> Sat, 28 Feb 2009 19:35:18 -0500

mpdtoys (0.10) unstable; urgency=low

* Fix some bad man page synopses. Closes: #515304

-- Joey Hess <joeyh@debian.org> Sun, 15 Feb 2009 13:35:39 -0500

mpdtoys (0.9) unstable; urgency=low

* vipl: Fix handling of utf-8 in song info. Closes: #514119

-- Joey Hess <joeyh@debian.org> Wed, 04 Feb 2009 17:57:19 -0500

mpdtoys (0.8) unstable; urgency=low

* vipl: New program, allows editing the mpd playlist in your text editor.
* Use debhelper v7; rules file minimisation.
* Use DESTDIR rather than DEST.

-- Joey Hess <joeyh@debian.org> Wed, 30 Apr 2008 02:04:49 -0400

mpdtoys (0.7) unstable; urgency=low

* sats: Add -n option allowing to stop after more than one song has played.

-- Joey Hess <joeyh@debian.org> Wed, 30 Jan 2008 02:52:50 -0500

mpdtoys (0.6) unstable; urgency=low

* mpswap: Fix handling when two hosts are specified on the command line.

-- Joey Hess <joeyh@debian.org> Sat, 19 Jan 2008 17:28:02 -0500

mpdtoys (0.5) unstable; urgency=low

* mpskip: Fix mispaste in synopsis. Closes: #457553

-- Joey Hess <joeyh@debian.org> Sun, 23 Dec 2007 11:19:50 -0500

mpdtoys (0.4) unstable; urgency=low

* mptoggle: Allow the playlists to use to be specified at the command line.

-- Joey Hess <joeyh@debian.org> Thu, 20 Dec 2007 15:39:06 -0500

mpdtoys (0.3) unstable; urgency=low

* mpswap, mpcp, mpmv: Fix handling of only one host being specified on the
command line.

-- Joey Hess <joeyh@debian.org> Sat, 15 Dec 2007 00:03:48 -0500

mpdtoys (0.2) unstable; urgency=low

* mpgenplaylists: Sort, don't rely on find order.
* mprev: Don't crash if the playlist is empty.
* Add mpinsert.

-- Joey Hess <joeyh@debian.org> Fri, 14 Dec 2007 23:39:54 -0500

mpdtoys (0.1) unstable; urgency=low

* First release.

-- Joey Hess <joeyh@debian.org> Sat, 01 Dec 2007 16:51:04 -0500
mpdtoys-0.20/debian/compat000064400000000000000000000000021126001635500155470ustar00rootroot000000000000007
mpdtoys-0.20/debian/control000064400000000000000000000020361126001635500157550ustar00rootroot00000000000000Source: mpdtoys
Section: sound
Priority: optional
Build-Depends: debhelper (>= 7), dpkg-dev (>= 1.9.0)
Maintainer: Joey Hess <joeyh@debian.org>
Standards-Version: 3.8.3
Homepage: http://kitenet.net/~joey/code/mpdtoys/
Vcs-Git: git://git.kitenet.net/mpdtoys

Package: mpdtoys
Architecture: all
Depends: perl, libaudio-mpd-perl (>= 0.19.0), ${misc:Depends}
Suggests: mpd, libproc-daemon-perl, libterm-readkey-perl, libstring-approx-perl
Description: small command line tools and toys for MPD
This is a collection of small toys and tools for doing various things
to MPD (Music Player Daemon) from the command line. Some of them are
very useful, while others are only amusing.
.
Some examples of things the mpdtoys can do include moving the playing
song between different mpd daemons on different machines, storing
the state of a mpd daemon and loading it back later, reversing the
playlist, slowly fading volume up or down, stopping playback after the
current song finishes, emulating a skipping record, and editing the
playlist in a text editor.
mpdtoys-0.20/debian/copyright000064400000000000000000000002541126001635500163050ustar00rootroot00000000000000Files: *
Copyright: (c) 2007 Joey Hess <joeyh@debian.org>
License: GPL-2+
On Debian systems, the complete text of the GPL can be found in
/usr/share/common-licenses/GPL.
mpdtoys-0.20/debian/docs000064400000000000000000000000051126001635500152170ustar00rootroot00000000000000TODO
mpdtoys-0.20/debian/rules000075500000000000000000000002151126001635500154270ustar00rootroot00000000000000#!/usr/bin/make -f
%:
dh $@

# Not intended for use by anyone except the author.
announcedir:
@echo ${HOME}/src/joeywiki/code/mpdtoys/news
mpdtoys-0.20/mpcp000077700000000000000000000000001126001635500154222mpstoreustar00rootroot00000000000000mpdtoys-0.20/mpfade000075500000000000000000000052171126001635500143160ustar00rootroot00000000000000#!/usr/bin/perl
use strict;
use warnings;
use Audio::MPD q{0.19.0};

=head1 NAME

mpfade - fade mpd volume in or out

=head1 SYNOPSIS

mpfade [minutes] [max|min] [host]

=head1 DESCRIPTION

B<mpfade> behaves differently depending on whether mpd is playing or not.

If mpd is not playing (or is paused), it starts it playing at a low volume,
and gradually cranks the volume up to the specified B<max> value (default
50) over the specified number of minutes (default 10).

If mpd is already playing, it works in reverse, reducing the volume over
time until it's a the specified B<min> (defaults to a tenth of what it was
at the start). Then it stops playing.

B<mpfade> tries to interact well with manual volume changes you make. If
the volume is fading up, and you change the volume manually, it stops. This
is intended to be useful when used as an alarm clock. If the volume is
fading down, changes you make to the volume will be noticed, and the fade
down will continue from that point.

The B<minutes> value can be a floating point value. If the hostname is
omitted, the MPD_HOST environment variable will be used.

=head1 AUTHOR

Copyright 2007 Joey Hess <joey@kitenet.net>

Licensed under the GNU GPL version 2 or higher.

http://kitenet.net/~joey/code/mpdtoys

=cut

my $seconds=60 * 10;
if (@ARGV && $ARGV[0] =~ /^[0-9.]+$/) {
$seconds=60 * shift;
}
my $endpoint;
if (@ARGV && $ARGV[0] =~ /^[0-9.]+$/) {
$endpoint=shift;
}

if (@ARGV) {
$ENV{MPD_HOST}=shift;
}

my $mpd=Audio::MPD->new(conntype => $Audio::MPD::REUSE);
my $vol=$mpd->status->volume;
if ($mpd->status->state eq 'play') {
if (defined $endpoint) {
if ($endpoint >= $vol) {
die "error: min ($endpoint) is not less than current volume ($vol)\n";
}
}
else {
$endpoint=int($vol / 10);
}
print "fading down from $vol to $endpoint over $seconds seconds\n";
fade($endpoint, $seconds);
$mpd->stop;
}
else {
if (! defined $endpoint) {
$endpoint=50;
}
$mpd->volume($vol=0);
print "fading up from $vol to $endpoint over $seconds seconds\n";
$mpd->play;
fade($endpoint, $seconds);
}

sub fade {
my $endpoint=shift;
my $seconds=shift;

if ($seconds < 1) {
$mpd->volume($endpoint);
return;
}

my $span=$endpoint - $vol;
my $oldvol=$vol;
my $curvol;

# TODO calculate how long to optimally sleep

while (sleep 1) {
$curvol=$mpd->status->volume;
if ($curvol != $oldvol) {
if ($span > 0) {
print "manual volume change, aborting fade up\n";
return;
}
else {
print "manual volume change\n";
$vol=$curvol;
}
}

$vol=$vol + ($span / $seconds);
last if abs($vol - $endpoint) <= 1;
if (abs(int($vol - $oldvol)) > 1) {
$mpd->volume(int($vol));
$oldvol=int($vol);
}
}
}
mpdtoys-0.20/mpgenplaylists000075500000000000000000000041321126001635500161300ustar00rootroot00000000000000#!/bin/sh
set -e

conf=/etc/mpd.conf
if [ -e $HOME/.mpdconf ]; then
conf=$HOME/.mpdconf
fi
if [ ! -e $conf ]; then
echo "error: $conf does not exist" >&2
exit 1
fi

music_directory=$(grep "^music_directory" $conf | cut -d '"' -f 2 | sed -e "s!^\~!$HOME!")
playlist_directory=$(grep "^playlist_directory" $conf | cut -d '"' -f 2 | sed -e "s!^\~!$HOME!")

if [ -z "$music_directory" ] || [ -z "$playlist_directory" ]; then
echo "error: failed to parse $conf" >&2
exit 1
fi

if [ ! -d "$music_directory" ] || [ ! -d "$playlist_directory" ]; then
echo "error: both $music_directory and $playlist_directory need to exist" >&2
exit 1
fi

rm $playlist_directory/\ *.m3u 2>/dev/null || true
IFS="
"
for dir in $(cd "$music_directory"; find -type d -follow | sed 's!^./!!'); do
playlist="$(echo "$dir" | tr "_" " " | sed 's!/! - !g')"
if [ "$playlist" = . ]; then
playlist=all
fi
find "$music_directory/$dir" -type f -follow | sort \
> "$playlist_directory/ $playlist".m3u
done

exit

<<POD

=head1 NAME

mpgenplaylists - generate mpd playlists for each subdirectory of music

=head1 SYNOPSIS

mpgenplaylists

=head1 DESCRIPTION

B<mpgenplaylists> generates mpd playlists.

It reads your ~/.mpdconf or /etc/mpd.conf to figure out where mpd keeps its
music directory and playlist directory.

For each subdirectory of the music directory, a playlist is generated in the
playlist directory. The playlists created by this tool always start with a
space to avoid conflicts with your manually created playlists.

So if you keep your sound in Artist/Album/ directories, you'll get playlists
named like " Artist - Album", and also playlists named just " Artist" that
contain all music by that artist. An " all" playlist is also created, that
contains all your music.

Each time it's run it updates the playlists, and removes any obsolete ones
that it created before.

=head1 LIMITATIONS

It does not currently sort songs in an album by track number, but instead
sorts by filename.

=head1 AUTHOR

Copyright 2007 Joey Hess <joey@kitenet.net>

Licensed under the GNU GPL version 2 or higher.

http://kitenet.net/~joey/code/mpdtoys

=cut

POD
mpdtoys-0.20/mpinsert000075500000000000000000000040151126001635500147160ustar00rootroot00000000000000#!/usr/bin/perl
use strict;
use warnings;
use Audio::MPD q{0.19.0};
use MpdToys;

=head1 NAME

mpinsert - insert song after currently playing song

=head1 SYNOPSIS

mpinsert [-n] [song ...]

=head1 DESCRIPTION

B<mpinsert> inserts a song (or songs) into the playlist directly after
the currently playing song.

If no songs are specified on the command line, it will read a list from
stdin.

Songs may be specified the same as they would be to mpc add: As paths to
files in the music database, or urls to stream.

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.

(If the perl String::Approx module is available, it will be used to handle
typos, etc in the names you enter.)

=head1 OPTIONS

=over 4

=item -n

Print the playlist position number that the first song was inserted at.

=back

=head1 AUTHOR

Copyright 2007-2009 Joey Hess <joey@kitenet.net>

Licensed under the GNU GPL version 2 or higher.

http://kitenet.net/~joey/code/mpdtoys

=cut

use Getopt::Long;
my $shownum=0;
GetOptions(
"n" => \$shownum,
) || usage();

sub usage {
die "Usage: mpinsert [-n] [song ...]\n";
}

my @list=@ARGV;
if (! @list) {
while (<>) {
chomp;
push @list, $_;
}
}

my $mpd=Audio::MPD->new(conntype => $Audio::MPD::REUSE);
my $pl=$mpd->playlist;

@list=reverse @list if $mpd->current;

my @out;
foreach my $item (@list) {
if (! length $item) {
die "no item specified to insert\n";
}

my @matches=MpdToys::findmatchingsongs($item, $mpd);
if (! @matches && MpdToys::canmatch_fuzzy()) {
@matches=MpdToys::findmatchingsongs_fuzzy($item, $mpd);
}

foreach my $song (@matches) {
$pl->add($song->file);
my @items=$pl->as_items;
if (! @items) {
die "failed!";
}
my $pos=$#items;

# move from end to just after current
my $current=$mpd->current;
if ($current) {
$pos=$current->pos+1;
$pl->move($#items, $pos);
}

push @out, ($pos+1)."\n" if $shownum;
}
}

if ($shownum) {
@out=reverse @out if $mpd->current;
print $out[0];
}
mpdtoys-0.20/mpload000077700000000000000000000000001126001635500157372mpstoreustar00rootroot00000000000000mpdtoys-0.20/mpmv000077700000000000000000000000001126001635500154422mpstoreustar00rootroot00000000000000mpdtoys-0.20/mprand000075500000000000000000000012621126001635500143370ustar00rootroot00000000000000#!/usr/bin/perl
use strict;
use warnings;
use Audio::MPD q{0.19.0};

=head1 NAME

mprand - play a random playlist

=head1 SYNOPSIS

mprand[host]

=head1 DESCRIPTION

B<mprand> picks a playlist at random and tells mpd to play it.

If the hostname is omitted, the MPD_HOST environment variable will be used.

=head1 AUTHOR

Copyright 2007 Joey Hess <joey@kitenet.net>

Licensed under the GNU GPL version 2 or higher.

http://kitenet.net/~joey/code/mpdtoys

=cut

if (@ARGV) {
$ENV{MPD_HOST}=shift;
}
my $mpd=Audio::MPD->new(conntype => $Audio::MPD::REUSE);

$mpd->playlist->clear;
my @playlists=$mpd->collection->all_playlists;
$mpd->playlist->load($playlists[rand @playlists]);
$mpd->play;
mpdtoys-0.20/mprev000075500000000000000000000020051126001635500142030ustar00rootroot00000000000000#!/usr/bin/perl
use strict;
use warnings;
use Audio::MPD q{0.19.0};
use Data::Dumper;

=head1 NAME

mprev - reverse the mpd playlist

=head1 SYNOPSIS

mprev [host]

=head1 DESCRIPTION

B<mprev> reverses mpd's playlist. That's all. The currently playing
song doesn't change. The song you heard last will be the next song to
play.

If the hostname is omitted, the MPD_HOST environment variable will be used.

=head1 AUTHOR

Copyright 2007 Joey Hess <joey@kitenet.net>

Licensed under the GNU GPL version 2 or higher.

http://kitenet.net/~joey/code/mpdtoys

=cut

if (@ARGV) {
$ENV{MPD_HOST}=shift;
}

my $mpd=Audio::MPD->new(conntype => $Audio::MPD::REUSE);
my $pl=$mpd->playlist;
my $current=$mpd->current;
if (! $current) {
die "playlist seems to be empty\n";
}
my $pos=$mpd->current->pos;
my $id=$mpd->current->id;
my $past=0;
my @list=$pl->as_items;
foreach my $song (reverse @list) {
if ($past) {
$pl->moveid($song->id, $#list);
}
elsif ($id eq $song->id) {
$past=1;
}
else {
$pl->moveid($song->id, $pos++);
}
}
mpdtoys-0.20/mprompt000075500000000000000000000157101126001635500145570ustar00rootroot00000000000000#!/usr/bin/perl
use strict;
use warnings;
use Audio::MPD q{0.19.0};
use MpdToys;
use Getopt::Long;
use Term::ReadKey;
use Encode;

=head1 NAME

mprompt - simple prompt-based control for mpd

=head1 SYNOPSIS

mpompt [-s] [-m key=key] [-t n] [-f] [tty] [-T] [host]

=cut

sub usage {
die "Usage: mprompt [-s] [-m key=key] [-t n] [-f] [-t] [tty] [host]\n";
}

=head1 DESCRIPTION

B<mprompt> is a mpd client with a prompt-based interface. It is
designed to be usable on a headless machine.

At the prompt, enter the name of a playlist, or part of the name of an
album, artist, or song. Matching items will start playing. You can also
paste in urls to stream.

(If the perl String::Approx module is available, it will be used to handle
typos, etc in the names you enter.)

Use the left and right arrow keys to adjust volume, and the up and down
arrow keys to move through the playlist.

The Tab and Enter keys can both be used to pause and unpause playback.
(Enter toggles pause only if nothing has been entered at the prompt.)

Example of how to run mprompt in /etc/inittab:

1:2345:respawn:/usr/bin/mprompt /dev/tty1

=head1 OPTIONS

=over 4

=item -s

This option allows shell commands to be typed in to mprompt, to be
run by whatever user it is running as. (Typically root if it is run from
/etc/inittab).

To enter a shell command, type a "!", followed by the command to run,
followed by Enter.

=item -m key=key

This option allows remapping keys. Any key can be remapped to any other
key, which is useful to support keyboard with unusual key layouts, or
missing keys.

For alphanumeric and punctuation keys, individual symbols can be remapped.
For example, "-m a=b" will turn each entered "a" into "b".

For other keys, use the following names:

=over 4

=item <return>

=item <tab>

=item <space>

=item <up>

=item <down>

=item <left>

=item <right>

=item <backspace>

=back

For example, -m "n=<down>" will map the "n" key to the down arrow, causing
that key to change to the next track; -m "<space>=<return>" will make the space
bar act as a pause.

It's possible to swap keys too. For example, -m "<down>=<up>" -m "<up>=<down>"

A single key can also be bound to a series of keystrokes. For example,
-m "1=Mule Variations<return>" will cause the "1" key to play the "Mule
Variations" album, a nice choice.

=item -t n

Adds a timeout, a specified number of seconds after which the entry
on the command line will be cleared. Useful for headless systems, to avoid
cat-on-keyboard confusing your later commands.

=item -T

Enables terse output mode. This mode tries to avoid displaying excessive
or complex things, with the intent that mprompt's output can be piped into
a speech synthesiser, such as espeak.

=back

=head1 SEE ALSO

vipl(1) mptoggle(1) mpd(1)

=head1 AUTHOR

Copyright 2009 Joey Hess <joey@kitenet.net>

Licensed under the GNU GPL version 2 or higher.

http://kitenet.net/~joey/code/mpdtoys

=cut

my $tty;
my $shell=0;
my $timeout=0;
my $terse=0;
my %controlchars = GetControlChars;
my %keysyms = (
"\n" => '<return>',
"\t" => '<tab>',
" " => '<space>',
"\e[A" => '<up>',
"\e[3~" => '<up>', # delete on some terminals, raw on others
"\e[B" => '<down>',
"\e[D" => '<left>',
"\e[C" => '<right>',
$controlchars{ERASE} => "<backspace>",
);
my %keymap;

Getopt::Long::Configure("no_ignore_case");
GetOptions(
"s" => \$shell,
"m=s" => sub {
my ($old, $new)=split(/=/, $_[1], 2);
$keymap{$old}=$new;
},
"t=i" => \$timeout,
"f" => sub { print STDERR "the -f option is now enabled by default\n" },
"T" => \$terse,
) || usage();

if (@ARGV) {
$tty=shift;
close STDIN;
close STDOUT;
open(STDIN, "<", $tty) || die "open $tty: $!";
open(STDOUT, ">", $tty) || die "open $tty: $!";
}
if (@ARGV) {
$ENV{MPD_HOST}=shift;
}
my $mpd=Audio::MPD->new(conntype => $Audio::MPD::REUSE);

sub quit {
ReadMode("restore");
exit(0);
};
$SIG{INT}=$SIG{TERM}=\&quit;
ReadMode("raw");

$|=1;

my $line="";
my $sequence;
my $laststroke=time;

showprompt();

KEY: while (my $key = ReadKey(0)) {
if ($timeout) {
if (length $line && time - $laststroke > $timeout) {
$line="";
print " <timeout>\n" unless $terse;
showprompt();
}
$laststroke=time;
}

if ($key eq $controlchars{INTERRUPT} ||
$key eq $controlchars{EOF}) {
quit();
}

# Sequences are started with escape, and accumulated
# until a recognised sequence is seen, or until it becomes clear
# that it is not part of a recognised sequence.
if (defined $sequence) {
$sequence.=$key;
if (exists $keysyms{$sequence}) {
$key=$sequence;
$sequence=undef;
}
else {
foreach my $sym (keys %keysyms) {
if ($sym=~/^\Q$sequence\E/) {
next KEY; # unfinished sequence
}
}
$key=$sequence;
$sequence=undef;
}
}

$key = $keysyms{$key} if exists $keysyms{$key};
$key = $keymap{$key} if exists $keymap{$key};

# The key may be mapped to a multiple letter sequence.
while (length $key) {
if ($key=~s/^(<[^>]+>)//) {
handle($1);
}
elsif ($key=~s/(.)//) {
handle($1);
}
}
}

sub handle {
my $key=shift;

if ($key eq "\e") {
$sequence=$key;
}
elsif ($key eq '<return>') {
if ($shell && $line =~ /^\!(.*)/) {
print "\nrunning $1\n";
system($1);
}
elsif (length $line && $line !~ /^\s*$/) {
queue($line);
}
else {
toggle();
}
$line="";
showprompt();
}
elsif ($key eq '<backspace>') {
if (length $line) {
chop $line;
print "\b \b";
}
}
elsif ($key eq '<space>') {
print " ";
$line.=" ";
}
elsif ($key eq '<tab>') {
toggle();
showprompt();
}
elsif ($key eq '<left>') {
adjustvolume(-5);
}
elsif ($key eq '<right>') {
adjustvolume(+5);
}
elsif ($key eq '<up>') {
$mpd->prev;
$mpd->play;
showplaying();
showprompt();
}
elsif ($key eq '<down>') {
$mpd->next;
$mpd->play;
showplaying();
showprompt();
}
else {
print "$key";
$line.=$key;
}
}

sub adjustvolume {
my $amount=shift;
my $vol=$mpd->status->volume;

$vol+=$amount;
if ($vol > 100) {
$vol=100;
}
elsif ($vol < 0) {
$vol=0;
}

if (! $terse) {
print "\nvolume: $vol%\n";
}
$mpd->volume($vol);
showprompt();
}

sub showprompt {
print "> $line";
}

sub showplaying {
print "\n";
my $song=$mpd->current;
if (! defined $song) {
print "nothing queued\n";
}
else {
if (! $terse) {
print encode_utf8($song->as_string)."\n";
}
}
}

sub toggle {
$mpd->pause;
my $state=$mpd->status->state;
print "\n";
print "$state\n" if ! $terse || $state ne "play";
}

sub queue {
my $line=shift;

print "\n";

my $pl=$mpd->playlist;

eval q{$pl->load($line)};
if (! $@) {
$pl->clear;
$pl->load($line);
$mpd->play;

print "added $line playlist";
showplaying();
return;
}

my @matches=MpdToys::findmatchingsongs($line, $mpd);
if (! @matches && MpdToys::canmatch_fuzzy()) {
print "trying fuzzy match..\n";
@matches=MpdToys::findmatchingsongs_fuzzy($line, $mpd);
}
if (@matches) {
$pl->clear;
foreach (@matches) {
$pl->add($_->file);
}
$mpd->play;
print "added ".int(@matches)." songs";
showplaying();
}
else {
print "no matches found for \"$line\"\n";
}
}
mpdtoys-0.20/mpskip000075500000000000000000000037641126001635500143720ustar00rootroot00000000000000#!/usr/bin/perl
use strict;
use warnings;
use Audio::MPD q{0.19.0};
use Time::HiRes qw(usleep);

=head1 NAME

mpskip - emulate a skipping record

=head1 SYNOPSIS

mpskip [num_skips] [duration] [skippos] [host]

=head1 DESCRIPTION

B<mpskip> makes mpd emulate a skipping record.

If B<num_skips> is not specified (or is 0), it will skip forever (or until
someone manually seeks past the "bad" part of the song, or changes songs).

The B<duration> is how long the skip should be, in seconds. Floating point
values can be used.

The B<skippos> is the number of seconds into the song to place the skip point.
Default is the current play position.

If the hostname is omitted, the MPD_HOST environment variable will be used.

=head1 LIMITATIONS

Doesn't insert the pop you hear on a real record player when the needle
skips.

If run against a remote host, it may not skip at exactly the same place
each time due to network issues.

=head1 AUTHOR

Copyright 2007 Joey Hess <joey@kitenet.net>

Licensed under the GNU GPL version 2 or higher.

http://kitenet.net/~joey/code/mpdtoys

=cut

my $num_skips=0;
if (@ARGV && $ARGV[0] =~ /^[0-9]+$/) {
$num_skips=shift;
}

my $duration=1;
if (@ARGV && $ARGV[0] =~ /^[0-9.]+$/) {
$duration=shift;
}

my $skippos;
if (@ARGV && $ARGV[0] =~ /^[0-9]+$/) {
$skippos=shift;
}

if (@ARGV) {
$ENV{MPD_HOST}=shift;
}

my $mpd=Audio::MPD->new(conntype => $Audio::MPD::REUSE);
if ($mpd->status->state ne 'play') {
die "error: mpd has to be playing first\n";
}

my $songid=$mpd->current->id;
$skippos=$mpd->status->time->seconds_sofar unless defined $skippos;

print "skipping".($num_skips ? " $num_skips times" : "").
" at position $skippos for $duration seconds\n";

while (usleep $duration * 1000000 / 2 ) {
my $status=$mpd->status;
my $pos=$status->time->seconds_sofar;
exit if $status->state ne 'play';
exit if $mpd->current->id ne $songid;
exit if $pos > $skippos + $duration + 1;
next if $pos <= $skippos;
$mpd->seek($skippos);
if ($num_skips) {
$num_skips--;
exit unless $num_skips;
}
}
mpdtoys-0.20/mpstore000075500000000000000000000141661126001635500145560ustar00rootroot00000000000000#!/usr/bin/perl
use strict;
use warnings;
use Audio::MPD q{0.19.0};
use Data::Dumper;

=head1 NAME

mpstore - store and transfer mpd state between daemons

=head1 SYNOPSIS

mpstore [host] > file

mpload [host] < file

mpcp [src] dest

mpmv [src] dest

mpswap [A] B

=head1 DESCRIPTION

These commands allow saving, loading, and transferring state between mpd
daemons running on different hosts.

B<mpstore> dumps a daemon's state to stdout.

B<mpload> loads a state dump from stdin and sends it to a daemon.

B<mpcp> copies the state from the src daemon to the dest daemon, causing it
to begin to play the same song as the src daemon, at the same position.

B<mpmv> moves the state, so the dest daemon is left playing what the src
daemon was playing, and the src daemon is paused.

B<mpswap> exchanges the state of daemons A and B, swapping what they're
playing.

The first hostname passed to each command can be omitted, if it is then
the MPD_HOST environment variable will be used. Like the MPD_HOST variable,
the hostname can be of the form "password@hostname" to specify a password.
If any hostname is "-", the MPD_HOST setting will be used.

The full list of state that is handled is:

=over

=item the contents of the playlist

=item the playback state (playing, paused, stopped)

=item the currently playing song

=item the position within the playing song

=item the volume control

=item the repeat, random, and cross fade settings

=back

=head1 LIMITATIONS

The host that state is transferred to must have the playing song available
in its library, with the same filename. It's ok if some other songs in the
playlist are not available; such songs will be skipped.

B<mpcp> cannot perfectly synchronise playback between the two daemons.
Network latency and timing prevent this. It should manage better than 0.5
second accuracy. If you need better accuracy of synchronised playback,
you should probably use Pulse Audio.

=head1 BUGS

The file format is not the same that mpd uses for saving its own state,
which would be nice.

=head1 AUTHOR

Copyright 2007 Joey Hess <joey@kitenet.net>

Licensed under the GNU GPL version 2 or higher.

http://kitenet.net/~joey/code/mpdtoys

=cut

# Allow "-" to be specified as a host, it will use MPD_HOST then.
my $real_MPD_HOST=$ENV{MPD_HOST};
my $prev_host;
sub sethost {
my $host=shift;
if ($host eq "-") {
$host=$real_MPD_HOST;
if (! defined $host) {
die "error: MPD_HOST is not set, cannot use '-'\n";
}
}
if (defined $prev_host && $host eq $prev_host) {
die "error: src and dest hosts cannot be the same\n";
}

$ENV{MPD_HOST}=$prev_host=$host;
}

if ($0=~/mpswap/) {
if (@ARGV == 2) {
sethost(shift);
}
if (! @ARGV) {
die "error: not enough hosts specified\n";
}
my $a=Audio::MPD->new(conntype => $Audio::MPD::REUSE);
sethost(shift);
my $b=Audio::MPD->new(conntype => $Audio::MPD::REUSE);

my $asnap=snapshot($a, 1);
my $bsnap=snapshot($b, 1);
transfer($asnap, $b);
transfer($bsnap, $a);
}
elsif ($0=~/mpstore/) {
if (@ARGV) {
sethost(shift);
}

my $src=Audio::MPD->new(conntype => $Audio::MPD::REUSE);

my $snap=snapshot($src, 0);
delete $snap->{src}; # don't dump this object
$Data::Dumper::Terse=1;
$Data::Dumper::Indent=1;
print Dumper($snap);
}
elsif ($0=~/mpload/) {
if (@ARGV == 2) {
sethost(shift);
}

my $dest=Audio::MPD->new(conntype => $Audio::MPD::REUSE);
my $code;
{
local $/=undef;
$code=<>;
}
my $snap=eval $code;
if ($@ || ! ref $snap) {
die "error: failed to parse stdin ($@)\n";
}
transfer($snap, $dest);
}
else {
if (@ARGV == 2) {
sethost(shift);
}

if (! @ARGV) {
die "error: not enough hosts specified\n";
}
my $src=Audio::MPD->new(conntype => $Audio::MPD::REUSE);
sethost(shift);
my $dest=Audio::MPD->new(conntype => $Audio::MPD::REUSE);

my $snap=snapshot($src, 1);
transfer($snap, $dest);
}

sub snapshot {
my $mpd=shift;
my $pause=shift;

my $status=$mpd->status;
my $state=$status->state;
my $current=$mpd->current;
if ($pause && $state eq 'play') {
$mpd->pause;
}

my @playlist;
foreach my $song ($mpd->playlist->as_items) {
push @playlist, $song->file;
}

return {
src => $mpd,
state => $state,
repeat => $status->repeat,
volume => $status->volume,
pos => $status->time->seconds_sofar,
xfade => $status->xfade,
current => defined $current ? $mpd->current->file : undef,
playlist => \@playlist,
};
}

sub transfer {
my $snap=shift;
my $dest=shift;

if (ref $snap->{playlist} eq 'ARRAY') {
# Feed playlist to dest.
$dest->playlist->clear;
eval {
$dest->playlist->add(@{$snap->{playlist}});
};
if ($@) {
# Try doing it a song at a time, in case only some
# songs are available.
foreach my $song (@{$snap->{playlist}}) {
eval {
$dest->playlist->add($song);
};
if ($@) {
print STDERR "warning: failed to add song to playlist ($song)\n";
}
}
}
}

# Set misc settings.
$dest->repeat($snap->{repeat}) if exists $snap->{repeat};
$dest->random($snap->{random}) if exists $snap->{random};
$dest->volume($snap->{volume}) if exists $snap->{volume};
$dest->fade($snap->{xfade}) if exists $snap->{xfade};

# Figure out the id of the song to play on dest.
my $id;
if (exists $snap->{current} && defined $snap->{current}) {
foreach my $song ($dest->playlist->as_items) {
if ($song->file eq $snap->{current}) {
$id=$song->id;
}
}
if (! defined $id) {
print STDERR "error: cannot find currently playing song (".
$snap->{current}.") on dest playlist\n";
return;
}
}

# Seek and set play state.
$dest->seekid($snap->{pos}, $id) if exists $snap->{pos} && defined $id;
if (exists $snap->{state}) {
if ($snap->{state} eq 'play') {
if ($0 =~ /mpcp/) {
# mpd only provides second accuracy, so src
# and dest are probably a fraction of a second
# off. For a more accurate copy of the state,
# seek the *src* back to the start of the
# current second too.
# Of course, this isn't perfect, due to
# network latency, etc.
$snap->{src}->seek($snap->{pos});
$snap->{src}->play;
$dest->play;
}
else {
$dest->play;
}
}
elsif ($snap->{state} eq 'pause') {
$dest->pause;
}
elsif ($snap->{state} eq 'stop') {
$dest->stop;
}
}

return 1;
}
mpdtoys-0.20/mpswap000077700000000000000000000000001126001635500157722mpstoreustar00rootroot00000000000000mpdtoys-0.20/mptoggle000075500000000000000000000025761126001635500147050ustar00rootroot00000000000000#!/usr/bin/perl
use strict;
use warnings;
use Audio::MPD q{0.19.0};

=head1 NAME

mptoggle - single button control for mpd

=head1 SYNOPSIS

mptoggle [host] [playlist ...]

=head1 DESCRIPTION

B<mptoggle> allows mpd to be controlled by a single physical button. It
was designed for a linksys nslu2 running mpd, for which the only available
physical control is a power button that can be remapped to run an arbitrary
program.

B<mptoggle> toggles playback each time it's run. So press the button once
to start, and a second time to stop. Each time it starts playing. By
default it selects a new different playlist, at random, to play. If
playlist names are specified at the command line, it will choose between
those at random.

Example of how to make the nslu2's power button run mptoggle, in
/etc/inittab:

ca:12345:ctrlaltdel:/usr/bin/mptoggle localhost

=head1 SEE ALSO

mprompt(1) mpd(1)

=head1 AUTHOR

Copyright 2007 Joey Hess <joey@kitenet.net>

Licensed under the GNU GPL version 2 or higher.

http://kitenet.net/~joey/code/mpdtoys

=cut

if (@ARGV) {
$ENV{MPD_HOST}=shift;
}
my $mpd=Audio::MPD->new(conntype => $Audio::MPD::REUSE);

if ($mpd->status->state ne 'play') {
$mpd->playlist->clear;
my @playlists;
if (@ARGV) {
@playlists=@ARGV;
}
else {
@playlists=$mpd->collection->all_playlists;
}
$mpd->playlist->load($playlists[rand @playlists]);
$mpd->play;
}
else {
$mpd->pause;
}
mpdtoys-0.20/sats000075500000000000000000000031051126001635500140260ustar00rootroot00000000000000#!/usr/bin/perl
use strict;
use warnings;
use Audio::MPD q{0.19.0};
use Getopt::Long;

=head1 NAME

sats - mpd stop after this song

=head1 SYNOPSIS

sats [-d] [-n num] [host]

=head1 DESCRIPTION

B<sats> is an acronym for Stop After This Song. It will wait for playback
of the currently playing song to finish, and then tell mpd to stop playing.

If the hostname is omitted, the MPD_HOST environment variable will be used.

=head1 OPTIONS

=over 4

=item -d

Daemonize rather than waiting in the foreground for the song to stop playing.

=item -n num

Stop after B<num> songs (default is 1).

=back

=head1 AUTHOR

Copyright 2007 Joey Hess <joey@kitenet.net>

Licensed under the GNU GPL version 2 or higher.

http://kitenet.net/~joey/code/mpdtoys

=cut

my $num=1;
my $daemon=0;
GetOptions(
"n=i" => \$num,
"d" => \$daemon,
) || usage();

sub usage {
die "Usage: sats [-d] [-n num] [host]\n";
}

if (@ARGV) {
$ENV{MPD_HOST}=shift;
}

my $mpd=Audio::MPD->new(conntype => $Audio::MPD::REUSE);

my $song=$mpd->current;
if ($mpd->status->state ne 'play') {
die "no song is currently playing\n";
}

if ($daemon) {
eval q{use Proc::Daemon};
Proc::Daemon::Init();
# daemonising closed the connection to mpd
$mpd=Audio::MPD->new(conntype => $Audio::MPD::REUSE);
}

# Polling is evil, it could look at the seek position, and sleep until the
# end. But that would break if something seeked in the song..

while (sleep 1) {
last if $mpd->status->state ne 'play';
my $current=$mpd->current;
if ($current->id != $song->id) {
$num--;
if ($num == 0) {
$mpd->stop;
exit;
}
$song=$current;
}
}
mpdtoys-0.20/vipl000075500000000000000000000065721126001635500140410ustar00rootroot00000000000000#!/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 => $Audio::MPD::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;
}
 
дизайн и разработка: Vladimir Lettiev aka crux © 2004-2005, Andrew Avramenko aka liks © 2007-2008
текущий майнтейнер: Michael Shigorin