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
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 to destination
copya copy album to 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
randomalbum and n random full albums
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
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", fg('bold', undef),
'File', fg($c[7], $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
$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("%19s %51.51s |@{[fg('bold', 'x')]}| %-47.47s\n",
bg($c[4], $i), fg($c[5], fg('bold', $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 "$_\n" for @random;
clear_playlist();
add_to_playlist(@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 {