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
23 from panucci
.services
import ForwardingObservableService
24 from panucci
.dbusinterface
import interface
26 from panucci
import util
28 class PanucciPlayer(ForwardingObservableService
):
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
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" ],
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
76 if self
.__player
is not None:
77 return getattr(self
.__player
, attr
)
79 self
.__log
.critical("No player available")
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)
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)
101 (pos
, dur
) = self
.get_position_duration()
103 text
= util
.convert_ns(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
:
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()
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
)
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] )
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
)