From d4cb48883528f531e9bac41808be97095809915f Mon Sep 17 00:00:00 2001 From: Rasmus Steinke Date: Sat, 23 Sep 2017 15:18:31 +0200 Subject: initial support for writing ratings to files, using a helper client --- clerk | 52 +++++++++++++++++----- clerk.conf | 5 ++- clerk_rating_client | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+), 12 deletions(-) create mode 100755 clerk_rating_client diff --git a/clerk b/clerk index a0273f4..e2bedd7 100755 --- a/clerk +++ b/clerk @@ -8,7 +8,7 @@ use utf8; use Config::Simple; use Time::HiRes qw (sleep); use Data::MessagePack; -#use DDP; +use DDP; use Encode qw(decode encode); use File::Basename; use File::Path qw(make_path); @@ -44,6 +44,7 @@ my $mpd_host = $general_cfg->{mpd_host}; my $tmux_config = $general_cfg->{tmux_config}; my $db_file = $general_cfg->{database}; my $songs = $general_cfg->{songs}; +my $tagging = $general_cfg->{tagging}; my $chunksize = $general_cfg->{chunksize}; my $player = $general_cfg->{player}; @@ -354,7 +355,7 @@ sub do_action { } } } elsif ($context eq "Tracks") { - my @action_items = ("Add\n", "Replace\n", "Rate Track\n"); + my @action_items = ("Add\n", "Replace\n", "---\n", "Rate Track\n"); $action = backend_call(\@action_items); if ($action eq "Replace\n") { $mpd->clear(); @@ -368,7 +369,10 @@ sub do_action { foreach my $line (split /\n/, $in) { my $uri = (split /[\t\n]/, $line)[-1]; $uri = decode('UTF-8', $uri ); - $mpd->sticker_value("song", "$uri", "rating", "$rating") + if ($tagging eq "true") { + $mpd->sticker_value("song", "$uri", "rating", "$rating"); + } + $mpd->send_message('rating', "$uri\tRATING\t${rating}"); } } elsif ($action eq "Add\n" || $action eq "Replace\n") { @@ -384,17 +388,45 @@ sub do_action { $mpd->play(); } } elsif ($context eq "Albums" || $context eq "Latest") { - my @action_items = ("Add\n", "Replace\n"); + my @action_items = ("Add\n", "Replace\n", "---\n", "Rate Album\n"); $action = backend_call(\@action_items); + if ($action eq "Rate Album\n") { + my @rating_value = ("1\n", "2\n", "3\n", "4\n", "5\n", "6\n", "7\n", "8\n", "9\n", "10\n"); + my $rating; + $rating = backend_call(\@rating_value); + chomp $rating; + my $input; + foreach my $line (split /\n/, $in) { + my $song_tags; + my $uri = (split /[\t\n]/, $line)[-1]; + $uri = decode('UTF-8', $uri ); + my @files = $mpd->search('filename', $uri); + my @song_tags = $files[0]; + my $artist = $song_tags[0]->{AlbumArtist}; + my $album = $song_tags[0]->{Album}; + my $date = $song_tags[0]->{Date}; + my @songs_to_tag = $mpd->search('albumartist', $artist, 'album', $album, 'date', $date); + foreach my $songs (@songs_to_tag) { + my $filename = $songs->{uri}; + $mpd->sticker_value("song", $filename, "albumrating", "$rating"); + if ($tagging eq "true") { + $mpd->send_message('rating', "$filename\tALBUMRATING\t${rating}"); + } + } + } + } + if ($action eq "Replace\n") { $mpd->clear(); } - my $input; - foreach my $line (split /\n/, $in) { - my $uri = (split /[\t\n]/, $line)[-1]; - $uri = decode('UTF-8', $uri ); - $mpd->add($uri); - system(@queue_cmd); + if ($action eq "Add\n" || $action eq "Replace\n") { + my $input; + foreach my $line (split /\n/, $in) { + my $uri = (split /[\t\n]/, $line)[-1]; + $uri = decode('UTF-8', $uri ); + $mpd->add($uri); + system(@queue_cmd); + } } if ($action eq "Replace\n") { $mpd->play(); diff --git a/clerk.conf b/clerk.conf index d42e32b..32abf9b 100644 --- a/clerk.conf +++ b/clerk.conf @@ -15,8 +15,9 @@ songs=20 # if mpd drops the connection while updating, reduce this. chunksize=30000 -# rofi theme to use for clerk -rofi_theme=clerk +# write tags to audio files. Needs running clerk_rating_client on machine with audio files +# ratings will always be written to sticker database. +tagging=false [Columns] # width of columns diff --git a/clerk_rating_client b/clerk_rating_client new file mode 100755 index 0000000..4d56526 --- /dev/null +++ b/clerk_rating_client @@ -0,0 +1,123 @@ +#!/usr/bin/perl + +binmode(STDOUT, ":utf8"); +use v5.10; +use warnings; +use Array::Utils qw(:all); +use DDP; +use File::Spec; +use strict; +use utf8; +use Encode qw(decode encode); +use File::Find; +use Getopt::Std; +use Net::MPD; + +my $mpd_host = "tauron"; +my $mpd = Net::MPD->connect($ENV{MPD_HOST} // $mpd_host // 'localhost'); +my $music_root = "/mnt/raid/Audio/Rips"; + +sub main { + my %options=(); + getopts("rst", \%options); + + if ($options{r} // $options{s} // $options{t}) { + if (defined $options{r}) { subscribe_ratings_channel(); track_rating(); } + elsif (defined $options{s}) { subscribe_ratings_channel(); sync_ratings(); } + elsif (defined $options{t}) { tag_from_sticker(); } + } else { subscribe_ratings_channel(); track_rating(); }; +} + +sub subscribe_ratings_channel { + $mpd->subscribe('rating'); +} + +sub track_rating { + while(1) { + $mpd->idle('message'); + my @blub = $mpd->read_messages; + foreach (@blub) { + my $string = $_->{message}; + my @array = split("\t", $string); + my $uri = $array[0]; + my $mode = $array[1]; + my $rating = $array[2]; + $uri = decode('UTF-8', $uri ); + my @files = $mpd->search('filename', $uri); + my @song_tags = $files[0]; + my $albumartist = $song_tags[0]->{AlbumArtist}; + my $artist = $song_tags[0]->{Artist}; + my $title = $song_tags[0]->{Title}; + my $album = $song_tags[0]->{Album}; + if ($uri =~ /.*.flac$/) { + if ($mode eq "RATING") { + print ":: tagging track \"${title}\" by \"${artist}\" with rating of \"${rating}\"\n"; + } elsif ($mode eq "ALBUMRATING") { + print ":: tagging track \"${title}\" by \"${albumartist}\" with albumrating of \"${rating}\"\n"; + } + system('metaflac', '--remove-tag=RATING', "${music_root}/${uri}"); + system('metaflac', "--set-tag=${mode}=${rating}", "${music_root}/${uri}"); + } + elsif ($uri =~ /.*.mp3$/) { + if ($mode eq "RATING") { + print ":: tagging track \"${title}\" by \"${artist}\" with rating of \"${rating}\"\n"; + } elsif ($mode eq "ALBUMRATING") { + print ":: tagging track \"${title}\" by \"${albumartist}\" with albumrating of \"${rating}\"\n"; + } + system('mid3v2', "--TXXX:${mode}:${rating}", "${music_root}/${uri}"); + } + elsif ($uri =~ /.*.ogg$/) { + print "!! OGG files not supported, yet\n"; + } + } + } +} + +sub sync_ratings { + my @sticker_uris; + my @actual_uris; + my @available_stickers = $mpd->sticker_find('song', 'rating', ''); + foreach my $rated_song (@available_stickers) { + push @sticker_uris, "$rated_song->{file}"; + } + + my @absolute; + find({ + wanted => sub { push @absolute, $_ if -f and -r }, + no_chdir => 1, + }, $music_root); + my @relative = map { File::Spec->abs2rel($_, $music_root) } @absolute; + push @actual_uris, $_ for @relative; + + my @diff = array_diff(@sticker_uris, @actual_uris); + foreach my $unrated_song (@diff) { + if ( $unrated_song =~ /.*.flac$/) { + my $rating = system('metaflac', '--show-tag=RATING', "${music_root}/${unrated_song}"); + print "$rating\n"; + if ($rating ne "0") { + print "rating ${music_root}/${unrated_song} with $rating\n"; + $mpd->sticker_value("song", "$unrated_song", "rating", "$rating"); + } + } + } +} + +sub tag_from_sticker { + my @available_stickers = $mpd->sticker_find('song', 'rating', ''); + foreach my $rated_song (@available_stickers) { + my $uri = $rated_song->{file}; + my $rating = $rated_song->{sticker}; + if ($uri =~ /.*.flac$/) { + system('metaflac', '--remove-tag=RATING', "${music_root}/${uri}"); + system('metaflac', "--set-tag=RATING=$rating", "${music_root}/${uri}"); + } + elsif ($uri =~ /.*.mp3$/) { + system('mid3v2', "--TXXX:RATING:${rating}", "${music_root}/${uri}"); + } + elsif ($uri =~ /.*.ogg$/) { + print "!! OGG files not supported, yet\n"; + } + } +} + +main(); -- cgit v1.2.3-24-g4f1b