| 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(self,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(self,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 | |