import math FILE_READ_CHUNK = 512 * 1024 def chunk_sequence(sequence, chunk_length, allow_incomplete=True): ''' Given a sequence, divide it into sequences of length `chunk_length`. allow_incomplete: If True, allow the final chunk to be shorter if the given sequence is not an exact multiple of `chunk_length`. If False, the incomplete chunk will be discarded. ''' (complete, leftover) = divmod(len(sequence), chunk_length) if not allow_incomplete: leftover = 0 chunk_count = complete + min(leftover, 1) chunks = [] for x in range(chunk_count): left = chunk_length * x right = left + chunk_length chunks.append(sequence[left:right]) return chunks def comma_split(s): ''' Split the string apart by commas, discarding all extra whitespace and blank phrases. ''' if s is None: return s s = s.replace(' ', ',') s = [x.strip() for x in s.split(',')] s = [x for x in s if x] return s def edit_params(original, modifications): ''' Given a dictionary representing URL parameters, apply the modifications and return a URL parameter string. {'a':1, 'b':2}, {'b':3} => ?a=1&b=3 ''' new_params = original.copy() new_params.update(modifications) if not new_params: return '' new_params = ['%s=%s' % (k, v) for (k, v) in new_params.items() if v] new_params = '&'.join(new_params) if new_params: new_params = '?' + new_params return new_params def fit_into_bounds(image_width, image_height, frame_width, frame_height): ''' Given the w+h of the image and the w+h of the frame, return new w+h that fits the image into the frame while maintaining the aspect ratio. ''' ratio = min(frame_width/image_width, frame_height/image_height) new_width = int(image_width * ratio) new_height = int(image_height * ratio) return (new_width, new_height) def hms_to_seconds(hms): ''' Convert hh:mm:ss string to an integer seconds. ''' hms = hms.split(':') seconds = 0 if len(hms) == 3: seconds += int(hms[0])*3600 hms.pop(0) if len(hms) == 2: seconds += int(hms[0])*60 hms.pop(0) if len(hms) == 1: seconds += int(hms[0]) return seconds def is_xor(*args): ''' Return True if and only if one arg is truthy. ''' return [bool(a) for a in args].count(True) == 1 def read_filebytes(filepath, range_min, range_max): ''' Yield chunks of bytes from the file between the endpoints. ''' range_span = range_max - range_min #print('read span', range_min, range_max, range_span) f = open(filepath, 'rb') f.seek(range_min) sent_amount = 0 with f: while sent_amount < range_span: #print(sent_amount) chunk = f.read(FILE_READ_CHUNK) if len(chunk) == 0: break yield chunk sent_amount += len(chunk) def seconds_to_hms(seconds): ''' Convert integer number of seconds to an hh:mm:ss string. Only the necessary fields are used. ''' seconds = math.ceil(seconds) (minutes, seconds) = divmod(seconds, 60) (hours, minutes) = divmod(minutes, 60) parts = [] if hours: parts.append(hours) if hours or minutes: parts.append(minutes) parts.append(seconds) hms = ':'.join('%02d' % part for part in parts) return hms def truthystring(s): if isinstance(s, (bool, int)) or s is None: return s s = s.lower() if s in {'1', 'true', 't', 'yes', 'y', 'on'}: return True if s in {'null', 'none'}: return None return False