#!/usr/bin/python
import socket, sys, string, select

debugging = 1

# Utility functions

def debug(str):
    if debugging:
        print str

def debug_on():
    global debugging
    debugging = 1

def debug_off():
    global debugging
    debugging = 0

def receive_reply(sock):
    ret = ""
    count = int(sock.recv(8))
    debug("REPLY LEN: %d" % count)
    while count > 0:
        ret = ret + sock.recv(65535)
        count = count - len(ret)
    debug("REPLY: '%s'" % ret)
    return ret

def send_request(sock, req):
    msg = "%-8d%s" % (len(req), req)
    debug("MESSAGE: '%s'" % msg)
    sock.send(msg)
    reply = receive_reply(sock)
    return reply

def send_message(sock, req):
    msg = "%-8d%s" % (len(req), req)
    debug("MESSAGE: '%s'" % msg)
    sock.send(msg)

# Classes (i.e. structures)

class connection:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.control = socket.socket(socket.AF_INET,
                                     socket.SOCK_STREAM)
        self.control.connect((self.host, self.port))
        reply = send_request(self.control,
                             "MYTH_PROTO_VERSION 1");
        debug("REPLY: '%s'" % reply)
        reply = send_request(self.control,
                             "ANN Playback %s 0" %
                             socket.gethostname())
        debug("REPLY: '%s'" % reply)
        if reply != "OK":
            print "Cannot establish player"
        reply = send_request(self.control,
                             "[]:[]QUERY_IS_ACTIVE_BACKEND[]:[]%s" %
                             socket.gethostname());
        debug("REPLY: '%s'" % reply)

class programinfo:
    # Program information, describes a recording in detail.
    # Used for file transfers.
    def __init__(self, r):
        spl = r.split("[]:[]")
        self.title = spl[0]
        self.subtitle = spl[1]
        self.description = spl[2]
        self.category = spl[3]
        self.chanId = spl[4]
        self.chanstr = spl[5]
        self.chansign = spl[6]
        self.channame = spl[7]
        self.pathname = spl[8]
        self.Start = long(spl[9])
        self.Length = long(spl[10])
        self.start_ts= spl[11]
        self.end_ts = spl[12]
        self.conflicting = int(spl[13])
        self.recording = int(spl[14])
        self.override = int(spl[15])
        self.hostname = spl[16]
        self.source_id = int(spl[17])
        self.card_id = int(spl[18])
        self.input_id = int(spl[19])
        self.rec_priority = spl[20]
        self.rec_status = int(spl[21])
        self.record_id = int(spl[22])
        self.rec_type = int(spl[23])
        self.rec_dups = int(spl[24])
        self.rec_start_ts = spl[25]
        self.rec_end_ts = spl[26]
        self.repeat = int(spl[27])
        self.program_flags = int(spl[28])
        self.FileName = self.pathname[7:].split("/")[-1]

class filetransfer:
    # File transfer information, used to gather the important parts of an
    # active file transfer.
    def __init__(self, conn, prog):
        self.err = 0
        self.have_stream = 0
        self.server = prog.hostname
        self.port = 6543
        self.data = 0
        self.id = ""
        self.start = 0
        self.end = 0

class recorder:
    # Recorder information, used to gather the important parts of an active
    # Live-TV download session.
    def __init__(self, r):
        self.err = 0
        self.have_stream = 0
        self.id = -1
        self.server = ""
        self.port = 0
        self.ring = 0
        self.data = 0
        self.framerate = 0.0
        parts = r.split("[]:[]")
        if len(parts) != 3:
            self.err = 1
            return
        self.id = parts[0]
        if self.id == "-1":
            self.err = 1
            return
        self.server = parts[1]
        self.port = int(parts[2])

class ringbuf:
    # Ring buffer description, part of Live-TV transfer
    def __init__(self, conn, rec):
        r = send_request(conn.control,
                         "QUERY_RECORDER %s[]:[]SETUP_RING_BUFFER[]:[]0"
                         % rec.id)
        debug("REPLY: '%s'" % r)
        parts = r.split("[]:[]")
        self.url = parts[0]
        self.id = parts[1]
        self.size = int(parts[2])
        self.start = int(parts[3])
        self.end = int(parts[4])

