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