| | 224 | def getRecording(self, chanid, starttime): |
| | 225 | """ |
| | 226 | Returns a Program object matching the channel id and start time |
| | 227 | """ |
| | 228 | res = self.backendCommand('QUERY_RECORDING TIMESLOT %d %d' % (chanid, starttime)).split(BACKEND_SEP) |
| | 229 | if res[0] == 'ERROR': |
| | 230 | return None |
| | 231 | else: |
| | 232 | return Program(res[1:]) |
| | 233 | |
| | 234 | def getRecordings(self): |
| | 235 | """ |
| | 236 | Returns a list of all Program objects which have already recorded |
| | 237 | """ |
| | 238 | programs = [] |
| | 239 | res = self.backendCommand('QUERY_RECORDINGS Play').split('[]:[]') |
| | 240 | num_progs = int(res.pop(0)) |
| | 241 | log.Msg(DEBUG, '%s total recordings', num_progs) |
| | 242 | for i in range(num_progs): |
| | 243 | programs.append(Program(res[i * PROGRAM_FIELDS:(i * PROGRAM_FIELDS) |
| | 244 | + PROGRAM_FIELDS])) |
| | 245 | return tuple(programs) |
| | 246 | |
| | 247 | def getCheckfile(self,program): |
| | 248 | """ |
| | 249 | Returns location of recording in file system |
| | 250 | """ |
| | 251 | res = self.backendCommand('QUERY_CHECKFILE[]:[]1[]:[]%s' % program.toString()).split(BACKEND_SEP) |
| | 252 | if res[0] == 0: |
| | 253 | return None |
| | 254 | else: |
| | 255 | return res[1] |
| | 256 | |
| | 257 | def getFrontends(self): |
| | 258 | """ |
| | 259 | Returns a list of Frontend objects for accessible frontends |
| | 260 | """ |
| | 261 | cursor = self.db.db.cursor() |
| | 262 | cursor.execute("SELECT DISTINCT hostname FROM settings WHERE hostname IS NOT NULL and value='NetworkControlEnabled' and data=1") |
| | 263 | frontends = [] |
| | 264 | for fehost in cursor.fetchall(): |
| | 265 | try: |
| | 266 | frontend = self.getFrontend(fehost[0]) |
| | 267 | frontends.append(frontend) |
| | 268 | except: |
| | 269 | print "%s is not a valid frontend" % fehost[0] |
| | 270 | cursor.close() |
| | 271 | return frontends |
| | 272 | |
| | 273 | def getFrontend(self,host): |
| | 274 | """ |
| | 275 | Returns a Frontend object for the specified host |
| | 276 | """ |
| | 277 | port = self.db.getSetting("NetworkControlPort",host) |
| | 278 | return Frontend(host,port) |
| | 279 | |
| | 280 | class Frontend: |
| | 281 | isConnected = False |
| | 282 | socket = None |
| | 283 | host = None |
| | 284 | port = None |
| | 285 | |
| | 286 | def __init__(self, host, port): |
| | 287 | self.host = host |
| | 288 | self.port = int(port) |
| | 289 | self.connect() |
| | 290 | self.disconnect() |
| | 291 | |
| | 292 | def __del__(self): |
| | 293 | if self.isConnected: |
| | 294 | self.disconnect() |
| | 295 | |
| | 296 | def __repr__(self): |
| | 297 | return "%s@%d" % (self.host, self.port) |
| | 298 | |
| | 299 | def __str__(self): |
| | 300 | return "%s@%d" % (self.host, self.port) |
| | 301 | |
| | 302 | def connect(self): |
| | 303 | self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| | 304 | self.socket.settimeout(10) |
| | 305 | self.socket.connect((self.host, self.port)) |
| | 306 | if self.recv()[:28] != "MythFrontend Network Control": |
| | 307 | self.socket.close() |
| | 308 | self.socket = None |
| | 309 | raise Exception('FrontendConnect','Connected socket does not belong to a mythfrontend') |
| | 310 | self.isConnected = True |
| | 311 | |
| | 312 | def disconnect(self): |
| | 313 | self.send("exit") |
| | 314 | self.socket.close() |
| | 315 | self.socket = None |
| | 316 | self.isConnected = False |
| | 317 | |
| | 318 | def send(self,command): |
| | 319 | if not self.isConnected: |
| | 320 | self.connect() |
| | 321 | self.socket.send("%s\n" % command) |
| | 322 | |
| | 323 | def recv(self,curstr=""): |
| | 324 | def subrecv(self,curstr=""): |
| | 325 | try: |
| | 326 | curstr += self.socket.recv(100) |
| | 327 | except: |
| | 328 | return None |
| | 329 | if curstr[-4:] != '\r\n# ': |
| | 330 | curstr = subrecv(self,curstr) |
| | 331 | return curstr |
| | 332 | return subrecv(self)[:-4] |
| | 333 | |
| | 334 | def sendJump(self,jumppoint): |
| | 335 | """ |
| | 336 | Sends jumppoint to frontend |
| | 337 | """ |
| | 338 | self.send("jump %s" % jumppoint) |
| | 339 | if self.recv() == 'OK': |
| | 340 | return 0 |
| | 341 | else: |
| | 342 | return 1 |
| | 343 | |
| | 344 | def getJump(self): |
| | 345 | """ |
| | 346 | Returns a tuple containing available jumppoints |
| | 347 | """ |
| | 348 | self.send("help jump") |
| | 349 | res = self.recv().split('\r\n')[3:-1] |
| | 350 | points = [] |
| | 351 | for point in res: |
| | 352 | spoint = point.split(' - ') |
| | 353 | points.append((spoint[0].rstrip(),spoint[1])) |
| | 354 | return tuple(points) |
| | 355 | |
| | 356 | def sendKey(self,key): |
| | 357 | """ |
| | 358 | Sends keycode to connected frontend |
| | 359 | """ |
| | 360 | self.send("key %s" % key) |
| | 361 | if self.recv() == 'OK': |
| | 362 | return 0 |
| | 363 | else: |
| | 364 | return 1 |
| | 365 | |
| | 366 | def getKey(self): |
| | 367 | """ |
| | 368 | Returns a tuple containing available special keys |
| | 369 | """ |
| | 370 | self.send("help key") |
| | 371 | res = self.recv().split('\r\n')[4] |
| | 372 | keys = [] |
| | 373 | for key in res.split(','): |
| | 374 | keys.append(key.strip()) |
| | 375 | return tuple(keys) |
| | 376 | |
| | 377 | def sendQuery(query): |
| | 378 | """ |
| | 379 | Returns query from connected frontend |
| | 380 | """ |
| | 381 | self.send("query %s" % query) |
| | 382 | return self.recv() |
| | 383 | |
| | 384 | def getQuery(self): |
| | 385 | """ |
| | 386 | Returns a tuple containing available queries |
| | 387 | """ |
| | 388 | self.send("help query") |
| | 389 | res = self.recv().split('\r\n')[:-1] |
| | 390 | queries = [] |
| | 391 | tmpstr = "" |
| | 392 | for query in res: |
| | 393 | tmpstr += query |
| | 394 | squery = tmpstr.split(' - ') |
| | 395 | if len(squery) == 2: |
| | 396 | tmpstr = "" |
| | 397 | queries.append((squery[0].rstrip().lstrip('query '),squery[1])) |
| | 398 | return tuple(queries) |
| | 399 | |
| | 400 | def sendPlay(play): |
| | 401 | """ |
| | 402 | Send playback command to connected frontend |
| | 403 | """ |
| | 404 | self.send("play %s" % play) |
| | 405 | if self.recv() == 'OK': |
| | 406 | return 0 |
| | 407 | else: |
| | 408 | return 1 |
| | 409 | |
| | 410 | def getPlay(self): |
| | 411 | """ |
| | 412 | Returns a tuple containing available playback commands |
| | 413 | """ |
| | 414 | self.send("help play") |
| | 415 | res = self.recv().split('\r\n')[:-1] |
| | 416 | plays = [] |
| | 417 | tmpstr = "" |
| | 418 | for play in res: |
| | 419 | tmpstr += play |
| | 420 | splay = tmpstr.split(' - ') |
| | 421 | if len(splay) == 2: |
| | 422 | tmpstr = "" |
| | 423 | plays.append((splay[0].rstrip().lstrip('play '),splay[1])) |
| | 424 | return tuple(plays) |
| | 425 | |
| | 426 | |
| | 427 | |
| | 514 | def toString(self): |
| | 515 | string = self.title |
| | 516 | string += BACKEND_SEP + self.subtitle |
| | 517 | string += BACKEND_SEP + self.description |
| | 518 | string += BACKEND_SEP + self.category |
| | 519 | if self.chanid: |
| | 520 | string += BACKEND_SEP + str(self.chanid) |
| | 521 | else: |
| | 522 | string += BACKEND_SEP |
| | 523 | string += BACKEND_SEP + self.channum |
| | 524 | string += BACKEND_SEP + self.callsign |
| | 525 | string += BACKEND_SEP + self.channame |
| | 526 | string += BACKEND_SEP + self.filename |
| | 527 | string += BACKEND_SEP + str(self.fs_high) |
| | 528 | string += BACKEND_SEP + str(self.fs_low) |
| | 529 | string += BACKEND_SEP + str(int(mktime(self.starttime.timetuple()))) |
| | 530 | string += BACKEND_SEP + str(int(mktime(self.endtime.timetuple()))) |
| | 531 | string += BACKEND_SEP + str(self.duplicate) |
| | 532 | string += BACKEND_SEP + str(self.shareable) |
| | 533 | string += BACKEND_SEP + str(self.findid) |
| | 534 | string += BACKEND_SEP + self.hostname |
| | 535 | string += BACKEND_SEP + str(self.sourceid) |
| | 536 | string += BACKEND_SEP + str(self.cardid) |
| | 537 | string += BACKEND_SEP + str(self.inputid) |
| | 538 | string += BACKEND_SEP + str(self.recpriority) |
| | 539 | string += BACKEND_SEP + str(self.recstatus) |
| | 540 | string += BACKEND_SEP + str(self.recordid) |
| | 541 | string += BACKEND_SEP + self.rectype |
| | 542 | string += BACKEND_SEP + self.dupin |
| | 543 | string += BACKEND_SEP + self.dupmethod |
| | 544 | string += BACKEND_SEP + str(int(mktime(self.recstartts.timetuple()))) |
| | 545 | string += BACKEND_SEP + str(int(mktime(self.recendts.timetuple()))) |
| | 546 | string += BACKEND_SEP + str(self.repeat) |
| | 547 | string += BACKEND_SEP + self.programflags |
| | 548 | string += BACKEND_SEP + self.recgroup |
| | 549 | string += BACKEND_SEP + str(self.commfree) |
| | 550 | string += BACKEND_SEP + self.outputfilters |
| | 551 | string += BACKEND_SEP + self.seriesid |
| | 552 | string += BACKEND_SEP + self.programid |
| | 553 | string += BACKEND_SEP + self.lastmodified |
| | 554 | string += BACKEND_SEP + str(self.stars) |
| | 555 | string += BACKEND_SEP + self.airdate |
| | 556 | string += BACKEND_SEP + str(self.hasairdate) |
| | 557 | string += BACKEND_SEP + self.playgroup |
| | 558 | string += BACKEND_SEP + str(self.recpriority2) |
| | 559 | string += BACKEND_SEP + self.parentid |
| | 560 | string += BACKEND_SEP + self.storagegroup |
| | 561 | string += BACKEND_SEP + self.audio_props |
| | 562 | string += BACKEND_SEP + self.video_props |
| | 563 | string += BACKEND_SEP + self.subtitle_type |
| | 564 | string += BACKEND_SEP + self.year |
| | 565 | |
| | 566 | return string |
| | 567 | |