Fixing issue with back button
[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):
99                 _moduleLogger.info("back")
100                 assert self._nextSearch is None
101                 self._nextSearch = stream_index.AsyncWalker(stream_index.get_previous)
102                 self._nextSearch.start(self.node, self._on_next_node, self._on_node_search_error)
103
104         def next(self):
105                 _moduleLogger.info("next")
106                 assert self._nextSearch is None
107                 self._nextSearch = stream_index.AsyncWalker(stream_index.get_next)
108                 self._nextSearch.start(self.node, self._on_next_node, self._on_node_search_error)
109
110         def seek(self, percent):
111                 target = percent * self._stream.duration
112                 self._stream.seek_time(target)
113
114         @property
115         def percent_elapsed(self):
116                 percent = float(self._stream.elapsed) / float(self._stream.duration)
117                 return percent
118
119         def _set_piece_by_node(self, node):
120                 assert node is None or node.is_leaf(), node
121                 if self._node is node:
122                         _moduleLogger.info("Already set to %r" % node)
123                         return
124                 self._node = node
125                 if self._node is not None:
126                         self._stream.set_file(self._node.uri)
127                 _moduleLogger.info("New node %r" % self._node)
128                 self.emit("title_change", self._node)
129
130         @misc_utils.log_exception(_moduleLogger)
131         def _on_next_node(self, node):
132                 self._nextSearch = None
133
134                 restart = self.state == self.STATE_PLAY
135                 self._set_piece_by_node(node)
136                 if restart:
137                         self.play()
138
139         @misc_utils.log_exception(_moduleLogger)
140         def _on_node_search_error(self, e):
141                 self._nextSearch = None
142                 self.emit("error", e, "")
143
144         @misc_utils.log_exception(_moduleLogger)
145         def _on_stream_state(self, s, state):
146                 _moduleLogger.info("State change %r" % state)
147                 self.emit("state_change", state)
148
149         @misc_utils.log_exception(_moduleLogger)
150         def _on_stream_eof(self, s, uri):
151                 _moduleLogger.info("EOF %s" % uri)
152                 self.next()
153
154         @misc_utils.log_exception(_moduleLogger)
155         def _on_stream_error(self, s, error, debug):
156                 _moduleLogger.info("Error %s %s" % (error, debug))
157                 self.emit("error", error, debug)
158
159         @misc_utils.log_exception(_moduleLogger)
160         def _on_call_start(self, monitor):
161                 _moduleLogger.info("Call in progress, pausing")
162                 self.pause()
163
164
165 gobject.type_register(Player)