diff options
author | Tom Ryder <tom@sanctum.geek.nz> | 2015-12-05 13:00:45 +1300 |
---|---|---|
committer | Tom Ryder <tom@sanctum.geek.nz> | 2015-12-05 13:00:45 +1300 |
commit | 4419fac56154a1cc39bca7b6abd0c80c6e0b3194 (patch) | |
tree | aaf5ba7bf569af50a058a664581f705e1c0fe595 /bin | |
parent | Add mplrc-notify-send example (diff) | |
download | mpdlrc-4419fac56154a1cc39bca7b6abd0c80c6e0b3194.tar.gz mpdlrc-4419fac56154a1cc39bca7b6abd0c80c6e0b3194.zip |
Move scripts into bin subdir
Diffstat (limited to 'bin')
-rwxr-xr-x | bin/mpdlrc | 154 | ||||
-rwxr-xr-x | bin/mpdlrc-notify-send | 16 |
2 files changed, 170 insertions, 0 deletions
diff --git a/bin/mpdlrc b/bin/mpdlrc new file mode 100755 index 0000000..b127c1c --- /dev/null +++ b/bin/mpdlrc @@ -0,0 +1,154 @@ +#!/usr/bin/env perl + +# +# mpdlrc -- Print timed lyrics from an LRC file for MPD's currently playing +# song line-by-line to stdout. See README.markdown. +# +# Author: Tom Ryder <tom@sanctum.geek.nz> +# Copyright: 2015 +# +package Sanctum::Mpdlrc; + +# Force me to write this properly +use strict; +use warnings; +use utf8; +use autodie qw(:all); + +# Require a few modules +use Carp; +use Const::Fast; +use Net::MPD; +use Time::HiRes qw(sleep); + +# Require at least Perl 5.12 +use 5.012; + +# Specify version number +our $VERSION = 0.1; + +# Specify some constants to appease Perl::Critic +const my $SECONDS_PER_MINUTE => 60; +const my $HUNDREDTHS_PER_SECOND => 100; + +# Connect to MPD, or give up and cry +my $mpd = Net::MPD->connect() + or croak('Failed to connect to MPD'); + +# We declare the PID outside of the main loop so we can kill it on subsequent +# iterations of the loop, should we need to restart the process. +my $pid; + +# Use UTF-8 for output, because forëigñ charåcters āre ìmpørtánt +binmode STDOUT, ':encoding(utf8)'; +STDOUT->autoflush(1); + +# Loop waiting for MPD events +MPD: while (1) { + + # Get the current status + my $status = $mpd->update_status(); + + # If there's a song playing, we'll try and spit some lyrics + if ( $status->{state} eq 'play' ) { + + # Get details about the current song + my $song = $mpd->current_song(); + + # Fork a new process + $pid = fork; + + # This block should only be run by the fork + if ( !$pid ) { + + # Build the expected filename for the lyric file from the song's + # author and title + my $lfn = sprintf '%s/.lyrics/%s - %s.lrc', $ENV{HOME}, + @{$song}{qw(Artist Title)}; + + # If no such file exists, we have failed + if ( !-e $lfn ) { + exit 1; + } + + # Read a lyrics queue object from the file, providing it with the + # elapsed time (i.e. telling it how far into the song we already + # are) + my $lyrics = read_lyrics_queue( $lfn, $status->{elapsed} ); + + # Step through the lyrics queue object, sleeping the required + # amount of time before printing each line of text to stdout + foreach my $lyric ( @{$lyrics} ) { + sleep $lyric->{delay}; + printf {*STDOUT} "%s\n", $lyric->{text}; + } + + # We, the fork, are done! + exit; + } + } + + # Wait for something else to happen to the player, whether or not there's a + # forked process going + $mpd->idle('player'); + + # Something important happened; kill any running lyric processes + if ($pid) { + kill 'INT', $pid; + } +} + +# Subroutine to read lyrics from the given filename and return a queue object +# specifying a list of lyrics to display and how long to wait before displaying +# each line +sub read_lyrics_queue { + my ( $lfn, $elapsed ) = @_; + $elapsed //= 0; + + # Read the file into a list of lines + open my $lfh, q{<:encoding(utf8)}, $lfn; + my @lines = readline $lfh; + close $lfh; + + # Start a list of lyric hashrefs + my @lyrics; + + # Read each line + LINE: foreach my $line (@lines) { + + # Get rid of trailing newlines + chomp $line; + + # If the line is in LRC format, we'll queue it up + if ( $line =~ m{\[(\d+):(\d+)[.](\d+)\](.+)}msx ) { + + # Read minutes, seconds, hundredth-seconds, and text from the + # matches in the line + my ( $min, $sec, $hsec, $text ) = ( $1, $2, $3, $4 ); + + # Flatten out the times into a number of seconds, fractional + # (that's why we need sleep() from Time::HiRes) + my $tsec = + ( $min * $SECONDS_PER_MINUTE ) + + $sec + + ( $hsec / $HUNDREDTHS_PER_SECOND ); + + # If the lyric is yet to be displayed, i.e. we haven't already + # passed the appropriate point in the song, queue it up and + # increment the elapsed time for queuing up the next lyric, if any + if ( $tsec > $elapsed ) { + my $lyric = { + delay => $tsec - $elapsed, + text => $text, + }; + push @lyrics, $lyric; + $elapsed += $lyric->{delay}; + } + + } + } + + # Return a reference to the built lyric object + return \@lyrics; +} + diff --git a/bin/mpdlrc-notify-send b/bin/mpdlrc-notify-send new file mode 100755 index 0000000..977cf1b --- /dev/null +++ b/bin/mpdlrc-notify-send @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +# Check we have the two programs we need +hash mpdlrc || exit +hash notify-send || exit + +# Read the priority and timeout from the environment, or set default values +priority=${MPDLC_PRIORITY:-low} +timeout=${MPDLC_TIMEOUT:-2000} + +# Loop over each line output by mpdlrc (which will need to be somewhere in your +# PATH) and pass it to notify-send(1) +while IFS= read -r lyric ; do + notify-send -u "$priority" -t "$timeout" "$lyric" +done < <(mpdlrc) + |