clPlayerForWin.py

00001 #!/usr/bin/env python
00002 # -*- coding: utf-8 -*-
00003 
00004 # ----------------------------------------------------------------------------
00005 # pyjama - python jamendo audioplayer
00006 # Copyright (c) 2008 Daniel Nögel
00007 #
00008 # This program is free software: you can redistribute it and/or modify
00009 # it under the terms of the GNU General Public License as published by
00010 # the Free Software Foundation, either version 3 of the License, or
00011 # (at your option) any later version.
00012 #
00013 # This program is distributed in the hope that it will be useful,
00014 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 # GNU General Public License for more details.
00017 # You should have received a copy of the GNU General Public License
00018 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
00019 # ----------------------------------------------------------------------------
00020 
00021 ## @package clPlayerForWin
00022 # Pyjamas player module for windows
00023 # Right now only a copy of clGstreamer010
00024 # Will add basic hacks for playback later
00025 
00026 
00027 import pygtk
00028 pygtk.require('2.0')
00029 
00030 import sys, os
00031 
00032 import gobject
00033 
00034 import pygst
00035 pygst.require('0.10')
00036 import gst
00037 import gst.interfaces
00038 import gtk
00039 
00040 
00041 # Time formatting
00042 from time import strftime, gmtime, time
00043 
00044 import functions
00045 
00046 ## Converts a given time from seconds to a more readable string
00047 # @param seconds as int
00048 # @return string
00049 def sec2time(seconds):
00050     try:
00051         time_string = strftime("%M:%S", gmtime(seconds))
00052     except ValueError:
00053         return "00:00"
00054     return time_string
00055 
00056 ## Pyjama's Player class based on gstreamer0.10
00057 class Player:
00058     def __init__(self, parent):
00059         parent.Events.add_event("player-status")
00060         parent.Events.add_event('end_of_playlist')
00061 
00062         ## Boolean - is the player playing?
00063         self.playing = False
00064         ## The gstreamer player object
00065         self.player = gst.element_factory_make("playbin", "player")
00066 
00067 
00068         #
00069         # The following lines of code were meant to replace the playbin
00070         # for some reasons this didn't work for me
00071         #
00072 
00073         #        #gst-launch-0.10 souphttpsrc location=http://upload.wikimedia.org/wikipedia/commons/4/48/Frase_de_Neil_Armstrong.ogg ! decodebin ! audioconvert ! alsasink
00074 
00075         #        self.player = gst.Pipeline("pyjama_player")
00076 
00077         #        self.src = gst.element_factory_make("souphttpsrc", "src")
00078         #        self.player.add(self.src)
00079         #        self.decoder = gst.element_factory_make("decodebin", "decoder")
00080         #        self.player.add(self.decoder)
00081         #        self.converter = gst.element_factory_make("audioconvert", "convert")
00082         #        self.player.add(self.converter)
00083         #        self.sink = gst.element_factory_make("alsasink", "sink")
00084         #        self.player.add(self.sink)
00085 
00086 
00087         ## End of stream
00088         self.EOS = False
00089 
00090         bus = self.player.get_bus()
00091         bus.enable_sync_message_emission()
00092         bus.add_signal_watch()
00093         bus.connect('sync-message::element', self.on_sync_message)
00094         bus.connect('message', self.on_message)
00095 
00096 #        gobject.timeout_add(500, self.update)
00097 
00098 
00099         ## Reference to pyjama
00100         self.pyjama = parent
00101         ## Song being played
00102         self.cur_playing = None
00103         ## Song played before
00104         self.last_played = None
00105         ## Obsolete
00106         self.connection_tries = 0
00107 
00108         ## Dictionary - Number each artist was played
00109         self.artist_counter = {}
00110         ## Dictionary - Number each album was played
00111         self.album_counter = {}
00112         ## Dictionary - Number each track was played
00113         self.track_counter = {}
00114 
00115         ## List of Tracks
00116         self.playlist = []#{}
00117         ## List of ids of the tracks in the playlist
00118         self.playlist_track_uids = []
00119 
00120         ## Playback position in %
00121         self.percentage = None
00122         self.text = None
00123         ## Status of the player
00124         self.status = None 
00125         ## Current position of song in s
00126         self.cursec = None
00127         ## Duration of the song being played in s
00128         self.duration = None
00129         ## Seconds to go
00130         self.togo = None
00131         ## Name of the artist being played
00132         self.artist_name  = None
00133         ## Name of the track being played
00134         self.track_name = None
00135         ## Number of the track being played
00136         self.numalbum = None
00137 
00138     ###################################################################
00139     #
00140     # adds a track-object to the playlist
00141     # RETURNS: n/A
00142     #
00143     def add2playlist(self, track):
00144         count = len(self.playlist)
00145 #        self.playlist[count] = track
00146         self.playlist.append(track)
00147         self.playlist_track_uids.append(track['uid'])
00148 
00149     ###################################################################
00150     #
00151     # moves a track-object inside the playlist
00152     # RETURNS: n/A
00153     #
00154     def move_item(self, source, dest, before):
00155 #        print source, dest
00156 #      #  if source == dest or (dest == source-1 and not before) or (dest == source+1 and before): return
00157 #        if dest == 0 and before: dest = 1
00158 #        if dest == len(self.playlist) and not before: dest = len(self.playlist) 
00159         if dest > source:
00160             if before: 
00161                 before = -1
00162             else:
00163                 before = 0
00164         else:
00165             if before: 
00166                 before = 0
00167             else:
00168                 before = 1
00169         dest = dest + before
00170 
00171         old = self.playlist.pop(source)
00172         self.playlist.insert(dest, old)
00173 
00174         old = self.playlist_track_uids.pop(source)
00175         self.playlist_track_uids.insert(dest, old)
00176 
00177         if self.pyjama.debug_extreme:
00178             for track in self.playlist:
00179                 print track['id']
00180 
00181     ###################################################################
00182     #
00183     # remove a track from playlist
00184     # RETURNS: n/A
00185     #
00186     def remove(self, items):
00187         for item in items:
00188             track = self.playlist.pop(item)
00189             self.playlist_track_uids.remove(track['uid'])
00190 #        for item in items:
00191 #            del self.playlist[item]
00192 #            self.playlist_track_uids = []
00193 #        pl = {}
00194 #        counter = 0
00195 #        for x in self.playlist:
00196 #            pl[counter] = self.playlist[x]
00197 #            self.playlist_track_uids.append(self.playlist[x]['uid'])
00198 #            counter += 1
00199 #        self.playlist = pl        
00200 
00201     ###################################################################
00202     #
00203     # guess what: clear playlist 
00204     # RETURNS: n/A
00205     #
00206     def clearplaylist(self):
00207         self.cur_playing = None
00208         self.last_played = None
00209         self.playlist = []#{}
00210         self.playlist_track_uids = []
00211         
00212     ###################################################################
00213     #
00214     # set volume via mocp
00215     # RETURNS: n/A
00216     #
00217     def set_volume(self, volume):
00218         #print volume
00219         volume = volume / 100.0
00220         #print volume
00221         self.player.set_property('volume', volume)
00222 
00223 
00224     ###################################################################
00225     #
00226     # checks status of mocp
00227     # RETURNS: None if not playing, otherwise mocp status-infos
00228     #
00229     def check_status(self):
00230         if self.status == "paused":
00231             self.pyjama.Events.raise_event("player-status", "paused")
00232             return "paused"
00233         if self.status == "End":
00234             self.pyjama.Events.raise_event("player-status", "end")
00235             return None
00236         if self.cur_playing == None: 
00237             self.pyjama.Events.raise_event("player-status", "end")
00238             self.status =  "End"
00239         else:
00240             ret = self.get_state
00241 #            if self.pyjama.debug_extreme:
00242 #                print ret
00243 #            else:
00244             self.status = "Playing"
00245             position, duration = self.query_position() 
00246             self.cursec = position / (10**9)
00247             self.duration = int(self.cur_playing['duration'])
00248             self.percentage =  (1.0 / self.duration) * self.cursec
00249             if self.percentage > 1: self.percentage = 0
00250             self.togo = self.cursec - self.duration
00251             self.artist_name = self.cur_playing['artist_name']
00252             self.track_name = self.cur_playing['name']
00253             self.numalbum = self.cur_playing['numalbum']
00254             self.text = "%s:%s: %s / %s - %s" % (self.cur_playing['artist_name'] ,self.cur_playing['name'], sec2time(self.cursec), sec2time(self.duration), sec2time(self.togo*-1))
00255             self.pyjama.Events.raise_event("player-status", "playing", self.cursec, self.duration)
00256 
00257 #                print self.togo
00258 #                if self.togo <= 0:
00259 #                    uid = self.cur_playing['uid']
00260 #                    if uid in self.playlist_track_uids:
00261 #                        pos = self.playlist_track_uids.index(uid)
00262 #                        if len(self.playlist)> pos+1:
00263 #                            self.play(self.playlist[pos+1], pos+1)        
00264         
00265 
00266     def on_eos(self):
00267         self.EOS = False
00268         #print "END OF STREAM"
00269         #self.playing = False        
00270         self.next()
00271         #self.player.seek(0L)
00272         #self.play_toggled()
00273         
00274     def on_sync_message(self, bus, message):
00275         if message.structure is None:
00276             return
00277         if message.structure.get_name() == 'prepare-xwindow-id':
00278             self.videowidget.set_sink(message.src)
00279             message.src.set_property('force-aspect-ratio', True)
00280             
00281     def on_message(self, bus, message):
00282         t = message.type
00283         if t == gst.MESSAGE_ERROR:
00284             err, debug = message.parse_error()
00285             print "Error: %s" % err, debug
00286             if self.EOS:
00287                 self.on_eos()
00288             else:
00289                 self.next()
00290         elif t == gst.MESSAGE_EOS:
00291             self.EOS = True
00292             self.on_eos()
00293 
00294     def set_location(self, location):
00295         self.player.set_property('uri', location)
00296 
00297     def query_position(self):
00298         "Returns a (position, duration) tuple"
00299         try:
00300             position, format = self.player.query_position(gst.FORMAT_TIME)
00301         except:
00302             position = gst.CLOCK_TIME_NONE
00303 
00304         try:
00305             duration, format = self.player.query_duration(gst.FORMAT_TIME)
00306         except:
00307             duration = gst.CLOCK_TIME_NONE
00308 
00309         return (position, duration)
00310 
00311 
00312     ## Seek inside a track
00313     # @param self Object Pointer
00314     # @param location Position to seek to in ns
00315     # @return 
00316     # - True if seeking was succesfull
00317     # - False if seeking failed
00318     def seek(self, location):   
00319         gst.debug("seeking to %r" % location)
00320         event = gst.event_new_seek(1.0, gst.FORMAT_TIME,
00321             gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE,
00322             gst.SEEK_TYPE_SET, location,
00323             gst.SEEK_TYPE_NONE, 0)
00324 
00325         res = self.player.send_event(event)
00326         if res:
00327             gst.info("setting new stream time to 0")
00328             self.player.set_new_stream_time(0L)
00329             return True
00330         else:
00331             gst.error("seek to %r failed" % location)
00332             return False
00333 
00334 
00335     def pause(self):
00336         gst.info("pausing player")
00337         self.status = "paused"
00338         self.player.set_state(gst.STATE_PAUSED)
00339         self.playing = False
00340 
00341     ###################################################################
00342     #
00343     # ???
00344     # RETURNS: n/A
00345     #
00346     def increment_counter(self, array, key):
00347         if array.has_key(key):
00348             array[key] += 1
00349         else:
00350             array[key] = 1
00351 #        print array[key]
00352 
00353     ###################################################################
00354     #
00355     # play a track
00356     # RETURNS: n/A
00357     #            
00358     def play(self, track=None, activate_item=-1):
00359 #        self.player.set_state(gst.STATE_NULL)
00360 #        gst.info("stopped player")
00361         if self.status == "paused" and track == None:
00362             track = self.cur_playing
00363         self.increment_counter(self.artist_counter, track['artist_id'])
00364         self.increment_counter(self.album_counter, track['album_id'])
00365         self.increment_counter(self.track_counter, track['id'])
00366         if track['stream'] == "query":
00367             track['stream'] = self.pyjama.jamendo.stream(track['id'])
00368         if track['duration'] == 0:
00369             #GET FROM JAMENDO
00370             track['duration'] = 60*59
00371                 
00372         self.last_played = self.cur_playing
00373         self.cur_playing = track
00374         
00375         if self.status != "paused":
00376             self.stop()
00377 
00378         try:
00379             self.set_location(track['stream'])
00380         except:
00381             print "error"
00382         gst.info("playing player")
00383         self.player.set_state(gst.STATE_PLAYING)
00384         self.playing = True
00385         self.status = "Playing"
00386 
00387         track['pos'] = activate_item
00388 
00389         ev = self.pyjama.Events
00390         ev.raise_event('nowplaying', track)
00391 #        ev.raise_event('nowplaying', artist = track['artist_name'], album = track['album_name'], track = track['name'], icon = img)
00392         #self.pyjama.notification(_("Now playing"), "<b><i>%s</i></b>:\n%s" % (track['artist_name'], track['name']), icon = img, size =)
00393         
00394         if activate_item > -1:
00395             self.pyjama.setplaylist(activate_item)
00396 
00397 
00398     ###################################################################
00399     #
00400     # stop playing
00401     # RETURNS: n/A
00402     #        
00403     def stop(self):
00404         self.percentage = None
00405         self.text = None
00406         self.cursec = None
00407         self.duration = None
00408         self.togo = None
00409         self.artist_name  = None
00410         self.track_name = None
00411         self.numalbum = None
00412         self.status = "End"
00413         self.player.set_state(gst.STATE_NULL)
00414         gst.info("stopped player")
00415                 
00416 
00417 
00418     ###################################################################
00419     #
00420     # play next track in playlist
00421     # RETURNS: n/A
00422     #
00423     def next(self):
00424         if self.cur_playing is not None:
00425             uid = self.cur_playing['uid']
00426             if uid in self.playlist_track_uids:
00427                 pos = self.playlist_track_uids.index(uid)
00428                 if len(self.playlist)> pos+1:
00429                     self.play(self.playlist[pos+1], pos+1)
00430                 else:
00431                     self.status = "End"
00432                     self.pyjama.Events.raise_event('end_of_playlist')
00433                 
00434     ###################################################################
00435     #
00436     # play previous track in playlist
00437     # RETURNS: n/A
00438     #                
00439     def prev(self):
00440         if self.cur_playing is not None:
00441             uid = self.cur_playing['uid']
00442             if uid in self.playlist_track_uids:
00443                 pos = self.playlist_track_uids.index(uid)
00444                 if pos-1 >= 0:
00445                     self.play(self.playlist[pos-1], pos-1)                
00446 
00447     def get_state(self, timeout=1):
00448         return self.player.get_state(timeout=timeout)
00449 
00450     def is_playing(self):
00451         return self.playing

Generated on Thu Jun 4 19:08:24 2009 for Pyjama by  doxygen 1.5.8