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 {