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