class recordernum:
    def __init__(self, r):
        self.id = -1
        self.host = ""
        self.port = -1
        spl = r.split("[]:[]")
        if len(spl) == 3:
            self.id = int(spl[0])
            self.host = spl[1]
            self.port = int(spl[2])

class chaninfo:
    # Channel information, describes the current channel on a Live-TV
    # transfer.  Includes program descriptive information about the current
    # show.
    def __init__(self, raw):
        spl = raw.split("[]:[]")
        self.title = spl[0]
        self.subtitle = spl[1]
        self.description = spl[2]
        self.category = spl[3]
        self.start_ts = spl[4]
        self.end_ts = spl[5]
        self.callsign = spl[6]
        self.iconpath = spl[7]
        self.channame = spl[8]
        self.chanid = spl[9]

class position:
    # Position information, used to locate key frames in a LiveTV stream.
    def __init__(self, keynum, value):
        self.keynum = long(keynum)
        self.value = long(value)

class freespace:
    def __init__(self, conn):
        r = send_request(conn.control, "QUERY_FREESPACE")
        spl = r.split("[];[]")
        debug("REPLY: '%s'" % r)
        self.total = long(spl[0])
        self.used = long(spl[1])

# recordernum functions
def recordernum_string(num):
    return "%d[]:[]%s[]:[]%d" % (num.id, num.host, num.port)


# Connection functions
def connection_shutdown(conn):
    conn.control.shutdown(2)
    conn.control.close()

def connection_check_block(conn, size):
    rfds = select.select([conn.control], [], [], 0)[0]
    if rfds > 0:
        reply = receive_reply(conn.control)
        debug("REPLY: '%s' size = %d" % (reply, size))
        if reply == str(size):
            return 1
    return 0

def connection_get_recorder_from_num(conn, num):
    # UNTESTED
    r = send_request(conn.control, "GET_RECORDER_FROM_NUM %s" %
                     recordernum_string(num))
    debug("REPLY: '%s'" % r)
    rec = recorder(r);
    if rec.err == 0:
        rec.ring = ringbuf(conn, rec)
    return rec

# Program Info Functions

def programinfo_string_list(prog):
    ret = str(prog.title)
    ret = ret + "[]:[]" + str(prog.subtitle)
    ret = ret + "[]:[]" + str(prog.description)
    ret = ret + "[]:[]" + str(prog.category)
    ret = ret + "[]:[]" + str(prog.chanId)
    ret = ret + "[]:[]" + str(prog.chanstr)
    ret = ret + "[]:[]" + str(prog.chansign)
    ret = ret + "[]:[]" + str(prog.channame)
    ret = ret + "[]:[]" + str(prog.pathname)
    ret = ret + "[]:[]" + str(prog.Start)
    ret = ret + "[]:[]" + str(prog.Length)
    ret = ret + "[]:[]" + str(prog.start_ts)
    ret = ret + "[]:[]" + str(prog.end_ts)
    ret = ret + "[]:[]" + str(prog.conflicting)
    ret = ret + "[]:[]" + str(prog.recording)
    ret = ret + "[]:[]" + str(prog.override)
    ret = ret + "[]:[]" + str(prog.hostname)
    ret = ret + "[]:[]" + str(prog.source_id)
    ret = ret + "[]:[]" + str(prog.card_id)
    ret = ret + "[]:[]" + str(prog.input_id)
    ret = ret + "[]:[]" + str(prog.rec_priority)
    ret = ret + "[]:[]" + str(prog.rec_status)
    ret = ret + "[]:[]" + str(prog.record_id)
    ret = ret + "[]:[]" + str(prog.rec_type)
    ret = ret + "[]:[]" + str(prog.rec_dups)
    ret = ret + "[]:[]" + str(prog.rec_start_ts)
    ret = ret + "[]:[]" + str(prog.rec_end_ts)
    ret = ret + "[]:[]" + str(prog.repeat)
    ret = ret + "[]:[]" + str(prog.program_flags)
    ret = ret + "[]:[]nothing"
    return ret

