fixed seeking with buttons when paused
[panucci.git] / src / panucci / player.py
blobea1cdabe0e7ee939d39e79df661148dd4669fc77
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 from panucci.services import ForwardingObservableService
24 from panucci.dbusinterface import interface
26 from panucci import util
28 class PanucciPlayer(ForwardingObservableService):
29 """
30 A proxy object which adds a layer of abstraction between all the different
31 backends. It picks the right backend for the job and forwards signals from
32 them.
33 """
34 signals = [ "playing", "paused", "stopped", "eof" ]
36 def __init__(self, playlist):
37 self.__log = logging.getLogger('panucci.player.PanucciPlayer')
38 ForwardingObservableService.__init__(self, self.signals, self.__log)
39 self.playlist = playlist
40 self.config = playlist.config
42 if self.config.get("options", "backend") == "gstreamer":
43 from panucci.backends.gstreamer import Player
44 self.__player = Player()
46 self.__initialized = False
47 self._start_position = 0
48 self._is_playing = None
50 # Forward the following signals
51 self.forward( self.__player,
52 [ "playing", "paused", "stopped", "eof" ],
53 PanucciPlayer )
55 self.__player.register( "playing", self.on_playing )
56 self.__player.register( "paused", self.on_paused )
57 self.__player.register( "stopped", self.on_stopped )
58 self.__player.register( "eof", self.on_stopped )
59 self.__player.register( "error", self.on_player_error )
62 self.playlist.register( 'new-track-loaded', self.on_new_track )
63 self.playlist.register( 'seek-requested', self.do_seek )
64 self.playlist.register( 'stop-requested', self.on_stop_requested )
65 self.playlist.register( 'reset-playlist', self.on_reset_playlist )
67 # Register the d-bus interface only once we're ready
68 interface.register_player(self)
70 def __getattr__(self, attr):
71 """ If the attribute isn't found in this object, get it from
72 the player object. This makes proxying function calls simple
73 and transparent.
74 """
76 if self.__player is not None:
77 return getattr(self.__player, attr)
78 else:
79 self.__log.critical("No player available")
80 raise AttributeError
82 def add_bookmark_at_current_position( self, label=None ):
83 """ Adds a bookmark at the current position
85 Returns: (bookmark lable string, position in nanoseconds)
86 """
88 default_label, position = self.get_formatted_position()
89 label = default_label if label is None else label
90 self.playlist.save_bookmark( label, position )
91 self.__log.info('Added bookmark: %s - %d', label, position)
92 return label, position
94 def get_formatted_position(self, pos=None):
95 """ Gets the current position and converts it to a human-readable str.
97 Returns: (bookmark lable string, position in nanoseconds)
98 """
100 if pos is None:
101 (pos, dur) = self.get_position_duration()
103 text = util.convert_ns(pos)
104 return (text, pos)
106 def on_new_track(self):
107 """ New track callback; stops the player and starts the new track. """
109 if self.playlist.current_filepath is not None:
111 filepath = self.playlist.current_filepath
112 if filepath.startswith('/'):
113 filepath = 'file://' + filepath
115 self.load_media(filepath)
117 # This is just here to prevent the player from starting to play
118 # when it is first opened. The first time this function is called it
119 # doesn't run self.play(), but otherwise it will.
120 if self.__initialized:
121 self.play()
123 self.__initialized = True
125 def do_seek(self, from_beginning=None, from_current=None, percent=None):
126 pos, dur = self.get_position_duration()
127 pos_sec = max(0, pos / 10**9)
128 dur_sec = max(0, dur / 10**9)
130 if self._is_playing and self.current_file:
131 interface.PlaybackStopped(self._start_position, pos_sec, dur_sec, self.current_file)
132 self._is_playing = False
134 # Hand over the seek command to the backend
135 return self.__player.do_seek(from_beginning, from_current, percent)
137 def on_playing(self):
139 Used to seek to the correct position once the file has started
140 playing. This has to be done once the player is ready because
141 certain player backends can't seek in any state except playing.
143 seek = self.playlist.play()
144 if seek > 0:
145 self._seek(seek)
147 pos, dur = self.get_position_duration()
148 pos_sec = pos / 10**9
149 dur_sec = dur / 10**9
151 interface.PlaybackStarted(pos_sec, self.current_file)
152 self._start_position = pos_sec
153 self._is_playing = True
155 def on_paused(self, *args):
156 self.on_stopped_paused()
157 self._is_playing = False
159 def on_stopped(self, *args):
160 self.on_stopped_paused()
161 self._is_playing = None
163 def on_stopped_paused(self):
164 pos, dur = self.get_position_duration()
165 pos_sec = max(0, pos / 10**9)
166 dur_sec = max(0, dur / 10**9)
167 if self.current_file is not None:
168 interface.PlaybackStopped(self._start_position, pos_sec, dur_sec, self.current_file)
170 @property
171 def current_file(self):
172 return self.playlist.current_filepath
174 def on_stop_requested(self):
175 self.playlist.stop( self.get_position_duration()[0] )
176 self.stop()
177 self._is_playing = False
179 def on_reset_playlist(self):
180 self.on_stop_requested()
181 self.__player.reset_position_duration()
183 def on_player_error(self, msg):
184 self.__log.error("Error: %s", msg)