def swap(range_A, range_B): '''Swaps the items contained in one range with the items contained in the other range. Return value: Nothing meaningful. ''' A = split_range(range_A) B = split_range(range_B) data.lock.acquire() try: # Normalize the case where negative numbers are used as range indices. n = len(data.song_queue) if A[0] < 0: A[0] = n - A[0] if A[1] < 0: A[1] = n - A[1] if B[0] < 0: B[0] = n - B[0] if B[1] < 0: B[1] = n - B[1] # Forbid overlapping ranges. if is_overlapping(A, B, len(data.song_queue)): raise ValueError("Overlapping ranges may not be swapped: %s %s" % (A, B)) # Make sure range A is closer to the head of the queue than range B. if A > B: A, B = B, A # Split the queue into various slices, delineated by the given ranges. prefix = data.song_queue[:A[0]] slice_A = data.song_queue[A[0]:A[1]] infix = data.song_queue[A[1]:B[0]] slice_B = data.song_queue[B[0]:B[1]] suffix = data.song_queue[B[1]:] # Piece the slices back together, swapping A with B. data.song_queue = prefix + slice_B + infix + slice_A + suffix finally: data.last_queue_update = time.time() data.lock.release() return True
def indexed_list(range=()): '''Lists the song queue's contents. If a range is specified, only the items that fall within that range are listed. This differs from list() only in its return value, and is useful when you want to know the starting position of your selected range within the song queue (which can be different than the starting index of the specified range if, for example, the starting index is a negative integer). Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is returned. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: A struct with two elements. This first is "list", an array of (base64-encoded) strings, representing the selected range from the song queue's contents. The second is "start", an integer index value that represents the position of the first item of the returned list in the song queue. ''' start, end = split_range(range) list = [Binary(i.encode()) for i in data.song_queue[start:end]] start_index = start if start_index < 0: start_index = len(data.song_queue) + start_index if start_index < 0: start_index = 0 return {'start': start_index, 'list': list}
def replace_range(range, items): '''Replaces a slice of the contents of the queue with the given items. This is equivalent to calling cut() and prepend() in succession, except that this operation is atomic. Argument: The first is an array of integers that represents a range. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. * The second argument is an array of (base64-encoded) strings, representing the items to be added. * When adding local filenames to the queue, only absolute pathnames should be used. Using relative pathnames would be foolish because the server isn't aware of the client's current working directory. Return value: Nothing meaningful. ''' start, end = split_range(range) for i in items: if not hasattr(i, 'data'): raise TypeError("Objects of type '%s' cannot be inserted." % \ i.__class__.__name__) data.lock.acquire() try: data.song_queue[start:end] = [ _f for _f in [str(i.data.decode()) for i in items] if _f ] finally: data.last_queue_update = time.time() data.lock.release() return True
def sub(pattern, replace, range=()): '''Performs a regular expression substitution on the items in the queue. Arguments: The first is a (base64-encoded) regular expression that specifies the text to be replaced. * The second argument is the (base64-encoded) string that will be used to replace the first occurrence of the regular expression within each queue item. Any backslash escapes in this string will be processed, including special character translation (e.g. "\\n" to newline) and backreferences to groups within the match. * Optionally, an array of integers may be given as a third argument. This argument represents a range to which the substitution will be limited. This range is interpreted in the same way as the range argument in other Moosic methods. * If performing a replacement changes an item in the queue into the empty string, then it is removed from the queue. Return value: Nothing meaningful. ''' start, end = split_range(range) if hasattr(pattern, 'data'): pattern = str(pattern.data) if hasattr(replace, 'data'): replace = str(replace.data) pattern = re.compile(pattern) data.lock.acquire() try: data.song_queue[start:end] = filter(None, [pattern.sub(replace, item, 1) for item in data.song_queue[start:end]]) finally: data.last_queue_update = time.time() data.lock.release() return True
def filter_(regexp, range=()): '''Removes all items that don't match the given regular expression. Arguments: A regular expression that specifies which items to keep. * Optionally, an array of integers may be given as a second argument. This argument represents a range to which the filtering will be limited. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. ''' start, end = split_range(range) data.lock.acquire() try: if hasattr(regexp, 'data'): regexp = regexp.data.decode() data.song_queue[start:end] = grep(regexp, data.song_queue[start:end]) finally: data.last_queue_update = time.time() data.lock.release() return True
def filter_(regexp, range=()): '''Removes all items that don't match the given regular expression. Arguments: A regular expression that specifies which items to keep. * Optionally, an array of integers may be given as a second argument. This argument represents a range to which the filtering will be limited. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. ''' start, end = split_range(range) data.lock.acquire() try: if hasattr(regexp, 'data'): regexp = regexp.data data.song_queue[start:end] = grep(regexp, data.song_queue[start:end]) finally: data.last_queue_update = time.time() data.lock.release() return True
def indexed_list(range=()): '''Lists the song queue's contents. If a range is specified, only the items that fall within that range are listed. This differs from list() only in its return value, and is useful when you want to know the starting position of your selected range within the song queue (which can be different than the starting index of the specified range if, for example, the starting index is a negative integer). Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is returned. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: A struct with two elements. This first is "list", an array of (base64-encoded) strings, representing the selected range from the song queue's contents. The second is "start", an integer index value that represents the position of the first item of the returned list in the song queue. ''' start, end = split_range(range) list = [Binary(i) for i in data.song_queue[start:end]] start_index = start if start_index < 0: start_index = len(data.song_queue) + start_index if start_index < 0: start_index = 0 return {'start':start_index, 'list':list}
def replace_range(range, items): '''Replaces a slice of the contents of the queue with the given items. This is equivalent to calling cut() and prepend() in succession, except that this operation is atomic. Argument: The first is an array of integers that represents a range. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. * The second argument is an array of (base64-encoded) strings, representing the items to be added. * When adding local filenames to the queue, only absolute pathnames should be used. Using relative pathnames would be foolish because the server isn't aware of the client's current working directory. Return value: Nothing meaningful. ''' start, end = split_range(range) for i in items: if not hasattr(i, 'data'): raise TypeError("Objects of type '%s' cannot be inserted." % \ i.__class__.__name__) data.lock.acquire() try: data.song_queue[start:end] = filter(None, [str(i.data) for i in items]) finally: data.last_queue_update = time.time() data.lock.release() return True
def list(range=()): '''Lists the song queue's contents. If a range is specified, only the items that fall within that range are listed. Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is returned. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: An array of (base64-encoded) strings, representing the selected range from the song queue's contents. ''' start, end = split_range(range) return [Binary(i) for i in data.song_queue[start:end]]
def list(range=()): '''Lists the song queue's contents. If a range is specified, only the items that fall within that range are listed. Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is returned. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: An array of (base64-encoded) strings, representing the selected range from the song queue's contents. ''' start, end = split_range(range) return [Binary(i.encode()) for i in data.song_queue[start:end]]
def cut(range): '''Remove all queued items that fall within the given range. Arguments: An array of integers that represents a range. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. ''' start, end = split_range(range) data.lock.acquire() try: del data.song_queue[start:end] finally: data.last_queue_update = time.time() data.lock.release() return True
def crop(range): '''Remove all queued items that do not fall within the given range. Arguments: An array of integers that represents a range. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. ''' start, end = split_range(range) data.lock.acquire() try: data.song_queue = data.song_queue[start:end] finally: data.last_queue_update = time.time() data.lock.release() return True
def move(range, dest): '''Moves a range of items to a new position within the queue. Arguments: The first argument is an array of integers that represents a range of items to be moved. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. * The second argument, "destination", specifies the position in the queue where the items will be moved. Return value: Nothing meaningful. ''' start, end = split_range(range) # Use an out-of-bound piece of data to mark the old positions of moved # items. Since the playlist normally only contains strings, any non-string # value will work as an effective marker. mark = None data.lock.acquire() try: # Copy the items to be moved. stuff_to_move = data.song_queue[start:end] # "Delete" the items from their old position by replacing them with # marker values. Regular removal isn't done at this point because we # don't want to invalidate the meaning of our destination index. data.song_queue[start:end] = [mark] * len(stuff_to_move) # Place the collected items at their destination. data.song_queue[dest:dest] = stuff_to_move # Remove the markers. data.song_queue = [ item for item in data.song_queue if item is not mark ] finally: data.last_queue_update = time.time() data.lock.release() return True
def move(range, dest): '''Moves a range of items to a new position within the queue. Arguments: The first argument is an array of integers that represents a range of items to be moved. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. * The second argument, "destination", specifies the position in the queue where the items will be moved. Return value: Nothing meaningful. ''' start, end = split_range(range) # Use an out-of-bound piece of data to mark the old positions of moved # items. Since the playlist normally only contains strings, any non-string # value will work as an effective marker. mark = None data.lock.acquire() try: # Copy the items to be moved. stuff_to_move = data.song_queue[start:end] # "Delete" the items from their old position by replacing them with # marker values. Regular removal isn't done at this point because we # don't want to invalidate the meaning of our destination index. data.song_queue[start:end] = [mark]*len(stuff_to_move) # Place the collected items at their destination. data.song_queue[dest:dest] = stuff_to_move # Remove the markers. data.song_queue = [item for item in data.song_queue if item is not mark] finally: data.last_queue_update = time.time() data.lock.release() return True
def reverse(range=()): '''Reverses the order of the items in the queue. Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is affected. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. ''' start, end = split_range(range) altered_slice = data.song_queue[start:end] altered_slice.reverse() data.lock.acquire() try: data.song_queue[start:end] = altered_slice finally: data.last_queue_update = time.time() data.lock.release() return True