def programinfo_list(conn):
    ret = []
    s = send_request(conn.control,
                     "QUERY_RECORDINGS Play")
    debug("REPLY: '%s'" % s)
    spl = s.split("[]:[]", 1)
    count = int(spl[0]);
    while count > 0:
        s = spl[-1]
        spl = s.split("[]:[]",29)
        ret = ret + [programinfo(string.join(spl[0:29], "[]:[]"))]
        count = count - 1
    return ret

def programinfo_stop_recording(conn, prog):
    # UNTESTED
    r = send_request(conn.control, "STOP_RECORDING %s" %
                     programinfo_string_list(prog));
    debug("REPLY: '%s'" % r)

def programinfo_check_recording(conn, prog):
    r = send_request(conn.control, "CHECK_RECORDING %s" %
                     programinfo_string_list(prog));
    debug("REPLY: '%s'" % r)
    return int(r)

def programinfo_delete_recording(conn, prog):
    # UNTESTED
    r = send_request(conn.control, "DELETE_RECORDING %s" %
                     programinfo_string_list(prog));
    debug("REPLY: '%s'" % r)
    if (r[0:3] == "BAD"):
        r = "-1"
    return int(r)

def programinfo_forget_recording(conn, prog):
    # UNTESTED
    r = send_request(conn.control, "FORGET_RECORDING %s" %
                     programinfo_string_list(prog));
    debug("REPLY: '%s'" % r)
    return int(r)

def programinfo_getallpending(conn):
    # UNTESTED
    ret = []
    s = send_request(conn.control,
                     "QUERY_GETALLPENDING")
    debug("REPLY: '%s'" % s)
    spl = s.split("[]:[]", 1)
    count = int(spl[0]);
    while count > 0:
        s = spl[-1]
        spl = s.split("[]:[]",29)
        ret = ret + [programinfo(string.join(spl[0:29], "[]:[]"))]
        count = count - 1
    return ret

def programinfo_getallscheduled(conn):
    # UNTESTED
    ret = []
    s = send_request(conn.control,
                     "QUERY_GETALLSCHEDULED")
    debug("REPLY: '%s'" % s)
    spl = s.split("[]:[]", 1)
    count = int(spl[0]);
    while count > 0:
        s = spl[-1]
        spl = s.split("[]:[]",29)
        ret = ret + [programinfo(string.join(spl[0:29], "[]:[]"))]
        count = count - 1
    return ret

def programinfo_getconflicting(conn):
    # UNTESTED
    ret = []
    s = send_request(conn.control,
                     "QUERY_GETCONFLICTING")
    debug("REPLY: '%s'" % s)
    spl = s.split("[]:[]", 1)
    count = int(spl[0]);
    while count > 0:
        s = spl[-1]
        spl = s.split("[]:[]",29)
        ret = ret + [programinfo(string.join(spl[0:29], "[]:[]"))]
        count = count - 1
    return ret

def programinfo_get_recorder_num(conn, prog):
    # UNTESTED
    r = send_request(conn.control, "GET_RECORDER_NUM %s" %
                     programinfo_string_list(prog));
    debug("REPLY: '%s'" % r)
    return recordernum(r)

def programinfo_fill_program_info(conn, hostname):
    # UNTESTED
    r = send_request(conn.control, "QUERY_GEN_PIXMAP %s" %
                     hostname)
    debug("REPLY: '%s'" % r)
    return programinfo(r)

# Filetransfer functions

def filetransfer_start_stream(conn, ft, prog):
    ft.data = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    ft.data.connect((ft.server, ft.port))
    r = send_request(ft.data,
                     "ANN FileTransfer %s[]:[]/%s" %
                     (socket.gethostname(), prog.FileName))
    debug("REPLY: '%s'" % r)
    spl = r.split("[]:[]")
    if spl[0] != "OK":
        print "ANN FileTransfer failed"
        ft.data.close()
        return -1
    ft.id = spl[1]
    ft.start = long(spl[2])
    ft.end = long(spl[3])
    ft.have_stream = 1
    return 0

