root/xchat/mp3.py

Revision 41, 23.7 kB (checked in by jmoiron, 1 year ago)
  • fix juk support w/o pydcop
  • fix dcop support sniffing
Line 
1 #!/usr/bin/env python
2 #
3 # this module uses code from Michael Hudson's xmms-py modules
4 # this code is available in its original form here:
5 #   http://www.python.net/crew/mwh/hacks/xmms-py.html
6 # the original code had this notice on it:
7 #
8 # Released by Michael Hudson on 2000-07-01, and again on 2001-04-26
9 # public domain; no warranty, no restrictions
10 #
11 # most of the support for xmms, beep, and audacious comes from
12 # various pieces of Hudson's modules
13 #
14 # licensed under the GNU GPL v2
15 # a copy of this license can be found:
16 #   http://www.gnu.org/copyleft/gpl.html
17 #
18 # Future Plan:
19 #   - allow for customizeable announce string (like dcclogger)
20 #   - more mp3 players (any requests?)
21 #
22 # 11/26/07 - Fixed a bug w/ using juk/amarok w/o pydcop
23 #
24
25
26 import xchat
27 import sys, struct
28 import socket, os, pwd
29 from subprocess import *
30
31 pcop, pydcop, bus = False, False, False
32 try:
33     import pcop, pydcop
34 except: pass
35 try:
36     import dbus
37     bus = dbus.SessionBus()
38 except: pass
39
40 __module_name__ = "pymp3"
41 __module_version__ = "0.5"
42 __module_description__ = "mp3 announce/utils"
43
44 __debugging__ = False
45
46 if __debugging__:
47     import traceback
48
49 def print_debug(string):
50     global __debugging__
51     if __debugging__: print "\00302" + str(string) + "\003"
52
53 def print_info(string):
54     print "\00303" + str(string) + "\003"
55
56 players = {
57     'audacious' : 'audacious',
58     'beep'      : 'beep-media-player',
59     'xmms'      : 'xmms',
60     'banshee'   : 'banshee.exe',
61     'juk'       : 'juk',
62     'amarok'    : 'amarokapp',
63     'rhythmbox' : 'rhythmbox',
64 }
65
66 # find out which player is running
67 def which():
68     ps = Popen(['ps', 'aux'], stdout=PIPE)
69     output = ps.stdout.readlines()
70     for line in output:
71         for player,findstr in players.items():
72             if line.rfind(findstr) > -1:
73                 return player
74     return
75
76 #FIXME: This code isn't that great; it should probably not rely on 'split' since
77 # quoted won't work properly.  Think of a way to fix this (maybe resort to shell=True)
78 def command(runstr):
79     return Popen(runstr.split(), stdout=PIPE).communicate()[0]
80
81 # these players use xmms style command socket
82 SOCKET_PLAYERS = ['audacious', 'beep', 'xmms']
83
84 class SocketCommand:
85     CMD_PLAY = 2                #
86     CMD_PAUSE = 3               #
87     CMD_STOP = 4                #
88     CMD_GET_PLAYLIST_POS = 7    #
89     # TODO: make socket_next and socket_prev use this
90     # instead of using next/prev repeatedly
91     #CMD_SET_PLAYLIST_POS = 8    #
92     CMD_GET_PLAYLIST_LENGTH = 9 #
93     CMD_GET_OUTPUT_TIME = 11    #
94     CMD_GET_PLAYLIST_FILE = 17  #
95     CMD_GET_PLAYLIST_TITLE = 18 #
96     CMD_GET_PLAYLIST_TIME = 19  #
97     CMD_GET_INFO = 20           #
98     CMD_EJECT = 28              #
99     CMD_PLAYLIST_PREV = 29      #
100     CMD_PLAYLIST_NEXT = 30      #
101     CMD_TOGGLE_REPEAT = 33      #
102     CMD_TOGGLE_SHUFFLE = 34     #
103
104 class ClientPacketHeader:
105     def __init__(self):
106         self.version,self.cmd,self.length = 0,0,0
107     def __repr__(self):
108         return "<< %s : version: %s cmd: %s length: %s >>"\
109             %(self.__class__.__name__,self.version,self.cmd,self.length)
110     def encode(self):
111         return struct.pack("hhl",self.version,self.cmd,self.length)
112
113 """
114 I've tried to make the following class a facsimily of a "persistent connection",
115 but my attempts have led to the following error with xmms:
116     ** WARNING **: ctrl_write_packet(): Failed to send data: Broken pipe
117 Even manually closing, deleting, and then re-initializing the socket did not avoid
118 this warning.  It seems that only letting the garbage collector snag old Connection
119 objects makes xmms happy.
120
121 There is one aspect here missing from Hudson's original library: sending a custom
122 send format with the 'args' option.  I wasn't using this feature in any requests,
123 as all of my provided formats were 'l' anyway.
124 """
125 class XmmsConnection:
126     def __init__(self,session=0):
127         self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
128         self.sock.connect("/tmp/xmms_%s.%d"%(pwd.getpwuid(os.geteuid())[0],session))
129
130     def read_header(self):
131         head = ClientPacketHeader()
132         head.version, head.cmd, head.length = struct.unpack("hhl",self.sock.recv(8))
133         return head
134
135     def send(self, cmd, args=''):
136         data = ""
137         if isinstance(args, int):
138             data = struct.pack('l', args)
139         packet = struct.pack("hhl", 1, cmd, len(data)) + data
140         self.sock.send(packet)
141
142     def get_reply(self, format=''):
143         header = self.read_header()
144         if format: reply = struct.unpack(format, self.sock.recv(header.length))
145         else: reply = self.sock.recv(header.length)
146         return reply
147
148 class AudaciousConnection(XmmsConnection):
149     def __init__(self,session=0):
150         self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
151         self.sock.connect("/tmp/audacious_%s.%d"%(pwd.getpwuid(os.geteuid())[0],session))
152
153 class MediaPlayer:
154     """A superclass that implements some book-keeping and some convenience functions
155     for media player objects.  These objects print out as an announce string, and
156     get (and cache) info from the player in a clean, consistent "getitem" API."""
157     def __init__(self, name):
158         self.name = name
159
160     def _nyi(self, name):
161         print_debug("%s not yet implemented for `%s`." % name, self.name)
162
163     def _empty_dict(self):
164         """Return an empty info dictionary with all of the keys set."""
165         keys = ['player', 'playlist_position', 'playlist_length', 'file',
166                 'display_title', 'elapsed', 'length', 'bitrate', 'frequency',
167                 'title', 'artist', 'album', 'track']
168         d = {}
169         for key in keys: d[key] = ''
170         d['player'] = self.name
171         return d
172
173     def human_bitrate(self, bps):
174         """Takes bits per second and returns a string with appropriate units."""
175         units = ['bps', 'kbps', 'Mbps']
176         # order of magnitude
177         # if we get a weird number, assume kbps = kiloBYTESpersec
178         # if we get a number ending in '00', assume it's 1000's of bits (correct)
179         if str(bps).endswith("00"):
180             reduce_factor = 1000
181         else:
182             reduce_factor = 1024.0
183         oom = 0
184         while bps /(reduce_factor**(oom+1)) >= 1:
185             oom += 1
186         return '%0.1f %s' % (bps/reduce_factor**oom, units[oom])
187
188     def s_to_ms(self, s):
189         """Converts seconds to minutes:seconds: mm:ss."""
190         s = int(s)
191         sec = s % 60
192         min = s / 60
193         return '%2d:%02d' % (min, sec)
194
195     def us_to_ms(self, us):
196         """Converts miliseconds to minutes:seconds:  mm:ss."""
197         us = int(us)
198         return self.s_to_ms(us/1000)
199
200     def play(self): self._nyi('Play')
201     def stop(self): self._nyi('Stop')
202     def pause(self): self._nyi('Pause')
203     def next(self): self._nyi('Next')
204     def prev(self): self._nyi('Prev')
205     def eject(self): self._nyi('eject')
206     def open(self): self._nyi('open')
207     def shuffle(self): self._nyi('shuffle')
208     def repeat(self): self._nyi('repeat')
209
210     def get_info(self): return self._empty_dict()
211
212     def __str__(self):
213         """FIXME: This implements the old announce strings.  It's probably easier
214         to move this to the subclasses, but for now this is fine."""
215         info = self.get_info()
216         if self.name in SOCKET_PLAYERS:
217             return '%s ~ [%s] of [%s] ~ %s ~ %sHz' % (info['display_title'], \
218                 info['elapsed'], info['length'], info['bitrate'], info['frequency'])
219
220         elif self.name in ['juk', 'amarok']:
221             return '%s - [%s] - %s ~ [%s] of [%s] ~ %s' % (info['artist'], \
222                 info['album'], info['title'], info['elapsed'], info['length'], \
223                 info['bitrate'])
224
225         elif self.name in ['banshee', 'rhythmbox']:
226             return '%s - [%s] - %s ~ [%s] of [%s]' % (info['artist'], info['album'], \
227                 info['title'], info['elapsed'], info['length'])
228
229     def __repr__(self):
230         return '<MediaPlayer %s ...>' % (self.name)
231
232 class Xmms(MediaPlayer):
233     def __init__(self, name='xmms'):
234         MediaPlayer.__init__(self, name)
235         self._ifcache = {}
236
237     def _makeConnection(self):
238         if self.name in ['beep', 'xmms']: return XmmsConnection()
239         elif self.name == 'audacious': return AudaciousConnection()
240         return False
241
242     def _cmd(self, command, args='', reply_format=''):
243         connection = self._makeConnection()
244         connection.send(command, args=args)
245         return connection.get_reply(format=reply_format)
246
247     def play(self):     self._cmd(SocketCommand.CMD_PLAY)
248     def stop(self):     self._cmd(SocketCommand.CMD_STOP)
249     def pause(self):    self._cmd(SocketCommand.CMD_PAUSE)
250     def next(self):     self._cmd(SocketCommand.CMD_PLAYLIST_NEXT)
251     def prev(self):     self._cmd(SocketCommand.CMD_PLAYLIST_PREV)
252     def eject(self):    self._cmd(SocketCommand.CMD_EJECT)
253     def open(self):     self._cmd(SocketCommand.CMD_EJECT)
254     def shuffle(self):  self._cmd(SocketCommand.CMD_TOGGLE_SHUFFLE)
255     def repeat(self):   self._cmd(SocketCommand.CMD_TOGGLE_REPEAT)
256
257     def get_info(self):
258         d = self._empty_dict()
259         d['playlist_position'] = self._cmd(SocketCommand.CMD_GET_PLAYLIST_POS, reply_format='l')[0]
260         position = d['playlist_position']
261         d['playlist_length'] = self._cmd(SocketCommand.CMD_GET_PLAYLIST_LENGTH, reply_format='l')[0]
262         d['file']  = self._cmd(SocketCommand.CMD_GET_PLAYLIST_FILE, args=position)[:-1]
263         d['display_title'] = self._cmd(SocketCommand.CMD_GET_PLAYLIST_TITLE, args=position)[:-1]
264         info = self._cmd(SocketCommand.CMD_GET_INFO, reply_format='lll')
265         utime_elapsed = self._cmd(SocketCommand.CMD_GET_OUTPUT_TIME, reply_format='l')[0]
266         utime_length  = self._cmd(SocketCommand.CMD_GET_PLAYLIST_TIME, args=position, reply_format='l')[0]
267         d['elapsed'] = self.us_to_ms(utime_elapsed)
268         d['length'] = self.us_to_ms(utime_length)
269         d['bitrate'] = self.human_bitrate(info[0])
270         d['frequency'] = info[1]
271         return d
272
273 BEEP_FIRST_RUN = True
274 BEEP_MESSAGE = """beep-media-player has a bug with its control socket and returns
275 bogus information for bitrate, frequency, and number of channels.  Consider the
276 'audacious' media player, or BMPx, as beep-media-player is no longer in
277 development.""".replace("\n", ' ')
278
279 class Beep(Xmms):
280     def __init__(self):
281         global BEEP_FIRST_RUN, BEEP_MESSAGE
282         if BEEP_FIRST_RUN:
283             print_info(BEEP_MESSAGE)
284             BEEP_FIRST_RUN = False
285         Xmms.__init__(self, 'beep')
286
287 class Audacious(Xmms):
288     def __init__(self):
289         Xmms.__init__(self, 'audacious')
290
291 BANSHEE_FIRST_RUN = True
292 BANSHEE_MESSAGE = """Although banshee is supported without them, it is recommended
293 that you install the python-dbus bindings for increased speed.""".replace("\n", " ")
294
295 class Banshee(MediaPlayer):
296     def __init__(self):
297         global BANSHEE_FIRST_RUN, BANSHEE_MESSAGE
298         if BANSHEE_FIRST_RUN and not bus:
299             print_info(BANSHEE_MESSAGE)
300             BANSHEE_FIRST_RUN = False
301         MediaPlayer.__init__(self, 'banshee')
302         self._ifcache = {}
303         interface = ['play', 'stop', 'pause', 'next', 'prev', 'eject', 'open', 'get_info']
304         if bus:
305             self.d_obj = bus.get_object("org.gnome.Banshee", "/org/gnome/Banshee/Player")
306             self.banshee = dbus.Interface(self.d_obj, "org.gnome.Banshee.Core")
307             for func in interface:
308                 setattr(self, func, getattr(self, '%s_dbus' % func))
309         else:
310             for func in interface:
311                 setattr(self, func, getattr(self, '%s_nodbus' % func))
312
313     def play_dbus(self): self.banshee.Play()
314     def stop_dbus(self): self.banshee.Pause()
315     def pause_dbus(self): self.banshee.TogglePlaying()
316     def next_dbus(self): self.banshee.Next()
317     def prev_dbus(self): self.banshee.Previous()
318     def eject_dbus(self): self.banshee.ShowWindow()
319     def open_dbus(self): self.banshee.ShowWindow()
320
321     def get_info_dbus(self):
322         d = self._empty_dict()
323         d['length'] = self.s_to_ms(self.banshee.GetPlayingDuration())
324         d['elapsed'] = self.s_to_ms(self.banshee.GetPlayingPosition())
325         d['artist'] = unicode(self.banshee.GetPlayingArtist()).encode('UTF-8')
326         d['title'] = unicode(self.banshee.GetPlayingTitle()).encode('UTF-8')
327         d['album'] = unicode(self.banshee.GetPlayingAlbum()).encode('UTF-8')
328         return d
329
330     def play_nodbus(self):     command('banshee --play')
331     def stop_nodbus(self):     command('banshee --pause')
332     def pause_nodbus(self):    command('banshee --toggle-playing')
333     def next_nodbus(self):     command('banshee --next')
334     def prev_nodbus(self):     command('banshee --previous')
335     def eject_nodbus(self):    command('banshee --show')
336     def open_nodbus(self):     command('banshee --show')
337     # shuffle & repeat not yet implemented
338
339     def get_info_nodbus(self):
340         d = self._empty_dict()
341         info = command(' '.join(['banshee', '--hide-field', '--query-title',
342                                  '--query-artist', '--query-position', '--query-album',
343                                  '--query-duration'])).strip()
344         # duration, artist, album, title, position
345         # banshee reports things in seconds
346         info = info.split('\n')
347         d['length'] = self.s_to_ms(info[0])
348         d['artist'] = info[1]
349         d['album']  = info[2]
350         d['title']  = info[3]
351         d['elapsed'] = self.s_to_ms(info[4])
352         return d
353
354 class Rhythmbox(MediaPlayer):
355     """MediaPlayer class for Rhythmbox, a Gtk/Gnome media player.  It's possible
356     to implement this without using python-dbus by wrapping around 'dbus-send',
357     but frankly I didn't feel like it."""
358     def __init__(self):
359         if not bus:
360             raise Exception('Rhythmbox is not supported w/o python-dbus bindings.')
361         MediaPlayer.__init__(self, 'rhythmbox')
362         player_obj = bus.get_object("org.gnome.Rhythmbox", "/org/gnome/Rhythmbox/Player")
363         shell_obj  = bus.get_object("org.gnome.Rhythmbox", "/org/gnome/Rhythmbox/Shell")
364         self.player = dbus.Interface(player_obj, "org.gnome.Rhythmbox.Player")
365         self.shell  = dbus.Interface(shell_obj,  "org.gnome.Rhythmbox.Shell")
366
367     def play(self):
368         if not bool(self.player.getPlaying()): self.player.playPause()
369     def stop(self):
370         if bool(self.player.getPlaying()): self.player.playPause()
371     def pause(self): self.player.playPause()
372     def next(self): self.player.next()
373     def prev(self): self.player.previous()
374     def eject(self): print_info("There isn't an easy way to do this in rhythmbox right now.")
375     def open(self): print_info("There isn't an easy way to do this in rhythmbox right now.")
376
377     def get_info(self):
378         d = self._empty_dict()
379         uri = unicode(self.player.getPlayingUri())
380         properties = dict([(unicode(key), val) for key,val in dict(self.shell.getSongProperties(uri)).items()])
381         d['length'] = self.s_to_ms(int(properties.get('duration', 0)))
382         d['elapsed'] = self.s_to_ms(int(self.player.getElapsed()))
383         d['artist'] = unicode(properties.get('artist', '')).encode('UTF-8')
384         d['album']  = unicode(properties.get('album', '')).encode('UTF-8')
385         d['title']  = unicode(properties.get('title', '')).encode('UTF-8')
386         # Banshee reports a 'bitrate', but as far as i can tell it's always 0
387         return d
388
389 JUK_FIRST_RUN = True
390 DCOP_MESSAGE = """Although juk is supported without them, it is recommended that
391 you install the python-dcop bindings for increased speed.""".replace("\n", ' ')
392
393 class Juk(MediaPlayer):
394     """MediaPlayer class for Juk, a Qt/KDE media player.  This implementation is
395     a bit messy because it resolves whether or not to use DCOP statically;  after
396     importing, the comparissons are made and the appropriate functions are used."""
397     def __init__(self):
398         global JUK_FIRST_RUN, DCOP_MESSAGE, pydcop
399         if JUK_FIRST_RUN and not pydcop:
400             print_info(DCOP_MESSAGE)
401         JUK_FIRST_RUN = False
402         MediaPlayer.__init__(self, 'juk')
403         self._ifcache = {}
404         # these functions are to be selected from _%s_dcop and #s_nodcop
405         self._functions = ['eject', 'open']
406         # these functions are created below; the keys are function names, the values
407         # are juk PLayer dcop values
408         self._func_map = {'play':'play', 'stop':'stop', 'pause':'playPause', 'next':'forward', 'prev':'back'}
409         if pydcop:
410             # if we have pydcop, create 'juk' and set some functions
411             self.juk = pydcop.anyAppCalled("juk")
412             self.get_property = (lambda x: self.juk.Player.trackProperty(x))
413             self.get_juk = (lambda func: getattr(self.juk.Player, func)())
414             for func in self._functions:
415                 setattr(self, func, getattr(self, '_%s_dcop' % func))
416         else:
417             # with no dcop, set equivalent functions to above using 'command' interface
418             self.get_property = (lambda x: command('dcop juk Player trackProperty %s' % (x)).strip())
419             self.get_juk = (lambda func: command('dcop juk Player %s' % func))
420             for func in self._functions:
421                 setattr(self, func, getattr(self, '_%s_nodcop' % func))
422         # this forloop sets all of the keys in 'func_map' to lambdas that call
423         # whatever 'get_juk' was created by the conditional above
424         for funcname, juk_property in self._func_map.items():
425             setattr(self, funcname, (lambda prop=juk_property: self.get_juk(prop)))
426
427     def _eject_dcop(self):
428         pcop.dcop_call("juk", "juk-mainwindow#1", "restore", ())
429         pcop.dcop_call("juk", "juk-mainwindow#1", "raise", ())
430     def _open_dcop(self): self._eject_dcop()
431
432     def _eject_nodcop(self):
433         command('dcop juk juk-mainwindow#1 restore')
434         command('dcop juk juk-mainwindow#1 raise')
435     def _open_nodcop(self): self._eject_nodcop()
436
437     def get_info(self):
438         d = self._empty_dict()
439         elapsed = self.get_juk('currentTime')
440         d['elapsed'] = self.s_to_ms(elapsed)
441         d['title'] = self.get_property('Title')
442         d['artist'] = self.get_property('Artist')
443         d['album'] = self.get_property('Album')
444         d['length'] = self.s_to_ms(self.get_property('Seconds'))
445         d['bitrate'] = '%s Kbps' % self.get_property('Bitrate')
446         return d
447
448 AMAROK_FIRST_RUN = True
449 AMAROK_DCOP_MESSAGE = """Although amarok is supported without them, it is recommended that
450 you install the python-dcop bindings for increased speed.""".replace("\n", ' ')
451
452 class Amarok(MediaPlayer):
453     """MediaPlayer class for Amarok, a Qt/KDE media player.  This implementation is
454     a bit messy because it resolves whether or not to use DCOP statically;  after
455     importing, the comparissons are made and the appropriate functions are used."""
456     def __init__(self):
457         global AMAROK_FIRST_RUN, AMAROK_DCOP_MESSAGE, pydcop
458         if AMAROK_FIRST_RUN and not pydcop:
459             print_info(AMAROK_DCOP_MESSAGE)
460         AMAROK_FIRST_RUN = False
461         MediaPlayer.__init__(self, 'amarok')
462         self._ifcache = {}
463         """If the pydcop is available, then we create a 'self.get_property' function
464         that uses pydcop; if it isn't available, we create a function that works the same
465         but using our 'command' interface.  Then, using the 'self.get_property', we bind
466         'self.play', 'self.stop', etc. to the object's namespace."""
467         self._functions = ['play', 'stop', 'pause']
468         if pydcop:
469             self.amarok = pydcop.anyAppCalled("amarok")
470             self.get_property = (lambda x: getattr(self.amarok.player, x)())
471             self.get_playlist = (lambda x: getattr(self.amarok.playlist, x)())
472             self.set_playlist = (lambda x: self.amarok.playlist.playByIndex(x))
473         else:
474             self.get_property = (lambda x: command('dcop amarok player %s' % x).strip())
475             self.get_playlist = (lambda x: command('dcop amarok playlist %s' % x).strip())
476             self.set_playlist = (lambda x: command('dcop amarok playlist playByIndex %s' % x))
477         for func in self._functions:
478             setattr(self, func, (lambda func=func: self.get_property(func)))
479
480     def open(self): print_info("There isn't an easy way to do this with amarok right now.")
481     def eject(self): print_info("There isn't an easy way to do this with amarok right now.")
482
483     def prev_n(self, n):
484         """Go backwards 'n' times in the playlist"""
485         position = self.get_playlist('getActiveIndex')
486         new_position = position - n
487         if new_position < 0: new_position = 0
488         self.set_playlist(new_position)
489
490     def next_n(self, n):
491         """Go forwards 'n' times in the playlist"""
492         position = self.get_playlist('getActiveIndex')
493         playlist_length = self.get_playlist('getTotalTrackCount')
494         new_position = position + n
495         if new_position >= playlist_length:
496             new_position = playlist_length - 1
497         self.set_playlist(new_position)
498
499     def get_info(self):
500         d = self._empty_dict()
501         # this comes back in 'm:ss'
502         d['elapsed'] = self.get_property('currentTime')
503         d['title'] = self.get_property('title')
504         d['artist'] = self.get_property('artist')
505         d['album'] = self.get_property('album')
506         d['length'] = self.get_property('totalTime')
507         d['bitrate'] = '%s Kbps' % self.get_property('bitrate')
508         return d
509
510 def current_player():
511     player = which()
512     print_debug("detected %s is running" % player)
513     if not player:
514         raise Exception("Currently not running a supported media player.")
515     player_obj = eval("%s()" % player.capitalize())
516     return player_obj
517
518 def help(args):
519     print "\nCommands:"
520     print "   \002/mp3\002          : announce the currently playing mp3"
521     print "   \002/mp3\002     \00303stop\003 : stop playing"
522     print "   \002/mp3\002     \00303play\003 : start playing"
523     print "   \002/mp3\002    \00303pause\003 : pause playback"
524     print "   \002/mp3\002 \00303next [#]\003 : skip to next (# of) track(s)"
525     print "   \002/mp3\002 \00303prev [#]\003 : skip to prev (# of) track(s)"
526     print "   \002/mp3\002     \00303open\003 : open files"
527     print ""
528
529 def usage():
530     print "Usage: \002/mp3\002 [cmd]\n\002/mp3\002 \037help\037 for commands."
531
532 def announce():
533     player = current_player()
534     xchat.command('me is listening to: %s' % (player))
535
536 def stop(*args):
537     player = current_player()
538     player.stop()
539
540 def play(*args):
541     player = current_player()
542     player.play()
543
544 def pause(*args):
545     player = current_player()
546     player.pause()
547
548 def open(*args):
549     player = current_player()
550     player.open()
551
552 def eject(*args):
553     player = current_player()
554     player.eject()
555
556 def _make_num(numstr):
557     try: return int(numstr)
558     except:
559         print_error('"%s" must be a number.' % numstr)
560         return False
561
562 def next(argv):
563     num = 1
564     if len(argv) == 3:
565         num = _make_num(argv[2])
566         if not num: return
567     player = current_player()
568     if player.name in ['amarok']:
569         player.next_n(num)
570     else:
571         for i in range(num):
572             player.next()
573
574 def prev(argv):
575     num = 1
576     if len(argv) == 3:
577         num = _make_num(argv[2])
578         if not num: return
579     player = current_player()
580     if player.name in ['amarok']:
581         player.prev_n(num)
582     else:
583         for i in range(num):
584             player.prev()
585
586 def dispatch(argv, arg_to_eol, c):
587     if len(argv) == 1:
588         try: announce()
589         except Exception, ex:
590             if __debugging__: traceback.print_exc(sys.stdout)
591             if len(getattr(ex, 'args', [])): print_info(ex.args[0])
592             else: usage()
593         return xchat.EAT_XCHAT
594     try:
595         {
596         "help"  : help,
597         "stop"  : stop,
598         "play"  : play,
599         "pause" : pause,
600         "next"  : next,
601         "prev"  : prev,
602         "eject" : eject,
603         "open"  : open,
604     }[argv[1]](argv)
605     except Exception, ex:
606         if __debugging__: traceback.print_exc(sys.stdout)
607         if len(getattr(ex, 'args', [])): print_info(ex.args[0])
608         else: usage()
609     return xchat.EAT_XCHAT
610
611 __unhook__ = xchat.hook_command("mp3", dispatch, help="/mp3 help for commands.")
Note: See TracBrowser for help on using the browser.