Remove trailing whitespace errors
[panucci.git] / src / panucci / backends / base.py
blobb0440e61f93a277d8b360a9357f4518a15b56b9d
1 #!/usr/bin/env python
3 # This file is part of Panucci.
4 # Copyright (c) 2008-2010 The Panucci Audiobook and Podcast Player 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/>.
20 from __future__ import absolute_import
22 import logging
24 import panucci
25 from panucci.settings import settings
26 from panucci.services import ObservableService
29 class BASE_ERROR:
30 pass
31 class BASE_ERROR_NO_MEDIA(BASE_ERROR):
32 error = _("No media selected")
33 class BASE_ERROR_UNSUPPORTED(BASE_ERROR):
34 error = _("Unsupported filetype.")
35 class BASE_ERROR_BACKEND(BASE_ERROR):
36 error = _("Something wrong with the backend")
37 class BASE_ERROR_HARDWARE(BASE_ERROR):
38 error = _("Hardware blocked/in-use")
39 class BASE_ERROR_BAD_FILE(BASE_ERROR):
40 error = _("File is corrupted/incomplete")
41 class BASE_ERROR_FILE_NOT_FOUND(BASE_ERROR):
42 error = _("File not found, make sure the file still exists.")
45 class BasePlayer(ObservableService):
46 """ The player base class, this can't be used directly because most of
47 the important functions need to be filled in by subclasses.
48 """
50 """
51 Signals
52 playing : ( )
53 Issued when the player starts playing
54 paused : ( )
55 Issued when the player pauses
56 stopped : ( )
57 Issued when the player stops
58 eof : ( )
59 Issued at the end of a file
60 error : ( error_code, error_string )
61 Issued when an error occurs. "error_code" is a unique code for a
62 specific error, "error_string" is a human-readable string that
63 describes the error.
64 """
65 signals = [ 'playing', 'paused', 'stopped', 'eof', 'error' ]
66 STATE_PLAYING, STATE_PAUSED, STATE_STOPPED, STATE_NULL = range(4)
68 def __init__(self):
69 self.__log = logging.getLogger('panucci.backends.BasePlayer')
70 ObservableService.__init__(self, self.signals, self.__log)
71 settings.register( 'volume_changed', self._set_volume_level )
73 # Cached copies of position and duration
74 self.__position, self.__duration = 0, 0
75 self.seeking = False # Are we seeking?
78 #############################################
79 # Functions to be implemented by subclasses
81 def get_state(self):
82 """ Get the current state of the player.
84 Returns: One of the following flags: player.PLAYING,
85 player.PAUSED,
86 player.STOPPED,
87 or player.NULL
88 """
90 def load_media(self, uri):
91 """ Loads a uri into the player
93 Params: uri - A full path to a media file.
94 Eg. file:///mnt/music/some-file.ogg
95 Returns: Nothing
96 """
98 def pause(self):
99 """ Pauses playback.
101 Returns: The current position in nanoseconds.
102 Signals: Must emit the "paused" signal.
105 def play(self, position=None):
106 """ Starts playing the playlist's current track.
108 Params: position is the absolute position to seek to before
109 playing. It is better to use this instead of relying on
110 play(); seek(...) because the player might not be ready to
111 seek at that point.
112 Returns: False if the current track cannot be played.
113 True if all is well.
114 Signals: Must emit the "playing" signal
117 def stop(self):
118 """ Stops playback.
120 Returns: Nothing
121 Signals: Must emit the "stopped" signal.
124 def _get_position_duration(self):
125 """ Get the position and duration of the current file.
127 Returns: ( current position, total duration )
130 def _seek(self, position):
131 """ Seek to an absolute position in the current file.
133 Params: position is the position to seek to in nanoseconds.
134 Returns: True if the seek was successfull.
137 def _set_volume_level(self, level):
138 """ Sets the volume level of the player. This should only be used in
139 conjunction with the settings manager's "volume_changed" signal.
141 Params: level is a float between 0 and 1.
142 Returns: Nothing
146 #############################################
147 # Generic Functions
149 def do_seek(self, from_beginning=None, from_current=None, percent=None ):
150 """ A very flexible function to seek in the current file
152 Params: Requires ONE of the following keyword arguments
153 - from_beginning=n: seek n nanoseconds from the start of
154 the file
155 - from_current=n: seek n nanoseconds from the current
156 position
157 - percent=n: seek n percent from the beginning of the file
159 Returns: False if the seek was NOT possible
160 ( position, duration ) if the seek was possible
162 error = False
163 position, duration = self.get_position_duration()
165 # if position and duration are 0 then player_get_position caught an
166 # exception. Therefore self.__player isn't ready to be seeking.
167 if not duration or self.get_state == self.STATE_NULL:
168 error = True
169 else:
170 if from_beginning is not None:
171 assert from_beginning >= 0
172 position = min( from_beginning, duration )
173 elif from_current is not None:
174 position = max( 0, min( position+from_current, duration ))
175 elif percent is not None:
176 assert 0 <= percent <= 1
177 position = int(duration*percent)
178 else:
179 self.__log.warning('No seek parameters specified.')
180 error = True
182 if not error:
183 self.__log.debug('do_seek: Seeking to: %d', position)
184 self._seek(position)
185 return position, duration
186 else:
187 self.__log.debug('do_seek: Could not seek.')
189 return False
191 def get_position_duration(self):
192 """ A cached version of _get_position_duration """
193 if self.playing:
194 self.__position, self.__duration = self._get_position_duration()
196 return self.__position, self.__duration
198 def play_pause_toggle(self):
199 self.pause() if self.playing else self.play()
201 @property
202 def playing(self):
203 """ Is the player playing? """
204 return self.get_state() == self.STATE_PLAYING