def filetransfer_end_stream(conn, ft):
    reply = send_request(conn.control,
                         "QUERY_FILETRANSFER %s[]:[]DONE" %
                         ft.id)
    debug("REPLY: '%s'" % reply)
    reply = send_request(conn.control,
                         "QUERY_IS_ACTIVE_BACKEND[]:[]%s" %
                         socket.gethostname())
    debug("REPLY: '%s'" % reply)

    # All done, close the file and return
    ft.data.shutdown(2)
    ft.data.close()
    ft.have_stream = 0

def filetransfer_request_block(conn, ft, size):
    send_message(conn.control,
                 "QUERY_FILETRANSFER %s[]:[]REQUEST_BLOCK[]:[]%d" %
                 (ft.id, size))

def checkfile(conn, prog):
    reply = send_request(conn.control,
                         "QUERY_CHECKFILE[]:[]%s" %
                         programinfo_string_list(prog))
    debug("REPLY: '%s'" % reply)
    return int(reply) == 1
    
# Recorder functions

def recorder_request_block(conn, rec, size):
    send_message(conn.control,
                 "QUERY_RECORDER %s[]:[]REQUEST_BLOCK_RINGBUF[]:[]%d" %
                 (rec.id, size))

def recorder_is_recording(conn, rec):
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]IS_RECORDING" % rec.id)
    debug("REPLY: %s" % r)
    return int(r) == 1

def recorder_get_framerate(conn, rec):
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]GET_FRAMERATE" % rec.id)
    debug("REPLY: %s" % r)
    return float(r)

def recorder_get_frames_written(conn, rec):
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]GET_FRAMES_WRITTEN" % rec.id)
    debug("REPLY: %s" % r)
    spl = r.split("[]:[]")
    if (int(spl[0]) < 0):
        return long(-1)
    return long(spl[1])

def recorder_get_free_space(conn, rec):
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]GET_FREE_SPACE" % rec.id)
    debug("REPLY: %s" % r)
    spl = r.split("[]:[]")
    if (int(spl[0]) < 0):
        return long(-1)
    return long(spl[1])

def recorder_get_keyframe_pos(conn, rec, keynum):
    # UNTESTED
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]GET_KEYFRAME_POS[]:[]%d" %
                     (rec.id, keynum))
    debug("REPLY: %s" % r)
    spl = r.split("[]:[]")
    if (int(spl[0]) < 0):
        return long(-1)
    return long(spl[1])

def recorder_fill_position_map(conn, rec, start, end):
    # UNTESTED
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]FILL_POSITION_MAP[]:[]%d[]:[]%d" %
                     (rec.id, start, end))
    debug("REPLY: %s" % r)
    ret = []
    if r == "":
        return ret
    spl = r.split("[]:[]")
    while len(spl) > 2:
        ret = ret + [position(spl[0], spl[1])]
        spl = spl[2:]
    return ret

def recorder_get_recording(conn, rec):
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]GET_RECORDING" %
                     rec.id)
    debug("REPLY: %s" % r)
    return programinfo(r)

def recorder_stop_playing(conn, rec):
    # UNTESTED
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]STOP_PLAYING" %
                     rec.id)
    debug("REPLY: %s" % r)

def recorder_frontend_ready(conn, rec):
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]FRONTEND_READY" %
                     rec.id)
    debug("REPLY: %s" % r)

def recorder_cancel_next_recording(conn, rec):
    # UNTESTED
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]CANCEL_NEXT_RECORDING" %
                     rec.id)
    debug("REPLY: %s" % r)

def recorder_pause(conn, rec):
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]PAUSE" % rec.id)
    debug("REPLY: %s" % r)

def recorder_finish_recording(conn, rec):
    # UNTESTED
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]FINISH_RECORDING" % rec.id)
    debug("REPLY: %s" % r)

def recorder_toggle_channel_favorite(conn, rec):
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]TOGGLE_CHANNEL_FAVORITE" % rec.id)
    debug("REPLY: %s" % r)

# Channel direction values for Change Channel
CHANNEL_DIRECTION_UP = 0       # Change chanel up
CHANNEL_DIRECTION_DOWN = 1     # Change channel down
CHANNEL_DIRECTION_FAVORITE = 3 # Flip between favorite channels
CHANNEL_DIRECTION_SAME = 4     # Stay where you are (not used, treated as down)
def recorder_change_channel(conn, rec, direction):
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]CHANGE_CHANNEL[]:[]%d" %
                     (rec.id, direction))
    debug("REPLY: %s" % r)

