-
Notifications
You must be signed in to change notification settings - Fork 3
/
MythUtil-Channel-videosource
executable file
·369 lines (351 loc) · 17.5 KB
/
MythUtil-Channel-videosource
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
#!/usr/bin/python3
import argparse
import sys
import json
import re
import natsort
import requests
import requests.auth
class MythTVServices():
def __init__(self, host=None, port=None, username=None, password=None):
if host is None:
host = 'localhost'
self.host = host
if port is None:
port = 6544
self.port = port
self.session = requests.Session()
if username and password:
self.session.auth = requests.auth.HTTPDigestAuth(username, password)
self.request(service='Myth', api='version')
def request(self, service=None, api=None, data={}, method=None, stream=False):
version = '0.28'
headers = {'User-Agent':'{} Python Services API Client'.format(version),
'Accept':'application/json',
'Accept-Encoding':'gzip,deflate'}
if api is None:
raise ValueError('api must be specified')
url = 'http://{}:{}/{}/{}'.format(self.host, self.port, service, api)
if method is None:
if bool(data):
method = 'post'
else:
method = 'get'
if method == 'get':
response = self.session.get(url, headers=headers, params=data, stream=stream)
elif method == 'post':
response = self.session.post(url, headers=headers, data=data, stream=stream)
else:
raise ValueError('method is not post or get: {}'.format(method))
response.raise_for_status()
if stream:
response.raw.decode_content = True
return response.raw
else:
return response.json()
def Capture(self, api=None, data={}, method=None, stream=False):
return self.request(service='Capture', api=api, data=data, method=method, stream=stream)
def Channel(self, api=None, data={}, method=None, stream=False):
return self.request(service='Channel', api=api, data=data, method=method, stream=stream)
def Content(self, api=None, data={}, method=None, stream=False):
return self.request(service='Content', api=api, data=data, method=method, stream=stream)
def Dvr(self, api=None, data={}, method=None, stream=False):
return self.request(service='Dvr', api=api, data=data, method=method, stream=stream)
def Frontend(self, api=None, data={}, method=None, stream=False):
return self.request(service='Frontend', api=api, data=data, method=method, stream=stream)
def Guide(self, api=None, data={}, method=None, stream=False):
return self.request(service='Guide', api=api, data=data, method=method, stream=stream)
def Myth(self, api=None, data={}, method=None, stream=False):
return self.request(service='Myth', api=api, data=data, method=method, stream=stream)
def Video(self, api=None, data={}, method=None, stream=False):
return self.request(service='Video', api=api, data=data, method=method, stream=stream)
def TrueOrFalse(arg):
ua = str(arg).lower()
if ua == 'true'[:len(ua)]:
return True
elif ua == 'yes'[:len(ua)]:
return True
elif ua == '1':
return True
elif ua == 'false'[:len(ua)]:
return False
elif ua == 'no'[:len(ua)]:
return False
elif ua == '0':
return False
raise argparse.ArgumentTypeError('{} is not a valid true/false value'.format(arg))
def unsignedInt(v):
try:
v = int(v)
except ValueError:
raise argparse.ArgumentTypeError("{} is not a valid positive integer".format(v))
if v < 0:
raise argparse.ArgumentTypeError("{} is not a valid positive integer".format(v))
return v
def versionTuple(v):
return tuple(map(int, (v.split("."))))
def transformAPIElementNames(APIname, existingDict):
transforms = \
{
'VideoSource':
{
'Id': 'SourceId',
},
'Channel':
{
'SourceId': 'SourceID',
'MplexId': 'MplexID',
'ChanId': 'ChannelID',
'ChanNum': 'ChannelNumber',
'ServiceId': 'ServiceID',
'ATSCMajorChan': 'ATSCMajorChannel',
'ATSCMinorChan': 'ATSCMinorChannel',
'Visible': 'visible',
'FrequencyId': 'FrequencyID',
'DefaultAuth': 'DefaultAuthority'
}
}
transform = transforms.get(APIname, {})
newDict = {}
for key, value in existingDict.items():
newKey = transform.get(key, key)
if newKey is not None:
newDict[newKey] = value
return newDict
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--backend', '--host', action='store', type=str, default='localhost',
help='the host (backend) to access. The default is localhost.')
parser.add_argument('--port', action='store', type=int, default=6544,
help='the port to connect to on on the host. The default is 6544')
parser.add_argument('--username', action='store', type=str, default=None,
help='the username to use for host authentication')
parser.add_argument('--password', action='store', type=str, default=None,
help='the password to use for host authentication')
sourcegroup = parser.add_mutually_exclusive_group(required=False)
sourcegroup.add_argument('--videosource-name', action='store', type=str, dest='sourceName',
help='the video source name')
sourcegroup.add_argument('--videosource-id', action='store', type=unsignedInt, dest='sourceId',
help='the video source id')
actiongroup = parser.add_mutually_exclusive_group(required=False)
actiongroup.add_argument('--add', action='store_true', default=False,
help='add a video source')
actiongroup.add_argument('--delete', action='store_true', default=False,
help='delete a video source')
actiongroup.add_argument('--update', action='store_true', default=False,
help='update a video source')
parser.add_argument('--grabber', action='store', type=str,
help='grabber name')
parser.add_argument('--config', action='store', type=str,
help='grabber configuration file')
parser.add_argument('--use-eit', default=None, dest='useEIT', type=TrueOrFalse,
help='use eit for guide')
parser.add_argument('--network-id', action='store', type=int, dest='networkId',
help='provider network id')
parser.add_argument('--bouquet-id', action='store', type=unsignedInt, dest='bouquetId',
help='provider bouquet id')
parser.add_argument('--region-id', action='store', type=unsignedInt, dest='regionId',
help='provider region id')
parser.add_argument('--scan-frequency', action='store', type=unsignedInt, dest='scanFreq',
help='provider start scan frequency')
parser.add_argument('--lcnoffset', action='store', type=unsignedInt, dest='lcnoffset',
help='offset for lcn')
parser.add_argument('--frequency-table', default=None, type=str.lower, dest='freqTable',
choices=["default", "us-bcast", "us-cable", "us-cable-hrc",
"us-cable-irc", "japan-bcast", "japan-cable", "europe-west",
"europe-east", "italy", "newzealand", "australia", "ireland",
"france", "china-bcast", "southafrica", "argentina",
"australia-optus", "singapore", "malaysia",
"israel-hot-matav"],
help='frequency table to use')
parser.add_argument('--grabber-userid', action='store', type=str, dest='grabberUserid',
help='userid for schedulesdirect1 (internal) grabber')
parser.add_argument('--grabber-password', action='store', type=str, dest='grabberPassword',
help='password for schedulesdirect1 (internal) grabber')
parser.add_argument('--lineupid', action='store', type=str,
help='lineupid for schedulesdirect1 (internal) grabber')
parser.add_argument('--new-videosource-name', action='store', type=str, dest='sourceNameNew',
help='new video source name for update')
args = parser.parse_args()
s = MythTVServices(args.backend, args.port, args.username, args.password)
try:
hostname = s.Myth('GetHostName')['String']
except Exception:
print('Unable to obtain hostname from host {}:{}'.format(args.backend, args.port))
sys.exit(1)
ChannelServiceVersion = versionTuple(s.Channel('version')['String'])
# If no action, just a list of video sources
if (not args.add) and (not args.update) and (not args.delete):
if (args.grabber is not None) or (args.config is not None) or (args.useEIT is not None) or (args.networkId is not None) or (args.freqTable is not None) or (args.grabberUserid is not None) or (args.grabberPassword is not None) or (args.bouquetId is not None) or (args.regionId is not None) or (args.scanFreq is not None) or (args.sourceNameNew is not None) or (args.lcnoffset is not None):
print('List requests cannot specify the grabber, config, use-eit, network-id, frequency-table, grabber-userid, grabber-password, bouquet-id, region-id, scan-frequency, lcnoffset or new-videosource-name')
sys.exit(1)
mythsl = s.Channel('GetVideoSourceList')['VideoSourceList']['VideoSources']
print('Configured videosources:')
for source in mythsl:
if ((args.sourceName is not None) or (args.sourceId is not None)) and (int(source['Id']) != args.sourceId and source['SourceName'] != args.sourceName):
continue
print(' {}'.format(source['SourceName']))
print(' ID: {}'.format(source['Id']))
print(' grabber: {}'.format(source['Grabber']))
print(' config: {}'.format(source['ConfigPath']))
print(' UseEIT: {}'.format(source['UseEIT']))
print(' FreqTable: {}'.format(source['FreqTable']))
print(' NetworkID: {}'.format(source['NITId']))
print(' userid: {}'.format(source['UserId']))
print(' password: {}'.format(source['Password']))
print(' lineupid: {}'.format(source['LineupId']))
if ChannelServiceVersion >= versionTuple("1.8"):
print(' BouquetID: {}'.format(source['BouquetId']))
print(' ReqionID: {}'.format(source['RegionId']))
print(' ScanFreq: {}'.format(source['ScanFrequency']))
if ChannelServiceVersion >= versionTuple("1.10"):
print(' LCNOffset: {}'.format(source['LCNOffset']))
sys.exit(0)
# Try to locate source name/id if specified
mythsl = s.Channel('GetVideoSourceList')['VideoSourceList']['VideoSources']
sourceId = None
sourceName = None
for source in mythsl:
if int(source['Id']) == args.sourceId or source['SourceName'] == args.sourceName:
sourceId = int(source['Id'])
sourceName = source['SourceName']
break
# Perform actions
if args.add:
if args.sourceName is None:
print('Video source name name must be specified for an add request')
sys.exit(1)
if sourceId is not None:
print('Video source already exists with that name')
sys.exit(1)
data = {}
data['SourceName'] = args.sourceName
data['Grabber'] = '/bin/true'
data['ConfigPath'] = None
data['LineupId'] = None
data['UserId'] = None
data['Password'] = None
data['FreqTable'] = 'default'
data['UseEIT'] = False
data['NITId'] = -1
data['BouquetId'] = 0
data['RegionId'] = 0
data['ScanFrequency'] = 0
data['LCNOffset'] = 0
if args.grabber is not None:
if (ChannelServiceVersion >= versionTuple("1.8")) and (args.grabber in ['schedulesdirect1', 'datadirect']):
print("Warning: backend version no longer supports the internal Schedules Direct grabber")
data['Grabber'] = args.grabber
if args.config is not None:
data['ConfigPath'] = args.config
if args.lineupid is not None:
data['LineupId'] = args.lineupid
if args.grabberUserid is not None:
data['UserId'] = args.grabberUserid
if args.grabberPassword is not None:
data['Password'] = args.grabberPassword
if args.useEIT is not None:
data['UseEIT'] = args.useEIT
if args.freqTable is not None:
data['FreqTable'] = args.freqTable
if args.networkId is not None:
data['NITId'] = args.networkId
if args.bouquetId is not None:
if ChannelServiceVersion < versionTuple("1.8"):
print("Warning: backend version does not support bouquet-id")
data['BouquetId'] = args.bouquetId
if args.regionId is not None:
if ChannelServiceVersion < versionTuple("1.8"):
print("Warning: backend version does not support region-id")
data['RegionId'] = args.regionId
if args.scanFreq is not None:
if ChannelServiceVersion < versionTuple("1.8"):
print("Warning: backend version does not support scan-frequency")
data['ScanFrequency'] = args.scanFreq
if args.lcnoffset is not None:
if ChannelServiceVersion < versionTuple("1.10"):
print("Warning: backend version does not support lcnoffset")
data['LCNOffset'] = args.lcnoffset
if int(s.Channel('AddVideoSource', transformAPIElementNames('VideoSource', data))['int']) > 0:
print('Video source added')
try:
s.Dvr('RescheduleRecordings')
except Exception:
pass
sys.exit(0)
else:
print('Video source addition failed')
sys.exit(1)
elif args.delete:
if (args.sourceId is None) and (args.sourceName is None):
print('No video source specified to delete')
sys.exit(1)
if sourceId is None:
print('Video source was not found to delete')
sys.exit(1)
data = {}
data['Id'] = source['Id']
if bool(s.Channel('RemoveVideoSource', transformAPIElementNames('VideoSource', data))['bool']):
print('Video source deleted')
try:
s.Dvr('RescheduleRecordings')
except Exception:
pass
sys.exit(0)
else:
print('Video source deletion failed')
sys.exit(1)
elif args.update:
if (args.sourceId is None) and (args.sourceName is None):
print('No video source specified to update')
sys.exit(1)
if sourceId is None:
print('Video source was not found to update')
sys.exit(1)
data = source.copy()
if args.sourceNameNew is not None:
data['SourceName'] = args.sourceNameNew
if args.grabber is not None:
if (ChannelServiceVersion >= versionTuple("1.8")) and (args.grabber in ['schedulesdirect1', 'datadirect']):
print("Warning: backend version no longer supports the internal Schedules Direct grabber")
data['Grabber'] = args.grabber
if args.config is not None:
data['ConfigPath'] = args.config
if args.lineupid is not None:
data['LineupId'] = args.lineupid
if args.grabberUserid is not None:
data['UserId'] = args.grabberUserid
if args.grabberPassword is not None:
data['Password'] = args.grabberPassword
if args.useEIT is not None:
data['UseEIT'] = args.useEIT
if args.freqTable is not None:
data['FreqTable'] = args.freqTable
if args.networkId is not None:
data['NITId'] = args.networkId
if args.bouquetId is not None:
if ChannelServiceVersion < versionTuple("1.8"):
print("Warning: backend version does not support bouquet-id")
data['BouquetId'] = args.bouquetId
if args.regionId is not None:
if ChannelServiceVersion < versionTuple("1.8"):
print("Warning: backend version does not support region-id")
data['RegionId'] = args.regionId
if args.scanFreq is not None:
if ChannelServiceVersion < versionTuple("1.8"):
print("Warning: backend version does not support scan-frequency")
data['ScanFrequency'] = args.scanFreq
if args.lcnoffset is not None:
if ChannelServiceVersion < versionTuple("1.10"):
print("Warning: backend version does not support lcnoffset")
data['LCNOffset'] = args.lcnoffset
if bool(s.Channel('UpdateVideoSource', transformAPIElementNames('VideoSource', data))['bool']):
print('Video source updated')
try:
s.Dvr('RescheduleRecordings')
except Exception:
pass
sys.exit(0)
else:
print('Video source update failed')
sys.exit(1)