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
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.
35 Issued when the player starts playing
37 Issued when the player pauses
39 Issued when the player stops
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
47 signals
= [ 'playing', 'paused', 'stopped', 'eof', 'error' ]
48 STATE_PLAYING
, STATE_PAUSED
, STATE_STOPPED
, STATE_NULL
= range(4)
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
70 """ Get the current state of the player.
72 Returns: One of the following flags: player.PLAYING,
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
86 self
.current_uri
= util
.file_to_url(uri
)
87 return self
._load
_media
(self
.current_uri
)
92 Returns: The current position in nanoseconds.
93 Signals: Must emit the "paused" signal.
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
104 Returns: False if the current track cannot be played.
106 Signals: Must emit the "playing" signal
110 def stop(self
, player
=False):
113 Params: player, if true delete player
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 #############################################
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
142 - from_current=n: seek n nanoseconds from the current
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
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
:
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
)
166 self
.__log
.warning('No seek parameters specified.')
170 self
.__log
.debug('do_seek: Seeking to: %d', position
)
172 return position
, duration
174 self
.__log
.debug('do_seek: Could not seek.')
178 def play_pause_toggle(self
):
179 self
.pause() if self
.playing
else self
.play()
183 """ Is the player playing? """
184 return self
.get_state() == self
.STATE_PLAYING
188 """ Is the player paused? """
189 return self
.get_state() == self
.STATE_PAUSED
193 """ Is the player stopped? """
194 return self
.get_state() == self
.STATE_STOPPED
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