def recorder_set_channel(conn, rec, channame):
    # Change channels absolutely (by name)
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]SET_CHANNEL[]:[]%s" %
                     (rec.id, channame))
    debug("REPLY: %s" % r)

# Adjustment directions for change {color, brightness, contrast, hue}
UP = 1   # Adjust it up one notch
DOWN = 0 # Adjust it down one notch

def recorder_change_color(conn, rec, direction):
    # Adjust color saturation up [1] or down [0]
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]CHANGE_COLOUR[]:[]%d" %
                     (rec.id, direction))
    debug("REPLY: %s" % r)
    return int(r)

def recorder_change_brightness(conn, rec, direction):
    # Adjust brightness (black level)  up [1] or down [0]
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]CHANGE_BRIGHTNESS[]:[]%d" %
                     (rec.id, direction))
    debug("REPLY: %s" % r)
    return int(r)

def recorder_change_contrast(conn, rec, direction):
    # Adjust contrast (white level)  up [1] or down [0]
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]CHANGE_CONTRAST[]:[]%d" %
                     (rec.id, direction))
    debug("REPLY: %s" % r)
    return int(r)

def recorder_change_hue(conn, rec, direction):
    # Adjust hue  up [1] or down [0]
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]CHANGE_HUE[]:[]%d" %
                     (rec.id, direction))
    debug("REPLY: %s" % r)
    return int(r)

def recorder_check_channel(conn, rec, channame):
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]CHECK_CHANNEL[]:[]%s" %
                     (rec.id, channame))
    debug("REPLY: %s" % r)
    return int(r) == 1

def recorder_check_channel_prefix(conn, rec, channame):
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]CHECK_CHANNEL_PREFIX[]:[]%s" %
                     (rec.id, channame))
    debug("REPLY: %s" % r)
    spl = r.split("[]:[]")
    if int(spl[0]) < 0:
        return -1
    return int(spl[1]) == 1

# Direction values for Get Next Program
BROWSE_SAME = 0     # Stay where you are
BROWSE_UP = 1       # Channel up, same time slot
BROWSE_DOWN = 2     # Channel down, same time slot
BROWSE_LEFT = 3     # Channel same, earlier time slot
BROWSE_RIGHT = 4    # Channel same, later time slot
BROWSE_FAVORITE = 5 # Not quite sure, I think this flips
                    # between tagged favorites
def recorder_get_next_program_info(conn, rec, cinfo, direction):
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]GET_NEXT_PROGRAM_INFO[]:[]%s[]:[]%s[]:[]%s[]:[]%s" %
                     (rec.id, cinfo.channame, cinfo.chanid, direction,
                      cinfo.start_ts))
    debug("REPLY: %s" % r)
    return chaninfo(r)

def recorder_get_program_info(conn, rec):
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]GET_PROGRAM_INFO" %
                     rec.id)
    debug("REPLY: %s" % r)
    return chaninfo(r)

def recorder_get_input_name(conn, rec):
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]GET_INPUT_NAME" %
                     rec.id)
    debug("REPLY: %s" % r)
    return r

def recorder_seek(conn, rec, pos, whence, curpos):
    r = send_request(conn.control,
                     "QUERY_RECORDER %s[]:[]SEEK_RINGBUF[]:[]%ld[]:[]0[]:[]%ld[]:[]%ld[]:[]0" %
                     (rec.id, pos, whence, curpos))
    debug("REPLY: %s" % r)
    return long(r.split("[]:[]")[0])

def recorder_spawn_livetv(conn, rec):
    r = send_request(conn.control, "QUERY_RECORDER %s[]:[]SPAWN_LIVETV" %
                     rec.id)
    debug("REPLY: '%s'" % r)
    if r != "ok":
        return -1
    return 0

def recorder_get_free_recorder(conn):
        r = send_request(conn.control, "GET_FREE_RECORDER")
        debug("REPLY: '%s'" % r)
        rec = recorder(r);
        rec.ring = ringbuf(conn, rec)
        return rec

