1 |
apollock |
8 |
#!/usr/bin/python |
2 |
|
|
|
3 |
|
|
import socket |
4 |
|
|
import datetime |
5 |
|
|
|
6 |
apollock |
10 |
class FrontendNotReady(Exception): |
7 |
|
|
pass |
8 |
|
|
|
9 |
|
|
|
10 |
|
|
class FrontendCmdFailure(Exception): |
11 |
|
|
pass |
12 |
|
|
|
13 |
|
|
|
14 |
apollock |
8 |
class Frontend: |
15 |
|
|
"""Class for operating on a MythTV frontend""" |
16 |
|
|
|
17 |
|
|
def __init__(self, host, port=6546): |
18 |
|
|
self.host = host |
19 |
|
|
self.port = port |
20 |
|
|
self.ready = False |
21 |
|
|
self.socket = None |
22 |
apollock |
10 |
# We might as well raise an exception at instantiation if |
23 |
|
|
# the frontend is unavailable |
24 |
|
|
self._connect() |
25 |
apollock |
8 |
|
26 |
|
|
|
27 |
|
|
def _connect(self): |
28 |
|
|
"""Create a connection to the frontend""" |
29 |
|
|
|
30 |
|
|
self.socket = socket.socket() |
31 |
apollock |
10 |
try: |
32 |
|
|
self.socket.connect((self.host, self.port)) |
33 |
|
|
except socket.error: |
34 |
|
|
raise FrontendNotReady("Unable to connect to frontend") |
35 |
apollock |
8 |
if self.socket.recv(4096).splitlines()[-1] == '# ': |
36 |
|
|
self.ready = True |
37 |
|
|
|
38 |
|
|
|
39 |
|
|
def _sendcmd(self, cmd): |
40 |
|
|
"""Send a command to the frontend""" |
41 |
|
|
|
42 |
apollock |
10 |
self._connect() |
43 |
apollock |
8 |
if self.ready: |
44 |
|
|
self.socket.send("%s\r\n" % cmd) |
45 |
|
|
return self.socket.recv(4096).splitlines() |
46 |
|
|
|
47 |
|
|
|
48 |
|
|
def location(self): |
49 |
|
|
"""Return what location the front end is currently in""" |
50 |
|
|
|
51 |
|
|
result = self._sendcmd("query location")[0].split(" ") |
52 |
apollock |
10 |
location = Location() |
53 |
|
|
location.where = result[0] |
54 |
|
|
if location.where == 'Playback': |
55 |
|
|
location.program = ProgramInfo() |
56 |
|
|
location.program.what = result[1] |
57 |
|
|
location.program.position = result[2] |
58 |
|
|
location.program.length = result[4] |
59 |
|
|
location.program.speed = result[5] |
60 |
|
|
location.program.chanid = result[6] |
61 |
apollock |
8 |
recdate = result[7].split("T")[0].split("-") |
62 |
apollock |
10 |
location.program.recdate = datetime.date(int(recdate[0]), int(recdate[1]), int(recdate[2])) |
63 |
apollock |
8 |
rectime = result[7].split("T")[1].split(":") |
64 |
apollock |
10 |
location.program.rectime = datetime.time(int(rectime[0]), int(rectime[1]), int(rectime[2])) |
65 |
|
|
if location.program.what == 'Recorded': |
66 |
|
|
try: |
67 |
|
|
location.program.title = self.recording(location.program.chanid, location.program.recdate.isoformat(), location.program.rectime.isoformat()) |
68 |
|
|
except FrontendNotReady: |
69 |
|
|
location.program.title = None |
70 |
|
|
elif location.where == 'PlaybackBox': |
71 |
apollock |
8 |
pass |
72 |
apollock |
10 |
elif location.where == 'MainMenu': |
73 |
apollock |
8 |
pass |
74 |
|
|
|
75 |
|
|
return location |
76 |
|
|
|
77 |
|
|
|
78 |
|
|
def recording(self, chanid, recdate, rectime): |
79 |
|
|
"""Returns recording information for a specified recording""" |
80 |
|
|
|
81 |
|
|
result = self._sendcmd("query recording %s %sT%s" % |
82 |
|
|
(chanid, recdate, rectime))[0].split(" ") |
83 |
|
|
|
84 |
|
|
return " ".join(result[2:]) |
85 |
|
|
|
86 |
|
|
|
87 |
|
|
def pause(self): |
88 |
|
|
"""Pauses the playback""" |
89 |
|
|
|
90 |
|
|
result = self._sendcmd("play speed pause")[0] |
91 |
|
|
if result != "OK": |
92 |
|
|
#TODO(apollock): raise some sort of exception |
93 |
apollock |
10 |
raise FrontendCmdFailure("Frontend said '%s' in response to 'play speed pause' command" % result) |
94 |
apollock |
8 |
|
95 |
|
|
|
96 |
|
|
def play(self): |
97 |
|
|
"""Resumes playback""" |
98 |
|
|
result = self._sendcmd("play speed normal")[0] |
99 |
|
|
if result != "OK": |
100 |
apollock |
10 |
raise FrontendCmdFailure("Frontend said '%s' in response to 'play speed normal' command" % result) |
101 |
|
|
|
102 |
|
|
|
103 |
|
|
def isPaused(self): |
104 |
|
|
"""Returns if the the frontend is paused or not""" |
105 |
|
|
loc1 = self.location() |
106 |
|
|
if loc1.where != 'Playback': |
107 |
|
|
return False |
108 |
|
|
else: |
109 |
|
|
loc2 = self.location() |
110 |
|
|
if loc2.where != 'Playback': |
111 |
|
|
return False |
112 |
|
|
else: |
113 |
|
|
return loc1.program.position == loc2.program.position |
114 |
|
|
|
115 |
|
|
|
116 |
|
|
class Location: |
117 |
|
|
"""Container for where the frontend currently is""" |
118 |
|
|
|
119 |
|
|
def __init__(self): |
120 |
|
|
self.program = None |
121 |
|
|
self.where = None |
122 |
|
|
|
123 |
|
|
|
124 |
|
|
class ProgramInfo: |
125 |
|
|
"""Container for program information""" |
126 |
|
|
|
127 |
|
|
def __init__(self): |
128 |
|
|
pass |