1# Copyright (C) 2005-2009 Quentin Sculo <squentin@free.fr> 2# 3# This file is part of Gmusicbrowser. 4# Gmusicbrowser is free software; you can redistribute it and/or modify 5# it under the terms of the GNU General Public License version 3, as 6# published by the Free Software Foundation 7 8use strict; 9use warnings; 10 11package GMB::DBus::Object; 12 13use base 'Net::DBus::Object'; 14use Net::DBus::Exporter 'org.gmusicbrowser'; 15 16sub new 17{ my ($class,$service) = @_; 18 my $self = $class->SUPER::new($service, '/org/gmusicbrowser'); 19 bless $self, $class; 20 21 Glib::Idle->add( 22 sub { ::Watch($self,CurSong => \&SongFieldsChanged); 23 ::Watch($self,CurSongID =>\&SongChanged); 24 ::Watch($self,PlayingSong =>\&PlayingSongChanged); 25 #::Watch($self,Save => \&GMB::DBus::Quit); 26 0; 27 }); 28 29 return $self; 30} 31 32dbus_method('RunCommand', ['string'], [],{no_return=>1}); 33sub RunCommand 34{ my ($self,$cmd) = @_; 35 warn "Received DBus command : '$cmd'\n"; 36 ::run_command(undef,$cmd); 37} 38 39dbus_method('CurrentSong', [], [['dict', 'string', 'string']]); 40sub CurrentSong 41{ my $self=$_[0]; 42 return {} unless defined $::SongID; 43 my %h; 44 $h{$_}=Songs::Get($::SongID,$_) for Songs::PropertyFields(), qw/uri album_picture/; 45 #warn "$_:$h{$_}\n" for sort keys %h; 46 return \%h; 47} 48dbus_method('CurrentSongFields', [['array', 'string']], [['array', 'string']]); 49sub CurrentSongFields 50{ my ($self,$fields)=@_; 51 return [] unless defined $::SongID; 52 my @ret= Songs::Get($::SongID,@$fields); 53 return \@ret; 54} 55 56dbus_method('GetPosition', [], ['double']); 57sub GetPosition 58{ my $self=$_[0]; 59 return $::PlayTime || 0; 60} 61 62dbus_method('Playing', [], ['bool']); 63sub Playing 64{ return $::TogPlay ? 1 : 0; 65} 66 67dbus_method('Set', [['struct', 'string', 'string', 'string']], ['bool']); 68sub Set 69{ my ($self,$array)=@_; 70 Songs::SetTagValue(@$array); #return false on error, true if ok 71} 72dbus_method('Get', [['struct', 'string', 'string']], ['string']); 73sub Get 74{ my ($self,$array)=@_; 75 Songs::GetTagValue(@$array); 76} 77dbus_method('GetLibrary', [], [['array', 'uint32']]); 78sub GetLibrary 79{ $::Library; 80} 81 82dbus_method('GetAlbumCover', ['uint32'], ['string']); 83sub GetAlbumCover 84{ my ($self,$ID)=@_; 85 my $file=Songs::Get($ID,'album_picture'); 86 return $file; 87} 88 89#slow, not a good idea 90dbus_method('GetAlbumCoverData', ['uint32'], [['array', 'byte']]); 91sub GetAlbumCoverData 92{ my ($self,$ID)=@_; 93 my $file=GetAlbumCover($self,$ID); 94 return undef unless $file && -r $file; 95 my $data; 96 if ($file=~m/\.(?:mp3|flac)$/i) 97 { $data=ReadTag::PixFromMusicFile($file); 98 } 99 else 100 { open my$fh,'<',$file; binmode $fh; 101 read $fh,$data, (stat $file)[7]; 102 close $fh; 103 } 104 return [map ord,split //, $data]; 105} 106 107dbus_method(CopyFields => [['array', 'string']], ['bool']); 108#copy fields from one song to another 109#1st arg : filename of ID of source, 2nd arg filename or ID of dest, both must be in the library, 110#following args are list of fields, example : added, lastplay, playcount, lastskip, skipcount, rating, label 111sub CopyFields 112{ my ($self,$array)=@_; 113 #my ($file1,$file2,@fields)=@$array; 114 Songs::CopyFields(@$array); #returns true on error 115} 116 117dbus_signal(SongFieldsChanged => ['uint32']); 118sub SongFieldsChanged 119{ $_[0]->emit_signal(SongFieldsChanged => $::SongID||0); 120} 121dbus_signal(SongChanged => ['uint32']); 122sub SongChanged 123{ $_[0]->emit_signal(SongChanged => $::SongID||0); 124} 125dbus_signal(PlayingSongChanged => ['uint32']); 126sub PlayingSongChanged 127{ $_[0]->emit_signal(PlayingSongChanged => $::SongID||0); 128} 129 130package GMB::DBus; 131 132use Net::DBus; 133use Net::DBus::Service; 134 135my $not_glib_dbus; 136our $bus; 137eval { require Net::DBus::GLib; $bus=Net::DBus::GLib->session; }; 138unless ($bus) 139{ #warn "Net::DBus::GLib not found (not very important)\n"; 140 $not_glib_dbus=1; 141 $bus= Net::DBus->session; 142} 143 144Glib::Idle->add(\&init); #initialize once the main gmb init is finished 145 146sub init 147{ #my $bus = Net::DBus->session; 148 my $service= $bus->export_service($::DBus_id); # $::DBus_id is 'org.gmusicbrowser' by default 149 my $object = GMB::DBus::Object->new($service); 150 DBus_mainloop_hack() if $not_glib_dbus; 151 0; #called in an idle, return 0 to run only once 152} 153 154sub DBus_mainloop_hack 155{ # use Net::DBus internals to connect it to the Glib mainloop, though unlikely, it may break with future version of Net::DBus 156 use Net::DBus::Reactor; 157 my $reactor=Net::DBus::Reactor->main; 158 159 for my $ref (['in','read'],['out','write'], ['err','exception']) 160 { my ($type1,$type2)=@$ref; 161 for my $fd (keys %{$reactor->{fds}{$type2}}) 162 { #warn "$fd $type2"; 163 Glib::IO->add_watch($fd,$type1, 164 sub{ my $cb=$reactor->{fds}{$type2}{$fd}{callback}; 165 $cb->invoke if $cb; 166 $_->invoke for $reactor->_dispatch_hook; 167 1; 168 }) if $reactor->{fds}{$type2}{$fd}{enabled}; 169 #Glib::IO->add_watch($fd,$type1,sub { Net::DBus::Reactor->main->step;Net::DBus::Reactor->main->step;1; }) if $reactor->{fds}{$type2}{$fd}{enabled}; 170 } 171 } 172 173 # run the dbus mainloop once so that events already pending are processed 174 # needed if events already waiting when gmb is starting 175 my $timeout=$reactor->add_timeout(1, Net::DBus::Callback->new( method => sub {} )); 176 Net::DBus::Reactor->main->step; 177 $reactor->remove_timeout($timeout); 178} 179 1801; 181