def recorder_start_stream(conn, rec):
    rec.data = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    rec.data.connect((rec.server, rec.port))
    r = send_request(rec.data, "ANN RingBuffer %s %s" %
                     (socket.gethostname(), rec.id))
    debug("REPLY: '%s'" % r)
    if r != "OK":
        print "Announce recorder failed"
        rec.data.close()
        return -1
    if recorder_spawn_livetv(conn, rec) < 0:
        print "SPAWN LIVETV Failed"
        rec.data.close()
        return -1
    if recorder_is_recording(conn, rec) == 0:
        print "Not recording"
        rec.data.close()
        return -1
    rec.frame_rate = recorder_get_framerate(conn, rec)
    rec.have_stream = 1
    return 0

def recorder_end_stream(conn, rec):
    reply = send_request(conn.control,
                         "QUERY_RECORDER %s[]:[]STOP_LIVETV" %
                         rec.id)
    debug("REPLY: '%s'" % reply)
    reply = send_request(conn.control,
                         "QUERY_RECORDER %s[]:[]DONE_RINGBUF" %
                         rec.id)
    debug("REPLY: '%s'" % reply)
    reply = send_request(conn.control,
                         "QUERY_IS_ACTIVE_BACKEND[]:[]%s" %
                         socket.gethostname())
    debug("REPLY: '%s'" % reply)
    rec.data.shutdown(2)
    rec.data.close()
    rec.have_stream = 0
    rec.frame_rate = 0.0

# Transfer Functions (file and live-tv)

def transfer_file(conn, prog):
    ft = filetransfer(conn, prog)
    if filetransfer_start_stream(conn, ft, prog) < 0:
        return
    out = file("/tmp/" + prog.FileName, "w+b")
    remaining = long(prog.Length)
    block = 256000;
    while remaining > 0:
        count = 0
        if (remaining < block):
            block = remaining

        # Ask for a block of data
        filetransfer_request_block(conn, ft, block)

        # Now read down the block of data a chunk at a time
        while count < block:
            reply = ft.data.recv(block - count)
            length = len(reply)
            count = count + length
            remaining = remaining - length
            if length > 0:
                # If we got anything, write it out, otherwise, just go
                # back for more.
                out.write(reply)

        # Read back a block end indicator, it should be '13      256000'
        if connection_check_block(conn, block) == 0:
            print "SHOULD BE BLOCK END BUT IS NOT!!!"
    # All done
    filetransfer_end_stream(conn, ft)
    out.close()

def transfer_live(conn, length):
    rec = recorder_get_free_recorder(conn)
    if rec.err:
        print "error getting recorder"
        return
    if recorder_start_stream(conn, rec) < 0:
        return
    prog = recorder_get_program_info(conn, rec)
    print "Channel %s: %s -- %s" % (
        prog.channame, prog.title, prog.description)
    out = file("/tmp/recording.mpg", "w+b")
    remaining = long(length)
    block = 256000;
    recorder_seek(conn, rec, 0L, 0L, 0L)
    while remaining > 0:
        # Ask for a block of data
        count = 0
        if (remaining < block):
            block = remaining
        recorder_request_block(conn, rec, block)

        # Now read down the block of data a chunk at a time
        while count < block:
            reply = rec.data.recv(block - count)
            length = len(reply)
            count = count + length
            remaining = remaining - length
            if length > 0:
                # If we got anything, write it out,
                # otherwise, just go back for more.
                out.write(reply)

        if connection_check_block(conn, block) == 0:
            print "SHOULD BE AT BLOCK END BUT NOT!!!"

    # All done...
    recorder_end_stream(conn, rec)
    out.close()

def main():
    # Set up a connection to the main backend server (mediaserve is my backend
    # server, substitute your own backed server name here)
    conn = connection("mediaserve", 6543)

    # Get and display a list of recorded programs
    list = programinfo_list(conn)
    for p in list:
        print ("%s: %s [%s] - %s" %
               (p.channame, p.title, p.description, p.start_ts))

    # If the list contained at least one recording, transfer it here
    if len(list) > 0:
        transfer_file(conn, list[0])

    # Grab a short sample of live tv (1 megabyte)
    transfer_live(conn, 1024*1024)

    # terminate the connection with the backend server
    connection_shutdown(conn)

# Run Main

main()
