Merge lp:~gabor-karsay/awn-extras/awnmediaplayers into lp:awn-extras

Proposed by Gabor Karsay
Status: Merged
Merged at revision: 1527
Proposed branch: lp:~gabor-karsay/awn-extras/awnmediaplayers
Merge into: lp:awn-extras
Diff against target: 302 lines (+189/-14)
1 file modified
shared/python/awnmediaplayers.py (+189/-14)
To merge this branch: bzr merge lp:~gabor-karsay/awn-extras/awnmediaplayers
Reviewer Review Type Date Requested Status
Michal Hruby Pending
Review via email: mp+46206@code.launchpad.net

Description of the change

mhr3, do you approve?

It's only about awnmediaplayes.py.

* It adds support for mediaplayers Clementine and Guayadeque and detects and launches already supported players Amarok, Audacious and VLC (searching for the binaries in path). I could have added more, but they are either not in Ubuntu repositories, so I couldn't/didn't want to test, or they have no GUI and are run from commandline. (Amarok launches, but it goes crazy in my Gnome environment.)

* Rhythmbox does not show artwork sometimes. I found the main reason, at least on my system: It doesn't show any artwork that has non-ASCII characters in its path. That is an encoding issue. It's also present in Dockmanager, I will report it later there. Onox reported a similar bug in Dockmanager, might be the same, it's bug #680681.

I've also taken and changed some code from Dockmanager to search in more places for artwork: in the song folder and in ID3 tags. This includes a new optional dependency on Mutagen. I wonder if that should be reported somewhere so that packagers can add it as a proposed dependency?

And a minor bugfix (catching an error while launching via DBus).

To post a comment you must log in.
Revision history for this message
Julien Lavergne (gilir) wrote :

Is a review really necessary ? I think mhr3 would already complain if there is a problem :)

Revision history for this message
Gabor Karsay (gabor-karsay) wrote :

