Fixing a Maemo 5 issue
[watersofshiloah] / src / player.py
1 import logging
2
3 import gobject
4
5 import util.misc as misc_utils
6 try:
7         import stream as _stream
8         stream = _stream # Silence PyFlakes
9 except ImportError:
10         import stream_gst as stream
11 import stream_index
12 import call_monitor
13
14
15 _moduleLogger = logging.getLogger(__name__)
16
17
18 class Player(gobject.GObject):
19
20         __gsignals__ = {
21                 'state_change' : (
22                         gobject.SIGNAL_RUN_LAST,
23                         gobject.TYPE_NONE,
24                         (gobject.TYPE_PYOBJECT, ),
25                 ),
26                 'title_change' : (
27                         gobject.SIGNAL_RUN_LAST,
28                         gobject.TYPE_NONE,
29                         (gobject.TYPE_PYOBJECT, ),
30                 ),
31                 'error' : (
32                         gobject.SIGNAL_RUN_LAST,
33                         gobject.TYPE_NONE,
34                         (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT),
35                 ),
36         }
37
38         STATE_PLAY = stream.Stream.STATE_PLAY
39         STATE_PAUSE = stream.Stream.STATE_PAUSE
40         STATE_STOP = stream.Stream.STATE_STOP
41
42         def __init__(self, index):
43                 gobject.GObject.__init__(self)
44                 self._index = index
45                 self._node = None
46                 self._nextSearch = None
47
48                 self._calls = call_monitor.CallMonitor()
49                 self._calls.connect("call_start", self._on_call_start)
50
51                 self._stream = stream.Stream()
52                 self._stream.connect("state-change", self._on_stream_state)
53                 self._stream.connect("eof", self._on_stream_eof)
54                 self._stream.connect("error", self._on_stream_error)
55
56         def set_piece_by_node(self, node):
57                 self._set_piece_by_node(node)
58
59         @property
60         def node(self):
61                 return self._node
62
63         @property
64         def title(self):
65                 if self._node is None:
66                         return ""
67                 return self._node.title
68
69         @property
70         def subtitle(self):
71                 if self._node is None:
72                         return ""
73                 return self._node.subtitle
74
75         @property
76         def can_navigate(self):
77                 if self._node is None:
78                         return False
79                 return self.node.can_navigate
80
81         @property
82         def state(self):
83                 return self._stream.state
84
85         def play(self):
86                 _moduleLogger.info("play")
87                 self._stream.play()
88
89                 self._calls.start()
90
91         def pause(self):
92                 _moduleLogger.info("pause")
93                 self._stream.pause()
94
95         def stop(self):
96                 _moduleLogger.info("stop")
97                 self._stream.stop()
98                 self.set_piece_by_node(None)
99
100                 self._calls.stop()
101
102         def back(self, forcePlay = False):
103                 _moduleLogger.info("back")
104                 assert self._nextSearch is None
105                 self._nextSearch = stream_index.AsyncWalker(stream_index.get_previous)
106                 self._nextSearch.start(
107                         self.node,
108                         lambda node: self._on_next_node(node, forcePlay),
109                         self._on_node_search_error
110                 )
111
112         def next(self, forcePlay = False):
113                 _moduleLogger.info("next")
114                 assert self._nextSearch is None
115                 self._nextSearch = stream_index.AsyncWalker(stream_index.get_next)
116                 self._nextSearch.start(
117                         self.node,
118                         lambda node: self._on_next_node(node, forcePlay),
119                         self._on_node_search_error
120                 )
121
122         def seek(self, percent):
123                 target = percent * self._stream.duration
124                 self._stream.seek_time(target)
125
126         @property
127         def percent_elapsed(self):
128                 percent = float(self._stream.elapsed) / float(self._stream.duration)
129                 return percent
130
131         def _set_piece_by_node(self, node):
132                 assert node is None or node.is_leaf(), node
133                 if self._node is node:
134                         _moduleLogger.info("Already set to %r" % node)
135                         return
136                 self._node = node
137                 if self._node is not None:
138                         self._stream.set_file(self._node.uri)
139                 _moduleLogger.info("New node %r" % self._node)
140                 self.emit("title_change", self._node)
141
142         @misc_utils.log_exception(_moduleLogger)
143         def _on_next_node(self, node, forcePlay):
144                 self._nextSearch = None
145
146                 restart = self.state == self.STATE_PLAY
147                 self._set_piece_by_node(node)
148                 if restart or forcePlay:
149                         self.play()
150
151         @misc_utils.log_exception(_moduleLogger)
152         def _on_node_search_error(self, e):
153                 self._nextSearch = None
154                 self.emit("error", e, "")
155
156         @misc_utils.log_exception(_moduleLogger)
157         def _on_stream_state(self, s, state):
158                 _moduleLogger.info("State change %r" % state)
159                 self.emit("state_change", state)
160
161         @misc_utils.log_exception(_moduleLogger)
162         def _on_stream_eof(self, s, uri):
163                 _moduleLogger.info("EOF %s" % uri)
164                 self.next(forcePlay = True)
165
166         @misc_utils.log_exception(_moduleLogger)
167         def _on_stream_error(self, s, error, debug):
168                 _moduleLogger.info("Error %s %s" % (error, debug))
169                 self.emit("error", error, debug)
170
171         @misc_utils.log_exception(_moduleLogger)
172         def _on_call_start(self, monitor):
173                 _moduleLogger.info("Call in progress, pausing")
174                 self.pause()
175
176
177 gobject.type_register(Player)