view release on metacpan or search on metacpan
lib/App/Pimpd/Collection/Album.pm view on Meta::CPAN
use App::Pimpd::Validate;
use Term::ExtendedColor qw(fg);
sub delete_album {
my $file = $mpd->current->file;
my($path) = $file =~ m|(.+)/.+$|m;
my @songs = $mpd->collection->all_items_simple($path);
printf("Remove %s ? [y/N] ", fg('bold', $path));
chomp(my $answer = <STDIN>);
if(lc($answer) ne 'y') {
return 1;
}
$path = "$config{music_directory}/$path";
if(remote_host()) {
open(OLD_STDOUT, '>&', STDOUT) or die("Cant dupe STDOUT: $!");
close(STDOUT);
system(
'ssh', "-p $config{ssh_port}",
"$config{ssh_user}\@$config{ssh_host}",
"rm -rv '$path'",
) == 0 and do {
open(STDOUT, '>&', OLD_STDOUT) or die("Cant reopen STDOUT: $!");
printf("Removed %s successfully\n", fg('bold', $path));
return;
};
open(STDOUT, '>&', OLD_STDOUT) or die("Cant reopen STDOUT: $!");
}
else {
if(remove_path($path)) {
printf("removed '%s'\n", fg('bold', $path));
return;
}
print STDERR "remove_path($path): $!\n";
return;
}
return;
}
sub albums_by_artist {
lib/App/Pimpd/Doc.pm view on Meta::CPAN
BEGIN {
use Exporter;
use vars qw(@ISA @EXPORT);
@ISA = qw(Exporter);
@EXPORT = qw(
help
);
}
use App::Pimpd;
use Term::ExtendedColor qw(fg bold);
sub help {
my $cmd = shift;
my %help = (
# Controls
'next' => \&_help_next,
'previous' => \&_help_previous,
'pause' => \&_help_pause,
lib/App/Pimpd/Doc.pm view on Meta::CPAN
if(exists($help{$cmd})) {
return $help{$cmd}->();
}
else {
return "No such topic.\n";
}
}
sub _help_status {
return << "EOF"
@{[fg('bold', 'Usage')]}: status
Display MPD status
EOF
}
sub _help_stats {
return << "EOF"
@{[fg('bold', 'Usage')]}: stats
Display statistics about MPD
EOF
}
sub _help_unlove {
return << "EOF"
@{[fg('bold', 'Usage')]}: unlove PATTERN
Un-love file(s) matching PATTERN.
EOF
}
sub _help_add_album {
return << "EOF"
@{[fg('bold', 'Usage')]}: add-album ALBUM
Add all songs from ALBUM to playlist.
If ALBUM is omitted, use albumtag from the current song.
EOF
}
sub _help_add {
return << "EOF"
@{[fg('bold', 'Usage')]}: add FILES || PLAYLIST
Argument can be either a playlist or a command that produces file lists.
Those commands include;
songs add all songs from the current album
slove add all songs matching pattern given to slove
EOF
}
sub _help_slove {
return << "EOF"
@{[fg('bold', 'Usage')]}: slove PATTERN
Search the database with loved songs for PATTERN.
If PATTERN is omitted, returns all loved songs.
The results are added to the current playlist.
EOF
}
sub _help_random_track {
return << "EOF"
@{[fg('bold', 'Usage')]}: randomtrack
Play a random song from the current playlist.
EOF
}
sub _help_stop {
return << "EOF"
@{[fg('bold', 'Usage')]}: stop
Stop @{[fg('bold', 'local and remote')]} playback.
EOF
}
sub _help_kill {
return << "EOF"
@{[fg('bold', 'Usage')]}: kill
Stop @{[fg('bold', 'local')]} playback.
EOF
}
sub _help_crop {
return << "EOF"
@{[fg('bold', 'Usage')]}: crop
Remove all but the current song from the playlist.
EOF
}
sub _help_clear {
return << "EOF"
@{[fg('bold', 'Usage')]}: clear
Clear the current playlist.
EOF
}
sub _help_random {
return << "EOF"
@{[fg('bold', 'Usage')]}: random
Toggle random on/off.
EOF
}
sub _help_repeat {
return << "EOF"
@{[fg('bold', 'Usage')]}: repeat
Toggle repeat on/off.
EOF
}
sub _help_pause {
return << "EOF"
@{[fg('bold', 'Usage')]}: pause
Toggle playback status.
EOF
}
sub _help_previous {
return << "EOF"
@{[fg('bold', 'Usage')]}: previous
Play the previous song in playlist.
EOF
}
sub _help_next {
return << "EOF"
@{[fg('bold', 'Usage')]}: next
Play the next song in playlist.
EOF
}
sub _help_stitle {
return << "EOF"
@{[fg('bold', 'Usage')]}: sartist TITLE
Search the collection for songs where the title tag
@{[fg('bold', 'partially')]} matches TITLE.
The results are added to the current playlist.
EOF
}
sub _help_salbum {
return << "EOF"
@{[fg('bold', 'Usage')]}: sartist ALBUM
Search the collection for songs where the album tag
@{[fg('bold', 'partially')]} matches ALBUM.
The results are added to the current playlist.
EOF
}
sub _help_sartist {
return << "EOF"
@{[fg('bold', 'Usage')]}: sartist ARTIST
Search the collection for songs where the artist tag
@{[fg('bold', 'partially')]} matches ARTIST.
The results are added to the current playlist.
EOF
}
sub _help_sany {
return << "EOF"
@{[fg('bold', 'Usage')]}: sany PATTERN
Search the collection for filenams matching PATTERN.
The results are added to the current playlist.
EOF
}
sub _help_albums {
return << "EOF"
@{[fg('bold', 'Usage')]}: albums [ARTIST]
List albums where ARTIST is featured.
If ARTIST is omitted, use the artist tag from the currently
playing song.
EOF
}
sub _help_songs {
return << "EOF"
@{[fg('bold', 'Usage')]}: songs [ALBUM]
List songs on ALBUM.
If ALBUM is omitted, use the album tag from the currently playing
song.
EOF
}
sub _help_queue {
return << "EOF"
@{[fg('bold', 'Usage')]}: queue INTEGERs
Put songs in a queue.
Arguments need to be valid playlist position IDs, as shown in
the 'playlist' output.
EOF
}
sub _help_splaylist {
return << "EOF"
@{[fg('bold', 'Usage')]}: splaylist PATTERN
Search the current playlist for PATTERN.
If more then one result is found, queue up the results.
See 'help queue'.
EOF
}
sub _help_love {
return << "EOF"
@{[fg('bold', 'Usage')]}: love [PLAYLIST]
Add the currently playing track to the library of loved songs.
If PLAYLIST is omitted, the song is added to a playlist following
this naming scheme:
@{[fg($c[0], '%year-%month-%genre.m3u')]}
If a genre tag is missing, the string 'undef' is used in its place.
EOF
}
sub _help_loved {
return << "EOF"
@{[fg('bold', 'Usage')]}: loved?
Check if the current song is already loved.
EOF
}
sub _help_delete_album {
return << "EOF"
@{[fg('bold', 'Usage')]}: delete_album
Deletes the current album from disk.
EOF
}
sub _help_rm_album {
return << "EOF"
@{[fg('bold', 'Usage')]}: rmalbum PATTERN
Search the current playlist for albums matching PATTERN and
removes the matches from the playlist.
EOF
}
sub _help_playlists {
return << "EOF"
@{[fg('bold', 'Usage')]}: playlists
List all by MPD known playlists.
EOF
}
sub _help_playlist {
return << "EOF"
@{[fg('bold', 'Usage')]}: playlist
Show the current playlist.
EOF
}
sub _help_shell {
return sprintf("\n%s%s",
"@{[fg('bold', fg($c[8], 'OPTIONS'))]}\t\t " .
"@{[fg('bold', fg($c[3], 'DESCRIPTION'))]} \t\t\t\t "
,"
np show the current song
info show all current information
copy copy song
optional argument: @{[bold('destination')]}
copya copy album
optional argument: @{[bold('destination')]}
queue put songs in a queue
@{[fg('bold', fg($c[0], ' Playlist'))]}
lsplaylists list all known playlists
add add playlists or songs to the current playlist
add-album add songs from album to playlist
rmalbum remove album from playlist
randomize randomize a new playlist with n tracks
optional arguments: @{[bold('quantity')]}, @{[bold('artist')]}
randomalbum and n random full albums
optional arguments: @{[bold('quantity')]}, @{[bold('artist')]}
love love song
loved? check if the current song is loved
unlove unlove file(s) matching PATTERN
splaylist search the current playlist for str
@{[fg('bold', fg($c[0], ' Collection'))]}
songs list songs on album
albums list albums by artist
optional argument: @{[bold('artist')]}
sartist search for artist str
salbum search for album str
stitle search for title str
sany search database for str
slove search the database with loved songs for pattern
@{[fg('bold', fg($c[0], ' Controls'))]}
next next track in playlist
previous previous track in playlist
pause toggle playback
repeat toggle repeat on/off
random toggle random on/off
clear clear playlist
crop remove all tracks but the current one
kill stop local playback
help show help for command
exit exit pimpd2
\n", shift,
);
}
sub _help_copy {
return << "EOF"
@{[fg('bold', 'Usage')]}: copy [DESTINATION]
Copy the currently playing song to DESTINATION.
If DESTINATION is omitted, use the @{[fg($c[0], '$target_directory')]}
setting defined in @{[fg('bold', 'pimpd2.conf')]}.
EOF
}
sub _help_info {
return << "EOF"
@{[fg('bold', 'Usage')]}: info
Show all available song metadata, as well as playback status
and various MPD settings.
EOF
}
sub _help_np {
return << "EOF"
@{[fg('bold', 'Usage')]}: np
Show basic song metadata on a single line.
EOF
}
sub _help_randomize {
return << "EOF"
@{[fg('bold', 'Usage')]}: randomize [INTEGER] [ARTIST]
Add n random songs from the collection to the current playlist.
The first, optional argument, is the number of songs to add.
The second, optional argument, is an artist name.
If a second argument is provided, add n random songs from that artist.
Defaults to 100 random songs.
EOF
}
sub _help_randomize_albums {
return << "EOF"
@{[fg('bold', 'Usage')]}: randomalbum [INTEGER]
Add n random full albums to the current playlist.
Defaults to 10 albums.
EOF
}
1;
__END__
lib/App/Pimpd/Info.pm view on Meta::CPAN
}
return;
}
sub current {
_current_update();
my $output;
if(to_terminal()) {
$output = sprintf("%s - %s on %s from %s [%s]",
fg($c[3], fg('bold', $current{artist})),
fg($c[11], $current{title}),
fg($c[0], $current{album}),
fg($c[4], fg('bold', $current{date})),
$current{genre},
);
}
else {
$output = sprintf("%s - %s on %s from %s [%s]",
$current{artist},
$current{title},
$current{album},
$current{date},
$current{genre},
lib/App/Pimpd/Info.pm view on Meta::CPAN
$status{'state'} = 'Playing' if($status{'state'} eq 'play');
$status{'state'} = 'Paused' if($status{'state'} eq 'pause');
$status{'state'} = 'Stopped' if($status{'state'} eq 'stop');
if($status{volume} < 0) {
$status{volume} = 'N/A (Software Mixer)';
}
printf("%s %8s: %.66s\n", fg('bold', fg('251', 'S')),
'Artist', fg($c[3], fg('bold', $current{artist}))
);
printf("%s %8s: %.66s\n", fg('bold', fg('250', 'O')),
'Album', fg($c[0], $current{album})
);
printf("%s %8s: %.66s\n", fg('bold', fg('249', 'N')),
'Song', fg($c[11], fg('bold', $current{title}))
);
printf("%s %8s: %.66s\n", fg('bold', fg(248, 'G')),
'Genre', fg($c[13], $current{genre})
);
printf("%s %9s: %s\n",'', 'File', ls_color($current{file}));
printf("%s %8s: %.66s\n", fg('bold', fg('247', 'I')),
'Date', $current{date}
);
printf("%s %8s: %.66s\n", fg('bold', fg('246', 'N')),
'Time', $current{time}
);
printf("%s %8s: %.66s\n", fg('bold', fg('245', 'F')),
'Bitrate', $current{bitrate}
);
printf("%s %8s: %.66s\n", fg('bold', fg('244', 'O')),
'Audio', $current{audio}
);
print fg($c[15]);
print '-' x 25, clear(), "\n";
printf("%s %8s: %.66s\n", fg('bold', fg('243', 'S')),
'Repeat', $status{repeat}
);
printf("%s %8s: %.66s\n", fg('bold', fg('242', 'T')),
'Shuffle', $status{shuffle}
);
printf("%s %8s: %.66s\n", fg('bold', fg('242', 'A')),
'Xfade', $status{xfade}
);
printf("%s %8s: %.66s\n", fg('bold', fg('241', 'T')),
'Volume', $status{volume}
);
printf("%s %8s: %.66s\n", fg('bold', fg('240', 'U')),
'State', $status{state}
);
printf("%s %8s: %.66s\n", fg('bold', fg('239', 'S')),
'List V', $status{list}
);
print fg($c[15]);
print '-' x 25, clear(), "\n";
printf("%s %8s: %.66s\n", fg('bold', fg('238', 'S')),
'Song', $stats{song}
);
printf("%s %8s: %.66s\n", fg('bold', fg('237', 'T')),
'List', $stats{length} . ' songs'
);
printf("%s %8s: %.66s\n", fg('bold', fg('236', 'A')),
'Songs', $stats{songs}
);
printf("%s %8s: %.66s\n", fg('bold', fg('235', 'T')),
'Albums', $stats{albums}
);
printf("%s %8s: %.66s\n", fg('bold', fg('234', 'S')),
'Artists', $stats{artists}
);
return;
}
sub _on_off {
my $state = shift;
if($state > 1) {
return "ON ($state)";
lib/App/Pimpd/Player.pm view on Meta::CPAN
return;
}
sub player_destruct {
open(my $fh, '<', $pidfile_pimpd) or return 1; # for now
my $pimpd_player = <$fh>;
close($fh);
if(kill(9, $pimpd_player)) {
unlink($player_tmp_log);
#printf("%s %s\n", fg('bold', $pimpd_player), 'terminated');
}
open(my $fh, '<', $pidfile_player) or confess($!);
my $pimpd_target = <$fh>;
close($fh);
if(kill(9, $pimpd_target)) {
#printf("%s %s\n", fg('bold', $pimpd_target, 'terminated'));
}
if(kill(9, $pimpd_target+1)) {
#printf("%s %s\n", fg('bold', $pimpd_target + 1), 'terminated');
}
return 0;
}
1;
__END__
=pod
lib/App/Pimpd/Playlist.pm view on Meta::CPAN
remove_album_from_playlist
);
}
#TODO
# List content in all playlist
# Search all playlist, without args, search for the current song
use App::Pimpd;
use App::Pimpd::Validate;
use Term::ExtendedColor qw(fg bg bold);
sub get_album_songs {
my $album = shift // $mpd->current->album;
if( (!defined($album)) or ($album eq '') ) {
return;
}
my @tracks = $mpd->collection->songs_from_album($album);
return wantarray() ? @tracks : scalar(@tracks);
}
lib/App/Pimpd/Playlist.pm view on Meta::CPAN
$mpd->random(0);
$mpd->play(shift(@to_play));
$mpd->playlist->move($mpd->current->pos, 0);
return 0 if(scalar(@to_play) == 0);
my $next_pos = $mpd->current->pos + 1;
print fg('bold', 'Queueing'), ":\n";
for(@to_play) {
printf("%-50.50s %s\n", fg($c[3], $list{$_}), "( $_ => $next_pos )");
$mpd->playlist->move($_, $next_pos);
$next_pos++;
}
return;
}
sub show_playlist {
lib/App/Pimpd/Playlist.pm view on Meta::CPAN
my $crnt_title = $mpd->current->title // undef;
my $crnt_artist = $mpd->current->artist // undef;
$title =~ s/(\w+)/\u\L$1/gm;
$artist =~ s/(\w+)/\u\L$1/gm;
$crnt_title =~ s/(\w+)/\u\L$1/gm;
$crnt_artist =~ s/(\w+)/\u\L$1/gm;
if($mpd->current->pos == $i) {
# bg('red4', $i) will add another 17 chars
printf("%s\n", bg('26', bold(sprintf("%4d %25.25s |x| %-47.47s", $i, $artist, $title))));
}
else {
printf("%4d %25.25s | | %-47.47s\n",
$i, $artist, $title);
}
$i++;
}
return;
}
lib/App/Pimpd/Playlist/Favorite.pm view on Meta::CPAN
printf("%s by %s is already loved!\n",
fg($c[11], $title), fg($c[2], $artist),
);
return;
}
}
else {
if(already_loved($file, $favlist_m3u)) {
printf("%s by %s is already loved in %s\n",
fg($c[11], $title), fg($c[2], $artist), fg('bold', $favlist_m3u),
);
return;
}
}
$genre =~ s/\s+/_/gm; # evil whitespace
my(undef, undef, undef, undef, $month, $year) = localtime(time);
$month += 1;
lib/App/Pimpd/Playlist/Favorite.pm view on Meta::CPAN
return 0;
};
return 1;
} # Nope, not remote
open(my $fh, '>>', $favlist_m3u)
or die("Could not open '$favlist_m3u' in append mode: $!");
print $fh "$file\n";
close($fh);
print fg($c[8], fg('bold', $title)), ' => ', fg($c[6], $favlist_m3u), "\n";
return;
}
1;
__END__
=pod
lib/App/Pimpd/Shell.pm view on Meta::CPAN
$opts = {
'randomize' => sub {
if(!defined($_[0])) {
$_[0] = 100;
}
elsif(defined($_[0]) and $_[0] !~ /^\d+$/m) {
print STDERR "Need a valid integer\n";
$_[0] = 100;
}
print 'Adding ' . fg('bold', @_) . " random tracks...\n";
my @random = randomize(@_);
# print $_->artist for @random;
# print "$_\n" for @random;
clear_playlist();
add_to_playlist(@random);
print "$_\n" for map { ls_color($_) } @random;
},
'randomalbum' => sub {
$_[0] = 10 if(!$_[0]);
print 'Adding ' . fg('bold', $_[0]) . " random albums...\n\n";
my @albums = randomize_albums($_[0]);
my $old = undef;
for(@albums) {
my($album_dir) = $_ =~ m|(.+)/.+|m;
if($old ne $album_dir) {
print "> $album_dir\n";
$old = $album_dir;
}
}
lib/App/Pimpd/Shell.pm view on Meta::CPAN
add_to_playlist(@albums);
},
'playlist' => sub {
if(empty_playlist()) {
print STDERR "Playlist is empty\n";
return 1;
}
show_playlist();
print fg('bold', ' >'), '> ', current(), "\n";
},
'love' => sub {
if(empty_playlist()) {
print STDERR "Nothing is playing - playlist is empty\n";
return 1;
}
add_to_favlist(@_);
},
'loved?' => sub {
if(already_loved($mpd->current->file)) {
printf("%s, %s by %s is loved.\n",
fg('bold', 'Yes'),
fg($c[10], $mpd->current->title),
fg($c[2], fg('bold', $mpd->current->artist)),
);
}
else {
printf("%s, %s by %s is not loved yet.\n",
fg('bold', 'No'),
fg($c[10], $mpd->current->title),
fg($c[2], fg('bold', $mpd->current->artist)),
);
}
},
'unlove' => sub {
if(!@_) {
print help('unlove');
return;
}
remove_favorite(@_);
lib/App/Pimpd/Shell.pm view on Meta::CPAN
'add-album' => sub {
add_to_playlist( map{ $_->file } get_album_songs(@_));
},
'lsplaylists' => sub { print "$_\n" for list_all_playlists(); },
'add' => sub {
if($_[0] eq 'songs') {
local $\ = "\n";
my @songs = map { $_->file } songs_on_album();
printf("Adding %d songs from %s\n",
scalar(@songs), fg('bold', $mpd->current->album),
);
add_to_playlist(@songs);
}
elsif($_[0] eq 'slove') {
shift @_; # so we can grab the PATTERN
my @result = search_favlist(@_);
if(scalar(@result) > 0) {
print "$_\n" for @result;
add_to_playlist(@result);
printf("\nAdded %s loved %s matching '%s'\n",
fg('bold', scalar(@result)),
(scalar(@result) > 1) ? 'songs' : 'song',
fg($c[4], fg('bold', $_[0])),
);
}
else {
printf("No songs matching '%s' were found\n",
fg($c[4], fg('bold', $_[0])),
);
}
}
else {
add_playlist(@_);
}
},
'next' => sub {
lib/App/Pimpd/Shell.pm view on Meta::CPAN
if(invalid_playlist_pos(@_)) {
printf("No such song%s\n", (@_ < 1) ? 's' : '');
return 1;
}
queue(@_);
},
'random' => sub {
$mpd->random;
my $status = ($mpd->status->random)
? "Random: " . fg('bold', 'On')
: "Random: " . fg('bold', 'Off');
print "$status\n";
},
'repeat' => sub {
$mpd->repeat;
my $status = ($mpd->status->repeat)
? "Repeat: " . fg('bold', 'On')
: "Repeat: " . fg('bold', 'Off');
print "$status\n";
},
'stats' => sub { stats(); },
'status' => sub { print status(), "\n"; },
'randomtrack' => sub {
play_pos_from_playlist(random_track_in_playlist());
print current(), "\n";
},
lib/App/Pimpd/Shell.pm view on Meta::CPAN
if( defined($opts->{$_[0]}) ) {
print help($_[0]);
}
else {
print help('shell');
}
},
};
while(1) {
#print fg($c[6], 'pimpd'), fg('bold', '> ');
#chomp(my $choice = <STDIN>);
my @available_cmd = keys(%{$opts});
push(@available_cmd, 'shell');
my $term = Term::ReadLine->new('pimpd2');
my $attr = $term->Attribs;
$attr->{completion_function} = sub {
my($text, $line, $start) = @_;
lib/App/Pimpd/Shell.pm view on Meta::CPAN
};
$attr->{autolist} = 0;
$attr->{maxcomplete} = 0;
# Sane keymap please.
$term->set_keymap('vi');
my $choice;
while(1) {
$choice = $term->readline(fg($c[6], 'pimpd') . fg('bold', '> '));
$term->addhistory($choice) if $choice =~ /\S/m;
($cmd) = $choice =~ m/^(\S+)/m;
($arg) = $choice =~ m/\s+(.+)$/m;
@cmd_args = split(/\s+/m, $arg);
if(defined($opts->{$cmd})) {
$mpd->play;
$opts->{$cmd}->(@cmd_args);
}
lib/App/Pimpd/Validate.pm view on Meta::CPAN
if(scalar(@choices) == 0) {
print STDERR "No such playlist '" . fg($c[5], $list), "'\n";
return;
}
print "'all' uses all playlists\n\n";
my $i = 0;
for my $choice(@choices) {
print fg('bold', sprintf("%3d", $i)), " $choice\n";
$i++;
}
print "choice: ";
chomp(my $answer = <STDIN>);
if( ($answer eq 'all') or ($answer eq '') ) {
return @choices;
}
elsif($answer eq 'current') {
return(undef);
'cp|copy' => sub { cp(@ARGV ? @ARGV : $config{target_directory}) },
'cpa|copy-album' => sub {
cp_album(@ARGV ? @ARGV : $config{target_directory})
},
'fav|favorite|love' => sub { add_to_favlist(@ARGV); }, # FIXME
'loved' => sub {
if(already_loved($mpd->current->file)) {
printf("%s, %s by %s is loved.\n",
fg('bold', 'Yes'),
fg($c[10], $mpd->current->title),
fg($c[2], fg('bold', $mpd->current->artist)),
);
}
else {
printf("%s, %s by %s is not loved yet.\n",
fg('bold', 'No'),
fg($c[10], $mpd->current->title),
fg($c[2], fg('bold', $mpd->current->artist)),
);
}
},
'unlove' => sub { remove_favorite(@ARGV); },
'aa|add-album' => sub {
add_to_playlist( map{ $_->file } get_album_songs(@ARGV) );
},
'slove' => sub {