summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clerk.conf4
-rwxr-xr-xclerk.pl277
-rw-r--r--clerk.tmux55
-rw-r--r--install.sh75
4 files changed, 132 insertions, 279 deletions
diff --git a/clerk.conf b/clerk.conf
index ffec2c6..8400919 100644
--- a/clerk.conf
+++ b/clerk.conf
@@ -3,8 +3,8 @@
mpd_host=tauron
# define file paths
-database=/home/carnager/.config/clerk/db_msgpack
-tmux_config=/home/carnager/.config/clerk/clerk.tmux
+database=PLACEHOLDER/.config/clerk/database.mpk
+tmux_config=PLACEHOLDER/.config/clerk/clerk.tmux
# backend to use, possible options: fzf and rofi
backend=fzf
diff --git a/clerk.pl b/clerk.pl
deleted file mode 100755
index 7b0f5e6..0000000
--- a/clerk.pl
+++ /dev/null
@@ -1,277 +0,0 @@
-#!/usr/bin/env perl
-
-binmode(STDOUT, ":utf8");
-use v5.10;
-use warnings;
-use strict;
-use utf8;
-use Config::Simple;
-use DDP;
-use Data::Dumper;
-use Data::MessagePack;
-use File::Basename;
-use File::Path qw(make_path);
-use File::Slurper 'read_binary';
-use File::stat;
-use Getopt::Std;
-use HTTP::Date;
-use IO::Select;
-use IPC::Run qw( timeout start );
-use List::Util qw(any);
-use Net::MPD;
-use autodie;
-
-$ENV{TMUX_TMPDIR}='/tmp/clerk/tmux';
-make_path($ENV{TMUX_TMPDIR}) unless(-d $ENV{TMUX_TMPDIR});
-
-my $config_file = $ENV{'HOME'} . "/.config/clerk/clerk.conf";
-
-if ($ENV{CLERK_CONF}) {
- $config_file = $ENV{CLERK_CONF};
-}
-
-# read configuration file
-my $cfg = new Config::Simple(filename=>"$config_file");
-
-my $general_cfg = $cfg->param(-block=>"General");
-my $mpd_host = $general_cfg->{mpd_host};
-my $tmux_config = $general_cfg->{tmux_config};
-my $db_file = $general_cfg->{database};
-my $backend = $general_cfg->{backend};
-my $chunksize = $general_cfg->{chunksize};
-
-my $columns_cfg = $cfg->param(-block=>"Columns");
-my $albumartist_l = $columns_cfg->{albumartist_l};
-my $album_l = $columns_cfg->{album_l};
-my $date_l = $columns_cfg->{date_l};
-my $title_l = $columns_cfg->{title_l};
-my $track_l = $columns_cfg->{track_l};
-my $artist_l = $columns_cfg->{artist_l};
-
-if ($ENV{CLERK_BACKEND}) {
- $backend = $ENV{CLERK_BACKEND};
-}
-
-# open connection to MPD
-my $mpd = Net::MPD->connect($ENV{MPD_HOST} // $mpd_host // 'localhost');
-
-sub main {
- create_db();
- if ($backend eq "fzf") {
- system('tmux', 'has-session', '-t', 'music');
- if ($? != -0) {
- system('tmux', '-f', $tmux_config, 'new-session', '-s', 'music', '-n', 'albums', '-d', './clerk.pl', '-a');
- system('tmux', 'new-window', '-t', 'music', '-n', 'tracks', './clerk.pl', '-t');
- system('tmux', 'new-window', '-t', 'music', '-n', 'latest', './clerk.pl', '-l');
- system('tmux', 'new-window', '-t', 'music', '-n', 'playlists', './clerk.pl', '-p');
- system('tmux', 'new-window', '-t', 'music', '-n', 'queue', 'ncmpcpp');
- }
- system('tmux', 'attach', '-t', 'music');
- }
-# elsif ($backend eq "rofi") {
- my %options=();
- getopts("talp", \%options);
-
- if (defined $options{t}) {
- list_db_entries_for("Tracks");
- } elsif (defined $options{a}) {
- list_db_entries_for("Albums");
- } elsif (defined $options{p}) {
- list_playlists();
- } elsif (defined $options{l}) {
- list_db_entries_for("Latest");
- }
-}
-
-
-sub create_db {
- # Get database copy and save as messagepack file, if file is either missing
- # or older than latest mpd database update.
- # get number of songs to calculate number of searches needed to copy mpd database
- my $mpd_stats = $mpd->stats();
- my $songcount = $mpd_stats->{songs};
- my $last_update = $mpd_stats->{db_update};
-
- if (!-f "$db_file" || stat("$db_file")->mtime < $last_update) {
- print STDERR "::: No cache found or cache file outdated\n";
- print STDERR "::: Chunksize set to $chunksize songs\n";
- my $times = int($songcount / $chunksize + 1);
- print STDERR "::: Requesting $times chunks from MPD\n";
- my @db;
- # since mpd will silently fail, if response is larger than command buffer, let's split the search.
- my $chunk_size = $chunksize;
- for (my $i=0;$i<=$songcount;$i+=$chunk_size) {
- my $endnumber = $i+$chunk_size;
- my @temp_db = $mpd->search('filename', '', 'window', "$i:$endnumber");
- push @db, @temp_db;
- }
-
- # only save relevant tags to keep messagepack file small
- # note: maybe use a proper database instead? See list_album function.
- my @filtered = map { $_->{mtime} = str2time($_->{'Last-Modified'}); +{$_->%{qw/Album Artist Date AlbumArtist Title Track uri mtime/}} } @db;
- pack_msgpack(\@filtered);
- }
-}
-
-sub backend_call {
- my ($in, $fields) = @_;
- my $input;
- my $out;
- $fields //= "1,2,3";
- my %backends = (
- fzf => [ qw(fzf
- --reverse
- --no-sort
- -m
- -e
- -i
- -d
- \t
- --tabstop=4
- +s
- --ansi),
- "--with-nth=$fields"
- ],
- rofi => [
- 'rofi',
- '-width',
- '1300',
- '-dmenu',
- '-multi-select',
- '-i',
- '-p',
- '> '
- ]
- );
- my $handle = start $backends{$backend} // die('backend not found'), \$input, \$out;
- $input = join "", (@{$in});
- finish $handle or die "No selection";
- return $out;
-}
-
-sub pack_msgpack {
- my ($filtered_db) = @_;
- my $msg = Data::MessagePack->pack($filtered_db);
- my $filename = "$db_file";
- open(my $out, '>:raw', $filename) or die "Could not open file '$filename' $!";
- print $out $msg;
- close $out;
-}
-
-sub unpack_msgpack {
- my $mp = Data::MessagePack->new->utf8();
- my $msgpack = read_binary("$db_file");
- my $rdb = $mp->unpack($msgpack);
- return $rdb;
-}
-
-sub do_action {
- my ($in, $context) = @_;
- my @action_items = ("Add\n", "Replace\n");
- my $action = backend_call(\@action_items);
- if ($action eq "Replace\n") {
- $mpd->clear();
- }
- my $input;
- if ($context eq "playlist") {
- chomp $in;
- $mpd->load("$in");
- } elsif ($context eq "tracks") {
- foreach my $line (split /\n/, $in) {
- my $uri = (split /[\t\n]/, $line)[-1];
- $mpd->add($uri);
- }
- }
- if ($action eq "Replace\n") {
- $mpd->play();
- }
- my @queue_cmd = ('tmux', 'findw', '-t', 'music', 'queue');
- system(@queue_cmd);
-}
-
-sub list_playlists {
- my @playlists = $mpd->list_playlists();
- my $output = formated_playlists(\@playlists);
-
- for (;;) {
- my $out = backend_call($output);
- do_action($out, "playlist");
- }
-}
-
-sub formated_albums {
- my ($rdb, $sorted) = @_;
-
- my %uniq_albums;
- for my $i (@$rdb) {
- my $newkey = join "", $i->@{qw/AlbumArtist Album Date/};
- if (!exists $uniq_albums{$newkey}) {
- my $dir = (dirname($i->{uri}) =~ s/\/CD.*$//r);
- $uniq_albums{$newkey} = {$i->%{qw/AlbumArtist Album Date mtime/}, Dir => $dir};
- } else {
- if ($uniq_albums{$newkey}->{'mtime'} < $i->{'mtime'}) {
- $uniq_albums{$newkey}->{'mtime'} = $i->{'mtime'}
- }
- }
- }
-
- my @albums;
- my $fmtstr = join "", map {"%-${_}.${_}s\t"} ($albumartist_l, $date_l, $album_l);
-
- my @skeys;
- if ($sorted) {
- @skeys = sort { $uniq_albums{$b}->{mtime} <=> $uniq_albums{$a}->{mtime} } keys %uniq_albums;
- } else {
- @skeys = sort keys %uniq_albums;
- }
-
- for my $k (@skeys) {
- my @vals = (map { "$_" // "Unknown" } $uniq_albums{$k}->@{qw/AlbumArtist Date Album/}), $uniq_albums{$k}->{Dir};
- my $strval = sprintf $fmtstr."%s\n", @vals;
- push @albums, $strvals;
-
- }
-
- return \@albums;
-}
-
-sub formated_tracks {
- my ($rdb) = @_;
- my $fmtstr = join "", map {"%-${_}.${_}s\t"} ($track_l, $title_l, $artist_l, $album_l);
- my @tracks = map {
- sprintf $fmtstr."%-s\n", (map { "$_" // "unknown" } $_->@{qw/Track Title Artist Album/}), $_->{uri};
-# sprintf $fmtstr."%-s\n", $_->@{qw/Track Title Artist Album uri/}
- } @{$rdb};
-
- return \@tracks;
-}
-
-sub formated_playlists {
- my ($rdb) = @_;
- my @playlists = map {
- sprintf "%s\n", $_->{playlist}
- } @{$rdb};
-
- return \@playlists;
-}
-
-sub list_db_entries_for {
- my ($kind) = @_;
- die "Wrong kind" unless any {; $_ eq $kind} qw/Albums Latest Tracks/;
-
- my $rdb = unpack_msgpack();
- my %fields = (Albums=> "1,2,3", Latest => "1,2,3", Tracks => "1,2,3,4");
- my %formater = (
- Albums => sub {formated_albums(@_, 0)},
- Latest => sub {formated_albums(@_, 1)},
- Tracks => \&formated_tracks
- );
-
- my $output = $formater{$kind}->($rdb);
- for (;;) {
- my $out = backend_call($output, $fields{$kind});
- do_action($out, "tracks");
- }
-}
-
-main;
diff --git a/clerk.tmux b/clerk.tmux
new file mode 100644
index 0000000..6469cb8
--- /dev/null
+++ b/clerk.tmux
@@ -0,0 +1,55 @@
+# Status bar
+set-option -g status-position top
+set -g status-interval 30
+set -g status-justify centre
+set -g status-left-length 40
+set -g status-left ''
+set -g status-right ''
+
+
+# Colors
+set -g status-bg colour235
+set -g status-fg default
+setw -g window-status-current-bg default
+setw -g window-status-current-fg default
+setw -g window-status-current-attr dim
+setw -g window-status-bg default
+setw -g window-status-fg white
+setw -g window-status-attr bright
+setw -g window-status-format ' #[fg=colour243,bold]#W '
+setw -g window-status-current-format ' #[fg=yellow,bold]#[bg=colour235]#W '
+
+# !Dont remove this keybinding header! (used to generate help)
+## Key Bindings
+bind-key -n F1 findw albums
+bind-key -n F2 findw tracks
+bind-key -n F3 findw latest
+bind-key -n F4 findw playlists
+bind-key -n F8 findw lyrics
+bind-key -n F5 findw queue
+bind-key -n C-F5 run-shell 'mpc prev --quiet'
+bind-key -n C-F6 run-shell 'mpc toggle --quiet'
+bind-key -n C-F7 run-shell 'mpc stop > /dev/null'
+bind-key -n C-F8 run-shell 'mpc next --quiet'
+bind-key -n C-F12 run-shell 'forceupdate="true" clerk_fzf --update'
+bind-key -n F12 run-shell 'clerk_fzf --update'
+bind-key -n F9 run-shell 'clerk_fzf --random_album'
+bind-key -n F10 run-shell 'clerk_fzf --random_tracks'
+bind-key -n C-h run-shell 'clerk_fzf --help'
+bind-key -n C-q kill-session -t music
+
+
+# tmux options
+set -g set-titles on
+set -g set-titles-string '#T'
+set -g default-terminal "screen-256color"
+setw -g mode-keys vi
+set -sg escape-time 1
+set -g repeat-time 1000
+set -g base-index 1
+setw -g pane-base-index 1
+set -g renumber-windows on
+unbind C-b
+set -g prefix C-a
+unbind C-p
+bind C-p paste-buffer
diff --git a/install.sh b/install.sh
new file mode 100644
index 0000000..c7fa3aa
--- /dev/null
+++ b/install.sh
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+
+dependencies=(
+ "Config::Simple"
+ "Data::Printer"
+ "inc::Module::Install"
+ "Data::MessagePack"
+ "File::Path"
+ "Net::MPD"
+ )
+
+printf "%s\n" "This script will install needed cpan modules"
+printf "%s\n" "and copy configs to $HOME/.config/clerk"
+
+read -e -p "Proceed? (Y/n) > " go_on
+go_on=${go_on:-y}
+
+case $go_on in
+ n) exit;
+ ;;
+esac
+
+read -e -p "Configure cpanp to install modules via pacman? (Y/n) > " cpan_arch
+cpan_arch=${cpan_arch:-y}
+
+
+case $cpan_arch in
+ y) if [[ -z $(pacman -Qsq perl-cpanplus-dist-arch) ]]
+ then
+ read -e -p "perl-cpanplus-dist-arch package not found. Install? (Y/n) > " cpanp_dist_install
+ cpanp_dist_install=${cpanp_dist_install:-y}
+ case $cpanp_dist_install in
+ y) yes | sudo pacman -S perl-cpanplus-dist-arch
+ ;;
+ esac
+ fi
+ setupdistarch;
+esac
+
+read -e -p "Install dependencies for clerk? (Y/n) > " deps_choice
+deps_choice=${deps_choice:-y}
+
+case "${deps_choice}" in
+ y)
+ for dep in "${dependencies[@]}"
+ do
+ cpanp i "${dep}"
+ done
+ sudo pacman -S perl-config-simple perl-http-date perl-ipc-run
+ ;;
+esac
+
+read -e -p "Set installation directory. (Default: $HOME/bin) > " foo
+foo=${foo:-$HOME/bin}
+case $foo in
+ y) export path="$HOME/bin";
+ ;;
+ *) export path="$foo";
+ ;;
+esac
+
+read -e -p "Install clerk to $path? (Y/n) > " install
+install=${install:-y}
+
+case $install in
+ y) cp clerk $path;
+ if [[ ! -d "${HOME}/.config/clerk" ]]
+ then
+ mkdir "${HOME}/.config/clerk"
+ fi
+ cp clerk.tmux clerk.conf "${HOME}/.config/clerk"
+ sed -i "s@PLACEHOLDER@"$HOME"@" "${HOME}/.config/clerk/clerk.conf"
+ ;;
+ *) exit;
+esac