I sort of forgot this one and merged it now since I got only encouraging comments :)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'shared/python/awnmediaplayers.py'
--- shared/python/awnmediaplayers.py 2011-01-07 20:51:06 +0000
+++ shared/python/awnmediaplayers.py 2011-01-13 23:06:58 +0000
@@ -20,6 +20,8 @@
2020
21import sys21import sys
22import os22import os
23import subprocess
24import atexit
2325
24import gobject26import gobject
25import pygtk27import pygtk
@@ -31,9 +33,32 @@
31from dbus.mainloop.glib import DBusGMainLoop33from dbus.mainloop.glib import DBusGMainLoop
32import string34import string
3335
36try:
37 import mutagen.mp3
38 import mutagen.mp4
39 from mutagen.id3 import ID3
40 import tempfile
41 album_art_file = "%s/awnmediaplayer_%s.png" % (tempfile.gettempdir(), os.getenv('USERNAME'))
42 art_icon_from_tag = True
43except ImportError:
44 art_icon_from_tag = False
45
46if gtk.gtk_version >= (2, 18):
47 from urllib import unquote
48
34DBusGMainLoop(set_as_default=True)49DBusGMainLoop(set_as_default=True)
3550
3651
52def cleanup():
53 if art_icon_from_tag:
54 try:
55 os.remove(album_art_file)
56 except OSError:
57 pass
58
59atexit.register(cleanup)
60
61
37def get_app_name():62def get_app_name():
38 player_name = None63 player_name = None
39 bus_obj = dbus.SessionBus().get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')64 bus_obj = dbus.SessionBus().get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
@@ -67,9 +92,35 @@
67 player_name = "DragonPlayer"92 player_name = "DragonPlayer"
68 elif bus_obj.NameHasOwner('org.freedesktop.MediaPlayer') == True:93 elif bus_obj.NameHasOwner('org.freedesktop.MediaPlayer') == True:
69 player_name = "mpDris"94 player_name = "mpDris"
95 elif bus_obj.NameHasOwner('org.mpris.clementine') == True:
96 player_name = "Clementine"
97 elif bus_obj.NameHasOwner('org.mpris.guayadeque') == True:
98 player_name = "Guayadeque"
70 return player_name99 return player_name
71100
72101
102def player_available(executable):
103 """Check if player is installed if it's not in 'Activatable Services' on DBus"""
104
105 for path in os.getenv('PATH').split(':'):
106 if path == '':
107 continue
108 if os.path.isfile(os.path.join(path, executable)):
109 return True
110 return False
111
112
113def launch_player(args):
114 """Launch player if this can't be done via DBus"""
115
116 try:
117 subprocess.Popen(args)
118 except OSError, e:
119 print "awnmediaplayer: error launching %s: %s" % (args, e)
120 return False
121 return True
122
123
73class GenericPlayer(object):124class GenericPlayer(object):
74 """Insert the level of support here"""125 """Insert the level of support here"""
75126
@@ -120,11 +171,13 @@
120 """171 """
121 if (self.dbus_base_name != None):172 if (self.dbus_base_name != None):
122 object_path = '/' + self.dbus_base_name.replace('.', '/')173 object_path = '/' + self.dbus_base_name.replace('.', '/')
123 bus = dbus.SessionBus()174 try:
124 obj = bus.get_object(self.dbus_base_name, object_path)175 bus = dbus.SessionBus()
125 return True176 obj = bus.get_object(self.dbus_base_name, object_path)
126 else:177 return True
127 return False178 except Exception, e:
179 print "awnmediaplayer: error launching %s: %s" % (self.__class__.__name__, e)
180 return False
128181
129 def get_dbus_name(self):182 def get_dbus_name(self):
130 """183 """
@@ -231,7 +284,6 @@
231 if info['arturl'][0:7] == "file://":284 if info['arturl'][0:7] == "file://":
232 result['album-art'] = str(info['arturl'][7:])285 result['album-art'] = str(info['arturl'][7:])
233 if gtk.gtk_version >= (2, 18):286 if gtk.gtk_version >= (2, 18):
234 from urllib import unquote
235 result['album-art'] = unquote(result['album-art'])287 result['album-art'] = unquote(result['album-art'])
236 else:288 else:
237 print "Don't understand the album art location: %s" % info['arturl']289 print "Don't understand the album art location: %s" % info['arturl']
@@ -286,7 +338,8 @@
286 def get_media_info(self):338 def get_media_info(self):
287 self.dbus_driver()339 self.dbus_driver()
288 ret_dict = {}340 ret_dict = {}
289 result = self.rbShell.getSongProperties(self.player.getPlayingUri())341 playinguri = self.player.getPlayingUri()
342 result = self.rbShell.getSongProperties(playinguri)
290343
291 # Currently Playing Title344 # Currently Playing Title
292 if result['artist'] != '':345 if result['artist'] != '':
@@ -304,19 +357,77 @@
304357
305 # cover-art358 # cover-art
306 if 'rb:coverArt-uri' in result:359 if 'rb:coverArt-uri' in result:
307 albumart_exact = result['rb:coverArt-uri']360 albumart_exact = result['rb:coverArt-uri'].encode('utf8')
308 # bug in rhythmbox 0.11.6 - returns uri, but not properly encoded,361 # bug in rhythmbox 0.11.6 - returns uri, but not properly encoded,
309 # but it's enough to remove the file:// prefix362 # but it's enough to remove the file:// prefix
310 albumart_exact = albumart_exact.replace('file://', '', 1)363 albumart_exact = albumart_exact.replace('file://', '', 1)
311 if gtk.gtk_version >= (2, 18):364 if gtk.gtk_version >= (2, 18):
312 from urllib import unquote
313 albumart_exact = unquote(albumart_exact)365 albumart_exact = unquote(albumart_exact)
314 ret_dict['album-art'] = albumart_exact366 # Sanity check if encoding and unquoting did work
367 if os.path.isfile(albumart_exact):
368 ret_dict['album-art'] = albumart_exact
369 return ret_dict
370 else:
371 print "awnmediaplayers: Unquoting error:\n%s\ndoes not match\n%s" % (result['rb:coverArt-uri'], albumart_exact)
372
373 # perhaps it's in the cache folder
374 if 'album' in result and 'artist' in result:
375 cache_dir = os.path.expanduser("~/.cache/rhythmbox/covers")
376 cache_file = '%s/%s - %s.jpg' % (cache_dir, result['artist'], result['album'])
377 if os.path.isfile(cache_file):
378 ret_dict['album-art'] = cache_file
379 return ret_dict
380
381 # The following is based on code from Dockmanager
382 # Copyright (C) 2009-2010 Jason Smith, Rico Tzschichholz, Robert Dyer
383
384 # Look in song folder
385 filename = playinguri.encode('utf8').replace('file://', '', 1)
386 if gtk.gtk_version >= (2, 18):
387 filename = unquote(filename)
388 coverdir = os.path.dirname(filename)
389 if os.path.isdir(coverdir):
390 covernames = ["cover", "album", "albumart", ".folder", "folder"]
391 extensions = [".jpg", ".jpeg", ".png"]
392 for f in os.listdir(coverdir):
393 for ext in extensions:
394 if f.lower().endswith(ext):
395 for name in covernames:
396 if f.lower() == (name + ext):
397 ret_dict['album-art'] = os.path.join(coverdir, f)
398 return ret_dict
315 else:399 else:
316 # perhaps it's in the cache folder400 print "awnmediaplayers: Unquoting error:\n%s (file)\ndoes not match\n%s (directory)" % (playinguri, coverdir)
317 if 'album' in result and 'artist' in result:401
318 cache_dir = ".cache/rhythmbox/covers"402 # Look for image in tags
319 ret_dict['album-art'] = '%s/%s - %s.jpg' % (cache_dir, result['artist'], result['album'])403 if art_icon_from_tag and 'mimetype' in result:
404 image_data = None
405 if result['mimetype'] == "application/x-id3":
406 try:
407 f = ID3(filename)
408 apicframes = f.getall("APIC")
409 if len(apicframes) >= 1:
410 frame = apicframes[0]
411 image_data = frame.data
412 except:
413 pass
414 elif result['mimetype'] == "audio/x-aac":
415 try:
416 f = mutagen.mp4.MP4(filename)
417 if "covr" in f.tags:
418 covertag = f.tags["covr"][0]
419 image_data = covertag
420 except:
421 pass
422 if image_data:
423 try:
424 loader = gtk.gdk.PixbufLoader()
425 loader.write(image_data)
426 loader.close()
427 loader.get_pixbuf().save(album_art_file, "png", {})
428 ret_dict['album-art'] = album_art_file
429 except:
430 pass
320431
321 return ret_dict432 return ret_dict
322433
@@ -627,6 +738,7 @@
627738
628739
629class Songbird(MPRISPlayer):740class Songbird(MPRISPlayer):
741 """Discontinued in 2010"""
630742
631 def __init__(self):743 def __init__(self):
632 MPRISPlayer.__init__(self, 'org.mpris.songbird')744 MPRISPlayer.__init__(self, 'org.mpris.songbird')
@@ -637,14 +749,27 @@
637 def __init__(self):749 def __init__(self):
638 MPRISPlayer.__init__(self, 'org.mpris.vlc')750 MPRISPlayer.__init__(self, 'org.mpris.vlc')
639751
752 def is_available(self):
753 return player_available('vlc')
754
755 def start(self):
756 return launch_player(['vlc', '--control', 'dbus'])
757
640758
641class Audacious(MPRISPlayer):759class Audacious(MPRISPlayer):
642760
643 def __init__(self):761 def __init__(self):
644 MPRISPlayer.__init__(self, 'org.mpris.audacious')762 MPRISPlayer.__init__(self, 'org.mpris.audacious')
645763
764 def is_available(self):
765 return player_available('audacious')
766
767 def start(self):
768 return launch_player('audacious')
769
646770
647class BMP(MPRISPlayer):771class BMP(MPRISPlayer):
772 """Beep Media Player, discontinued"""
648773
649 def __init__(self):774 def __init__(self):
650 MPRISPlayer.__init__(self, 'org.mpris.bmp')775 MPRISPlayer.__init__(self, 'org.mpris.bmp')
@@ -662,8 +787,15 @@
662 def __init__(self):787 def __init__(self):
663 MPRISPlayer.__init__(self, 'org.mpris.amarok')788 MPRISPlayer.__init__(self, 'org.mpris.amarok')
664789
790 def is_available(self):
791 return player_available('amarok')
792
793 def start(self):
794 return launch_player('amarok')
795
665796
666class Aeon(MPRISPlayer):797class Aeon(MPRISPlayer):
798 """Discontinued"""
667799
668 def __init__(self):800 def __init__(self):
669 MPRISPlayer.__init__(self, 'org.mpris.aeon')801 MPRISPlayer.__init__(self, 'org.mpris.aeon')
@@ -681,3 +813,46 @@
681813
682 def __init__(self):814 def __init__(self):
683 MPRISPlayer.__init__(self, 'org.freedesktop.MediaPlayer')815 MPRISPlayer.__init__(self, 'org.freedesktop.MediaPlayer')
816
817
818class Clementine(MPRISPlayer):
819
820 def __init__(self):
821 MPRISPlayer.__init__(self, 'org.mpris.clementine')
822
823 def is_available(self):
824 return player_available('clementine')
825
826 def start(self):
827 return launch_player('clementine')
828
829 def previous(self):
830 self.player.Prev()
831 # We have to emit song changed signal ourselves (Clementine 0.5)
832 self.song_changed_emitter()
833
834 def next(self):
835 self.player.Next()
836 # We have to emit song changed signal ourselves (Clementine 0.5)
837 self.song_changed_emitter()
838
839
840class Guayadeque(MPRISPlayer):
841
842 def __init__(self):
843 MPRISPlayer.__init__(self, 'org.mpris.guayadeque')
844
845 def dbus_driver(self):
846 bus_obj = dbus.SessionBus().get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
847 if bus_obj.NameHasOwner(self.dbus_base_name) == True:
848 self.session_bus = dbus.SessionBus()
849 self.proxy_obj = self.session_bus.get_object(self.dbus_base_name, '/Player')
850 self.player = dbus.Interface(self.proxy_obj, 'org.freedesktop.MediaPlayer')
851 self.player.connect_to_signal('TrackChange', self.song_changed_emitter, member_keyword='member')
852 self.player.connect_to_signal('StatusChange', self.playing_changed_emitter)
853
854 def is_available(self):
855 return player_available('guayadeque')
856
857 def start(self):
858 return launch_player('guayadeque')

Subscribers

People subscribed via source and target branches