Source code for pytomo.flvlib.astypes

import os
import time
import datetime
import logging

from primitives import *
from constants import *
from helpers import FixedOffset, Local, OrderedAttrDict


"""
The AS types and their FLV representations.
"""

log = logging.getLogger('flvlib.astypes')


[docs]class MalformedFLV(Exception): pass # Number
[docs]def get_number(f, max_offset=None): return get_double(f)
[docs]def make_number(num): return make_double(num) # Boolean
[docs]def get_boolean(f, max_offset=None): value = get_ui8(f) return bool(value)
[docs]def make_boolean(value): return make_ui8((value and 1) or 0) # String
[docs]def get_string(f, max_offset=None): # First 16 bits are the string's length length = get_ui16(f) # Then comes the string itself ret = f.read(length) return ret
[docs]def make_string(string): if isinstance(string, unicode): # We need a blob, not unicode. Arbitrarily choose UTF-8 string = string.encode('UTF-8') length = make_ui16(len(string)) return length + string # Longstring
[docs]def get_longstring(f, max_offset=None): # First 32 bits are the string's length length = get_ui32(f) # Then comes the string itself ret = f.read(length) return ret
[docs]def make_longstring(string): if isinstance(string, unicode): # We need a blob, not unicode. Arbitrarily choose UTF-8 string = string.encode('UTF-8') length = make_ui32(len(string)) return length + string # ECMA Array
[docs]class ECMAArray(OrderedAttrDict): pass
[docs]def get_ecma_array(f, max_offset=None): length = get_ui32(f) log.debug("The ECMA array has approximately %d elements", length) array = ECMAArray() while True: if max_offset and (f.tell() == max_offset): log.debug("Prematurely terminating reading an ECMA array") break marker = get_ui24(f) if marker == 9: log.debug("Marker!") break else: f.seek(-3, os.SEEK_CUR) name, value = get_script_data_variable(f, max_offset=max_offset) array[name] = value return array
[docs]def make_ecma_array(d): length = make_ui32(len(d)) rest = ''.join([make_script_data_variable(name, value) for name, value in d.iteritems()]) marker = make_ui24(9) return length + rest + marker # Strict Array
[docs]def get_strict_array(f, max_offset=None): length = get_ui32(f) log.debug("The length is %d", length) elements = [get_script_data_value(f, max_offset=max_offset) for _ in xrange(length)] return elements
[docs]def make_strict_array(l): ret = make_ui32(len(l)) rest = ''.join([make_script_data_value(value) for value in l]) return ret + rest # Date
[docs]def get_date(f, max_offset=None): timestamp = get_number(f) / 1000.0 time_offset = get_si16(f) tz = FixedOffset(time_offset, "FLV Fixed Offset") return datetime.datetime.fromtimestamp(timestamp, tz)
[docs]def make_date(date): if date.tzinfo: local_date = date.astimezone(Local) else: local_date = date ret = make_number(time.mktime(local_date.timetuple()) * 1000) time_offset = date.utcoffset() if time_offset is not None: offset = time_offset.days * 24 * 26 + time_offset.seconds / 60 else: offset = 0 return ret + make_si16(offset) # Null
[docs]def get_null(f, max_offset=None): return None
[docs]def make_null(none): return '' # Object
[docs]class FLVObject(OrderedAttrDict): pass
[docs]def get_object(f, max_offset=None): ret = FLVObject() while True: if max_offset and (f.tell() == max_offset): log.debug("Prematurely terminating reading an object") break marker = get_ui24(f) if marker == 9: log.debug("Marker!") break else: f.seek(-3, os.SEEK_CUR) name, value = get_script_data_variable(f) setattr(ret, name, value) return ret
[docs]def make_object(obj): # If the object is iterable, serialize keys/values. If not, fall # back on iterating over __dict__. # This makes sure that make_object(get_object(StringIO(blob))) == blob try: iterator = obj.iteritems() except AttributeError: iterator = obj.__dict__.iteritems() ret = ''.join([make_script_data_variable(name, value) for name, value in iterator]) marker = make_ui24(9) return ret + marker # MovieClip
[docs]class MovieClip(object): def __init__(self, path): self.path = path def __eq__(self, other): return isinstance(other, MovieClip) and self.path == other.path def __repr__(self): return "<MovieClip at %s>" % self.path
[docs]def get_movieclip(f, max_offset=None): ret = get_string(f) return MovieClip(ret)
[docs]def make_movieclip(clip): return make_string(clip.path) # Undefined
[docs]class Undefined(object): def __eq__(self, other): return isinstance(other, Undefined) def __repr__(self): return '<Undefined>'
[docs]def get_undefined(f, max_offset=None): return Undefined()
[docs]def make_undefined(undefined): return '' # Reference
[docs]class Reference(object): def __init__(self, ref): self.ref = ref def __eq__(self, other): return isinstance(other, Reference) and self.ref == other.ref def __repr__(self): return "<Reference to %d>" % self.ref
[docs]def get_reference(f, max_offset=None): ret = get_ui16(f) return Reference(ret)
[docs]def make_reference(reference): return make_ui16(reference.ref)
as_type_to_getter_and_maker = { VALUE_TYPE_NUMBER: (get_number, make_number), VALUE_TYPE_BOOLEAN: (get_boolean, make_boolean), VALUE_TYPE_STRING: (get_string, make_string), VALUE_TYPE_OBJECT: (get_object, make_object), VALUE_TYPE_MOVIECLIP: (get_movieclip, make_movieclip), VALUE_TYPE_NULL: (get_null, make_null), VALUE_TYPE_UNDEFINED: (get_undefined, make_undefined), VALUE_TYPE_REFERENCE: (get_reference, make_reference), VALUE_TYPE_ECMA_ARRAY: (get_ecma_array, make_ecma_array), VALUE_TYPE_STRICT_ARRAY: (get_strict_array, make_strict_array), VALUE_TYPE_DATE: (get_date, make_date), VALUE_TYPE_LONGSTRING: (get_longstring, make_longstring) } type_to_as_type = { bool: VALUE_TYPE_BOOLEAN, int: VALUE_TYPE_NUMBER, long: VALUE_TYPE_NUMBER, float: VALUE_TYPE_NUMBER, # WARNING: not supporting Longstrings here. # With a max length of 65535 chars, noone will notice. str: VALUE_TYPE_STRING, unicode: VALUE_TYPE_STRING, list: VALUE_TYPE_STRICT_ARRAY, dict: VALUE_TYPE_ECMA_ARRAY, ECMAArray: VALUE_TYPE_ECMA_ARRAY, datetime.datetime: VALUE_TYPE_DATE, Undefined: VALUE_TYPE_UNDEFINED, MovieClip: VALUE_TYPE_MOVIECLIP, Reference: VALUE_TYPE_REFERENCE, type(None): VALUE_TYPE_NULL } # SCRIPTDATAVARIABLE
[docs]def get_script_data_variable(f, max_offset=None): name = get_string(f) log.debug("The name is %s", name) value = get_script_data_value(f, max_offset=max_offset) log.debug("The value is %r", value) return (name, value)
[docs]def make_script_data_variable(name, value): log.debug("The name is %s", name) log.debug("The value is %r", value) ret = make_string(name) + make_script_data_value(value) return ret # SCRIPTDATAVALUE
[docs]def get_script_data_value(f, max_offset=None): value_type = get_ui8(f) log.debug("The value type is %r", value_type) try: get_value = as_type_to_getter_and_maker[value_type][0] except KeyError: raise MalformedFLV("Invalid script data value type: %d", value_type) log.debug("The getter function is %r", get_value) value = get_value(f, max_offset=max_offset) return value
[docs]def make_script_data_value(value): value_type = type_to_as_type.get(value.__class__, VALUE_TYPE_OBJECT) log.debug("The value type is %r", value_type) # KeyError can't happen here, because we always fall back on # VALUE_TYPE_OBJECT when determining value_type make_value = as_type_to_getter_and_maker[value_type][1] log.debug("The maker function is %r", make_value) type_tag = make_ui8(value_type) ret = make_value(value) return type_tag + ret