Debian packaging for 0.99.3 on Fremantle
[panucci.git] / src / panucci / backends / base.py
blob022fbc65465248377058c24bf40773106ba596ff
1 # -*- coding: utf-8 -*-
3 # This file is part of Panucci.
4 # Copyright (c) 2008-2011 The Panucci Project
6 # Panucci is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # Panucci is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with Panucci. If not, see <http://www.gnu.org/licenses/>.
19 from __future__ import absolute_import
21 import logging
23 import panucci
24 from panucci import services
25 from panucci import util
27 class BasePlayer(services.ObservableService):
28 """ The player base class, this can't be used directly because most of
29 the important functions need to be filled in by subclasses.
30 """
32 """
33 Signals
34 playing : ( )
35 Issued when the player starts playing
36 paused : ( )
37 Issued when the player pauses
38 stopped : ( )
39 Issued when the player stops
40 eof : ( )
41 Issued at the end of a file
42 error : ( error_code, error_string )
43 Issued when an error occurs. "error_code" is a unique code for a
44 specific error, "error_string" is a human-readable string that
45 describes the error.
46 """
47 signals = [ 'playing', 'paused', 'stopped', 'eof', 'error' ]
48 STATE_PLAYING, STATE_PAUSED, STATE_STOPPED, STATE_NULL = range(4)
50 def __init__(self):
51 self.__log = logging.getLogger('panucci.backends.BasePlayer')
52 services.ObservableService.__init__(self, self.signals, self.__log)
54 # Cached copies of position and duration
55 self.__position, self.__duration = 0, 0
56 self.seeking = False # Are we seeking?
57 self.current_uri = None
59 #############################################
60 # Functions to be implemented by subclasses
62 def get_position_duration(self):
63 """ A cached version of _get_position_duration """
64 if self.playing or self.paused:
65 self.__position, self.__duration = self._get_position_duration()
67 return self.__position, self.__duration
69 def get_state(self):
70 """ Get the current state of the player.
72 Returns: One of the following flags: player.PLAYING,
73 player.PAUSED,
74 player.STOPPED,
75 or player.NULL
76 """
77 return self._get_state()
79 def load_media(self, uri):
80 """ Loads a uri into the player
82 Params: uri - A full path to a media file.
83 Eg. file:///mnt/music/some-file.ogg
84 Returns: Nothing
85 """
86 self.current_uri = util.file_to_url(uri)
87 return self._load_media(self.current_uri)
89 def pause(self):
90 """ Pauses playback.
92 Returns: The current position in nanoseconds.
93 Signals: Must emit the "paused" signal.
94 """
95 return self._pause()
97 def play(self, position=None):
98 """ Starts playing the playlist's current track.
100 Params: position is the absolute position to seek to before
101 playing. It is better to use this instead of relying on
102 play(); seek(...) because the player might not be ready to
103 seek at that point.
104 Returns: False if the current track cannot be played.
105 True if all is well.
106 Signals: Must emit the "playing" signal
108 return self._play()
110 def stop(self, player=False):
111 """ Stops playback.
113 Params: player, if true delete player
114 Returns: Nothing
115 Signals: Must emit the "stopped" signal.
117 return self._stop(player)
119 def seek(self, position):
120 """ Seek to an absolute position in the current file.
122 Params: position is the position to seek to in nanoseconds.
123 Returns: True if the seek was successfull.
125 return self._seek(position)
127 def get_volume_level(self):
128 return self._get_volume_level()
130 def set_volume_level(self, percent):
131 self._set_volume_level(percent)
133 #############################################
134 # Generic Functions
136 def do_seek(self, from_beginning=None, from_current=None, percent=None):
137 """ A very flexible function to seek in the current file
139 Params: Requires ONE of the following keyword arguments
140 - from_beginning=n: seek n nanoseconds from the start of
141 the file
142 - from_current=n: seek n nanoseconds from the current
143 position
144 - percent=n: seek n percent from the beginning of the file
146 Returns: False if the seek was NOT possible
147 ( position, duration ) if the seek was possible
149 error = False
150 position, duration = self.get_position_duration()
152 # if position and duration are 0 then player_get_position caught an
153 # exception. Therefore self.__player isn't ready to be seeking.
154 if not duration or self.get_state() == self.STATE_NULL:
155 error = True
156 else:
157 if from_beginning is not None:
158 assert from_beginning >= 0
159 position = min( from_beginning, duration )
160 elif from_current is not None:
161 position = max( 0, min( position+from_current, duration ))
162 elif percent is not None:
163 assert 0 <= percent <= 1
164 position = int(duration*percent)
165 else:
166 self.__log.warning('No seek parameters specified.')
167 error = True
169 if not error:
170 self.__log.debug('do_seek: Seeking to: %d', position)
171 self.seek(position)
172 return position, duration
173 else:
174 self.__log.debug('do_seek: Could not seek.')
176 return False
178 def play_pause_toggle(self):
179 self.pause() if self.playing else self.play()
181 @property
182 def playing(self):
183 """ Is the player playing? """
184 return self.get_state() == self.STATE_PLAYING
186 @property
187 def paused(self):
188 """ Is the player paused? """
189 return self.get_state() == self.STATE_PAUSED
191 @property
192 def stopped(self):
193 """ Is the player stopped? """
194 return self.get_state() == self.STATE_STOPPED
196 @property
197 def null(self):
198 """ Is the player stopped? """
199 return self.get_state() == self.STATE_NULL
201 def set_position_duration(self, pos, dur):
202 """ used for setting pos and dur on startup"""
203 self.__position, self.__duration = pos, dur
205 return self.__position, self.__duration
207 def reset_position_duration(self):
208 self.__position, self.__duration = 0, 0