# amarok by Tobias 'camel69' Wulff and Konrad 'freakazoid' Gräfe use strict; use vars qw($VERSION %IRSSI); $VERSION = "1.0"; %IRSSI = ( authors => "Tobias 'camel69' Wulff, Konrad 'freakazoid' Gräfe", contact => "camel69(at)codeeye.de, freakazoid(at)teamblind.de", name => "Amarok2 (via ssh)", description => "Retrievs song infos and controls Amarok2 via DBUS, optionally running on another computer via ssh", license => "Public Domain", commands => "amarok2", url => "http://freakazoid.teamblind.de/2009/06/17/amarok2-und-irssi/" ); use Irssi; Irssi::settings_add_bool('amarok2', 'amarok2_use_ssh', 1); Irssi::settings_add_str('amarok2', 'amarok2_ssh_client', 'localhost'); Irssi::settings_add_str('amarok2', 'amarok2_ssh_port', '22'); Irssi::settings_add_str('amarok2', 'amarok2_ssh_user', 'changeme'); sub show_help() { my $help = $IRSSI{name}." ".$VERSION." /amarok2 song [loud] Prints the artist and title of the song which is currently played. If the argument loud is given, all users in the current channel can see what song you are currently listening. /amarok2 time [loud] Prints the total time of the song as well as the played time and remaining time. Same behaviour for given argument loud as above. /amarok2 pause Pauses (or unpauses) the current song. /amarok2 play Plays the current song (again). /amarok2 stop Stops the current song. /amarok2 next Skips to the next song. /amarok2 prev Skips to the previous song. /amarok2 seek [+|-]secs|min:secs Seeks to the given position. If + or - is given amaroK seeks relatively the amount of minutes and/or seconds to the current position. /amarok2 vol [0 to 100] Prints or changes the output volume of amaroK. /amarok2 mute Toggles between volume 0 and the last used volume. /amarok2 help Prints this help text. Settings you can change with /SET amarok2_use_ssh: Enable or disable remote amaroK'ing amarok2_ssh_client: IP or hostname of the remote pc amarok2_ssh_port: port of the ssh deamon amarok2_ssh_user: username to log into the remote pc"; print CLIENTCRAP $help; } my $preprint = '%Bamarok2%n> '; # Load settings my $amarok_use_ssh = Irssi::settings_get_bool('amarok2_use_ssh'); my $ssh_client = Irssi::settings_get_str('amarok2_ssh_client'); my $ssh_port = Irssi::settings_get_str('amarok2_ssh_port'); my $ssh_user = Irssi::settings_get_str('amarok2_ssh_user'); sub cmd ($) { my ($postcmd) = @_; my $cmd = 'qdbus org.kde.amarok /Player '.$postcmd; if ($amarok_use_ssh == 1) { return `ssh $ssh_client -l $ssh_user -p $ssh_port '$cmd' 2>/dev/null`; } else { return `$cmd`; } } sub get_status () { # ohne --literal gibt qdbus das Ergebnis nicht aus... -_- my $cmd = 'qdbus --literal org.kde.amarok /Player org.freedesktop.MediaPlayer.GetStatus'; my $status_literal; if ($amarok_use_ssh == 1) { $status_literal = `ssh $ssh_client -l $ssh_user -p $ssh_port '$cmd' 2>/dev/null`; } else { $status_literal = `$cmd`; } my ($status) = 0; if ($status_literal =~ /.*\[Argument: \(iiii\) \d, \d, \d, \d\].*/) { $status = $status_literal =~ /.*\[Argument: \(iiii\) (\d), \d, \d, \d\].*/; } else { return 'error'; } if ($status == 0) { return 'playing'; } elsif ($status == 1) { return 'paused'; } elsif ($status == 2) { return 'stopped'; } else { return 'error'; } } sub amarokSong ($$) { my($witem, $me_cmd) = @_; if ($me_cmd == 1) { if (!$witem or $witem->{type} ne 'CHANNEL') { print CLIENTCRAP $preprint."The option 'loud' can only be used in channels."; return; } } my $metadata = cmd('GetMetadata'); my ($artist) = $metadata =~ /artist: (.+)/; $artist =~ s/\r//; my ($title) = $metadata =~ /title: (.+)/; $title =~ s/\r//; my $text = 'hört: '.$artist.' - '.$title.' d-.-b'; $text =~ s/\n//g; if ($me_cmd == 1) { $witem->command("ME ".$text); } else { print CLIENTCRAP $preprint.$text; } } sub amarokTime ($$) { my ($witem, $me_cmd) = @_; if ($me_cmd == 1 and (!$witem or $witem->{type} ne 'CHANNEL')) { print CLIENTCRAP $preprint."The option 'loud' can only be used in channels."; return; } my $metadata = cmd('GetMetadata'); my ($time_total_secs) = $metadata =~ /mtime: (.+)/; $time_total_secs /= 1000; my $time_played_secs = cmd('PositionGet'); $time_played_secs /= 1000; my $time_remaining_secs = $time_total_secs - $time_played_secs; # Zeiten in richtige Minutenangabe umwandeln my @time_total = (0, $time_total_secs % 60); $time_total[0] = ($time_total_secs - $time_total[1]) / 60; my @time_played = (0, $time_played_secs % 60); $time_played[0] = ($time_played_secs - $time_played[1]) / 60; my @time_remaining = (0, $time_remaining_secs % 60); $time_remaining[0] = ($time_remaining_secs - $time_remaining[1]) / 60; # Text bauen und ausgeben # Gesamtzeit my $text = 'Total time of track is '.$time_total[0].':'; if ($time_total[1] < 10) { $text .= '0'; } $text .= $time_total[1]; # Gespielte Zeit $text .= ' (played: '.$time_played[0].':'; if ($time_played[1] < 10) { $text .= '0'; } $text .= $time_played[1]; # Verbleibende Zeit $text .= ' / remaining: '.$time_remaining[0].':'; if ($time_remaining[1] < 10) { $text .= '0'; } $text .= $time_remaining[1].')'; if ($me_cmd == 1) { $witem->command("SAY ".$text); } else { print CLIENTCRAP $preprint.$text; } } sub amarokSeek ($) { my($time) = @_; # format correct? # just seconds: + or -, some numbers (seconds) # mm:ss format: + or -, some numbers (minutes), :, 2 numbers (seconds) if ($time !~ /^(\+|-)?[0-9]+$/ and $time !~ /^(\+|-)?[0-9]+:[0-9]{2}$/) { print CLIENTCRAP $preprint.'%RERROR%n: Wrong time format (see help for correct format)!'; return; } my $origtime = cmd('PositionGet'); $origtime /= 1000; # Assume there's no + or - my $seek_sign = ''; # Check for + or - in $time # If a sign is found save it in $seek_sign and remove # it from $time. $_ = $time; if (/^\+/) { $seek_sign = '+'; $time =~ s/^\+//g; } elsif (/^-/) { $seek_sign = '-'; $time =~ s/^-//g; } # Now split $timearg at ':' if there's one my @timeparts = split(/:/, $time); # time has format mm:ss if (defined $timeparts[1]) { # Convert $time into secs $time = 60 * $timeparts[0] + $timeparts[1]; } # if there's a + or - recalc $time if ($seek_sign eq '+') { $time = $origtime + $time; } elsif ($seek_sign eq '-') { $time = $origtime - $time; } # print and do it cmd('PositionSet '.($time * 1000)); # zu langsam, zu unnütz #my $newtime = cmd('PositionGet'); #$newtime /= 1000; #print CLIENTCRAP $preprint.'Seeked to '.$newtime.'.'; print CLIENTCRAP $preprint.'Seeked to '.$time.'.'; } sub cmd_amarok ($$$) { my ($args, $server, $witem) = @_; my @arg = split(/ /, $args); # enough arguments? if (scalar(@arg) == 0) { print CLIENTCRAP $preprint.'%RERROR%n: not enough arguments!'; return; } my $loud = 0; if (defined $arg[1] && $arg[1] eq 'loud') { $loud = 1; } # is Amarok running? # if so, is it currently playing? my $status = get_status(); if ($status eq 'error') { print CLIENTCRAP $preprint.'%RERROR%n: amaroK is not running!'; return; } elsif ($status eq 'stopped' && $arg[0] ne 'play' && $arg[0] ne 'help' && $arg[0] ne 'vol' && $arg[0] ne 'mute') { print CLIENTCRAP $preprint.'%RERROR%n: amaroK is not playing yet!'; print CLIENTCRAP $preprint.'Only the play, vol, mute and help commands are available.'; return; } # Amarok 2 is running and playing or some commands are available though. if ($arg[0] eq 'song') { amarokSong($witem, $loud); } elsif ($arg[0] eq 'time') { amarokTime($witem, $loud); } elsif ($arg[0] eq 'pause') { cmd('Pause'); if ($status eq 'paused') { print CLIENTCRAP $preprint.'Song unpaused.'; } elsif ($status == 'playing') { print CLIENTCRAP $preprint.'Song paused.'; } } elsif ($arg[0] eq 'next') { cmd('Next'); print CLIENTCRAP $preprint.'Skipped to next song.'; } elsif ($arg[0] eq 'Prev') { cmd('prev'); print CLIENTCRAP $preprint.'Skipped to previous song.'; } elsif ($arg[0] eq 'Play') { cmd('play'); print CLIENTCRAP $preprint.'Playing song.'; } elsif ($arg[0] eq 'Stop') { cmd('stop'); print CLIENTCRAP $preprint.'Song stopped.'; } elsif ($arg[0] eq 'seek') { if (!(defined $arg[1])) { print CLIENTCRAP $preprint.'Not enough arguments.'; } else { amarokSeek($arg[1]); } } elsif ($arg[0] eq 'vol') { if (!(defined $arg[1])) { my $o_vol = cmd('VolumeGet'); $o_vol =~ s/\r?\n//; print CLIENTCRAP $preprint.'Current volume is '.$o_vol.'%%.'; } else { if ($arg[1] < 0 or $arg[1] > 100) { print CLIENTCRAP $preprint.'Given volume is out of range (0-100)'; return; } cmd('VolumeSet '.$arg[1]); print CLIENTCRAP $preprint.'Volume changed to '.$arg[1].'%%.'; } } elsif ($arg[0] eq 'mute') { cmd('Mute'); print CLIENTCRAP $preprint.'Mute toggled.'; } elsif ($arg[0] eq 'help') { show_help(); } else { print CLIENTCRAP $preprint.'%RERROR%n: Unknown command!'; } } Irssi::command_bind('amarok2' => \&cmd_amarok); foreach my $cmd ('song', 'time', 'pause', 'play', 'stop', 'next', 'prev', 'seek', 'vol', 'mute', 'help') { Irssi::command_bind('amarok2 '.$cmd => sub { cmd_amarok("$cmd ".$_[0], $_[1], $_[2]); } ); } print CLIENTCRAP $preprint.$IRSSI{name}.' '.$VERSION.' loaded: type /amarok help for help';