def step_impl(context, no): assert navigate_to( context, "chapter", focus_threshold=30), "fail to find chapter on progress bar" # navigate to the dest. chapter found_chapter = find_current_chapters(context) assert found_chapter > 0, "no focused chapter found" cur_row = (found_chapter - 1) // CHAPTER_GRID_COLS cur_col = (found_chapter - 1) % CHAPTER_GRID_COLS debug(f"cur row:{cur_row}, cur col:{cur_col}") dest_row = (int(no) - 1) // CHAPTER_GRID_COLS dest_col = (int(no) - 1) % CHAPTER_GRID_COLS debug(f"dest row:{dest_row}, dest col:{dest_col}") for _ in range(abs(dest_col - cur_col)): if dest_col > cur_col: press(context, keys.KEY_RIGHT, 1) elif dest_col < cur_col: press(context, keys.KEY_RIGHT, 1) for _ in range(abs(dest_row - cur_row)): if dest_row > cur_row: press(context, keys.KEY_DOWN, 1) elif dest_row < cur_row: press(context, keys.KEY_UP, 1) press(context, keys.KEY_SELECT)
def step_impl(context, switch): assert switch == "on" or switch == "off" press(context, keys.KEY_DOWN, 1) # to show progress bar cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(context.frame) region = REGION_PROGRESS_BAR frame = cv2.imread(context.frame) mp = (3, 0.8, *get_default_match_parameter()[2::] ) # use ccorr_normed method 0 and threshold =0.65 if switch == "off": debug(f"match {playback_soft_buttons['cc off unhilite'][1]}") if match(frame, cv2.imread(playback_soft_buttons["cc off unhilite"][1]), region, match_parameter=mp)[0] is True: debug(f"closed caption is already {switch}\n\n") return elif switch == 'on': debug(f"match {playback_soft_buttons['cc on unhilite'][1]}") if match(frame, cv2.imread(playback_soft_buttons["cc on unhilite"][1]), region, match_parameter=mp)[0] is True: debug(f"closed caption is already {switch}") return debug(f"navigate_to {switch}\n\n") if navigate_to(context, "cc on" if switch == "off" else "cc off"): press(context, keys.KEY_SELECT, 1) # press(context, keys.KEY_UP, 1) press(context, keys.KEY_SELECT, 1) return assert False, f"fail to switch {switch} closed caption"
def is_valid_kiss_frame(f) -> bool: f"""Check if this frame is valid. :param f: the frame to inspect :returns: True if valid, False if not >>> is_valid_kiss_frame(b'\xC0\x00\x54\x45\x53\x54\xC0') True >>> is_valid_kiss_frame(b'\xC0') False >>> is_valid_kiss_frame(b'\xC0\x00\x54\x45\x53\x54') False """ valid = True # Assume it is valid # Check for a minimum length if len(f) < 2: debug("ERROR: KISS frame length < 2") valid = False # Check if this kiss frame end with an FEND. If not, there is an error if not f[-1:] == kiss.FEND: debug(f"ERROR: KISS frame should end with FEND. {f=}") valid = False return valid
def step_impl(context, screen): assert screen in MENU_SCREEN_TABS, f"error: unknown {screen} tab on the menu page" if screen == MENU_SCREEN_TABS[0]: key = keys.KEY_LEFT max_try = 2 else: max_try = len(MENU_SCREEN_TABS) - 1 key = keys.KEY_RIGHT goto_vudu_home(context) # we are in menu screen now # check current selection is on spotlight cam = context.cam while max_try: context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(path=context.frame) text_in_highlight, region = find_selection_text( cv2.imread(context.frame), cv2.imread(MAIN_MENU_SEL_LEFT), cv2.imread(MAIN_MENU_SEL_RIGHT), x_offset=10, y_offset=5, region=Region(0, 0, 1000, 80), match_parameter=get_default_match_parameter()) debug(f"current selection is {text_in_highlight} at {region}") if text_in_highlight is not None and screen.lower( ) in text_in_highlight.lower(): debug(f"found {screen}") return press(context, key, 1) max_try -= 1 assert False, f"fail to go to {screen} tab on the menu page"
def step_impl(context): cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(path=context.frame) frame = cv2.imread(context.frame) thank_you_purchase_title = thank_you_purchase_popup_title(context,frame) debug(f"thank you purchase popup title: {thank_you_purchase_title}") assert fuzzy_match("Thank You", thank_you_purchase_title, 0.9), f"thank you purchase popup title {thank_you_purchase_title} is not found"
def step_impl(context): cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(path=context.frame) frame = cv2.imread(context.frame) confirm_purchase_title = confirm_purchase_popup_title(context,frame) debug(f"confirm purchase popup title: {confirm_purchase_title}") assert fuzzy_match("Confirm Purchase", confirm_purchase_title, 0.9) or \ fuzzy_match("Confirm Rental", confirm_purchase_title, 0.9), f"Confirm purchase popup title {confirm_purchase_title} is not found "
def __init__(self, config_dir, read_now=True): # TODO: Check to make sure that the location is valid, and that the name is fldigi_def.xml self.config_dir = config_dir self.location = os.path.join(config_dir, "fldigi_def.xml") debug(f"{self.location}") self.settings = {} self.dirty = False if read_now is True: self.read()
def close(self) -> bool: """Close the connection and the database""" # debug('Closing database connection') if self.con: self.con.close() debug("Database has been closed") return True return False
def strip_fends_from_kissframe(frame) -> bytes: r"""Strip leading and trailing FENDS from the given kiss frame :param frame: The kiss frame to process :returns: The stripped frame >>> strip_fends_from_kissframe(b'\xC0\x00\x54\x45\x53\x54\xC0') b'\x00\x54\x45\x53\x54' """ frame = frame.strip(kiss.FEND) debug(f"stripped frame: {frame=}") return frame
def detect_shoppable_ads(context): debug("=== detect_shoppable_ads ===") frame = cv2.imread(context.frame) ads = "./images/Roku/shoppable_ads.png" ads1 = "./images/Roku/shoppable_ads_1.png" ads2 = "./images/Roku/shoppable_ads_2.png" ads_region = Region(x=1000, y=400, right=1280, bottom=600) ads1_region = Region(x=1000, y=360, right=1280, bottom=620) ads2_region = Region(x=1070, y=260, right=1166, bottom=350) return match(frame, ads, rads_region)[0] or \ match(frame, ads1, ads1_region)[0] or \ match(frame, ads2, ads2_region)[0]
def step_impl(context): cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(path=context.frame) region = Region(0, 0, 1000, 70) match_result = ocr(cv2.imread(context.frame), region) for tab in MENU_SCREEN_TABS: #assert tab in match_result, f"{tab} not found in menu tabs" if tab not in match_result: debug(f"error: {tab} not found in menu tabs") else: debug(f"{tab} found in menu tabs")
def get_dbasefile_path(dbase_filename="btdevice_dbase.sqlite") -> Path: """Determine full path to Database file :returns: fullpath to database file """ path = pathlib.Path.cwd().joinpath("dbase") debug(f"path = {path}") # if not os.path.isdir(path): # os.makedirs(path) path.mkdir(parents=True, exist_ok=True) fullpath = path.joinpath(dbase_filename) return fullpath
def open(self) -> bool: """ If the database does not exist yet, the table 'devices' will be created. If it already exists, it will just be openend. """ debug(f"Opening database {self.dbasefile}") self.con = sqlite3.connect(self.dbasefile, check_same_thread=False) self.cur = self.con.cursor() if self.cur: return True else: return False
def current_menu_selection(context): cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(path=context.frame) text_in_highlight, region = find_selection_text( cv2.imread(context.frame), cv2.imread(MAIN_MENU_SEL_LEFT), cv2.imread(MAIN_MENU_SEL_RIGHT), x_offset=10, y_offset=5, region=Region(0, 0, 1000, 80)) debug(f"current selection is {text_in_highlight} at {region}") return text_in_highlight
def search(self, searchfor) -> list: """Generic search in the software database for string s :param searchfor: The string to search for :return: List of tuples like [(u'c:\\xxxx,), (u'c:\\yyyy,)] """ if not searchfor: debug("search string or search list was not defined") return [] if type(searchfor) == str: searchfor = [searchfor ] # Convert string to list with a single item list_of_tuples = [] for searchstring in searchfor: debug(f"Searching for '{searchstring}' with GLOB") self.cur.execute( # "select addr from devices where addr GLOB ? order by addr", SEARCH_ADDR, [searchstring], ) list_of_tuples.extend(self.cur.fetchall()) debug(f"dbase search result = {list_of_tuples}") returnlist = [x[0] for x in list_of_tuples] returnlist = sorted(returnlist) debug(f"returnlist = {returnlist}") return returnlist
def step_impl(context, video_quality): assert video_quality in video_qualities press(context, keys.KEY_DOWN) # to show progress bar # find current video quaility current_video_qaulity = find_video_quality_on_progress_bar(context) assert current_video_qaulity is not None, "no video quality found in play page" if video_quality == current_video_qaulity: debug(f"video quality is already {video_quality}\n\n") return elif video_quality == current_video_qaulity: debug(f"video quality is already {video_quality}\n\n") return elif video_quality == current_video_qaulity: debug(f"video quality is already {video_quality}\n\n") return debug(f"navigate_to {current_video_qaulity}\n\n") if navigate_to(context, current_video_qaulity): press(context, keys.KEY_SELECT, 1) if video_quality == 'uhd' or video_quality == 'hdx': if current_video_qaulity == "sd": press(context, keys.KEY_UP, 1) press(context, keys.KEY_UP, 1) else: press(context, keys.KEY_UP, 1) if video_quality == 'sd': press(context, keys.KEY_UP, 1) press(context, keys.KEY_UP, 1) press(context, keys.KEY_SELECT, 1) return assert False, f"fail to change video quality to {video_quality} "
def step_impl(context, tab): assert tab in MENU_SCREEN_TABS, "error: {tab} is an unknown screen" cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(path=context.frame) text_in_highlight, region = find_selection_text( cv2.imread(context.frame), cv2.imread(MAIN_MENU_SEL_LEFT), cv2.imread(MAIN_MENU_SEL_RIGHT), region=Region(0, 0, 1000, 80), match_parameter=get_default_match_parameter()) debug(f"current selection is {text_in_highlight} at {region}") assert tab.lower() in text_in_highlight.lower( ), f"{tab} selection not found"
def step_impl(context, tab): assert tab in settings_screen_tabs, "error: {tab} is an unknown screen" cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(path=context.frame) text_in_highlight, region = find_selection_text( cv2.imread(context.frame), cv2.imread(SETTINGS_MENU_SEL), None, x_offset=20, region=Region(50, 170, 400, 540), match_parameter=get_default_match_parameter()) debug(f"current selection is {text_in_highlight} at {region}") assert fuzzy_match(tab, text_in_highlight), f"{tab} selection not found"
def __init__(self, filename): """Intialize this class @param filename The name of the database file The database fields: create table software(path text primary key, name text) """ debug("Initializing DeviceDatabase") if not filename: print("No DeviceDatabase filename given") self.dbasefile = pathlib.Path(filename) debug(f"dbasefile = {self.dbasefile}") if not self.dbasefile.parent.is_dir(): print(f"Could not find foldername for {self.dbasefile}") self.dbasefile.parent.mkdir(exist_ok=True) self.con = None self.cur = None if self.dbasefile.is_file(): self.open() debug("Opened existing database") else: self.create() debug("Created new database")
def process_devices(data): """Process the bluetoothctl devices :param data: string with lines to process :returns: dictionary of bluetooth devices Example string: see [sample_output] """ for line in data.splitlines(): datetimestr = get_timestamp() if not line: continue # Reset values device = None addr = None try: dev, addr, value = line.split(" ", maxsplit=2) debug(f"{dev}, {addr}, {value}") if addr not in bt_devices: # This is a new device # device = BTDevice(addr=addr, dev=dev, timestamp=datetimestr, name=value) device = BTDevice() device.addr = addr device.dev = dev device.timestamp = datetimestr device.name = value # print('new device has been found', device) else: # This device was seen before. Update the information device = bt_devices.get(addr) device.timestamp = datetimestr # print('existing device has been updated:', device) except ValueError: print('ERROR: Problem in line "line"') # Add / Replace this device in the dictionary # print(device) if addr and device: bt_devices[addr] = device return bt_devices
def process_device_info(info) -> BTDevice: """Process deviceinfo string for one device :param info: The string to process. For an example, see ... """ bt_device = BTDevice() hex_value_list = reset_value() key = "" for line in info.splitlines(): line = line.strip() if not line: # Empty line continue if line.startswith("Device "): dev_type, addr, comment = line.split(" ", maxsplit=2) bt_device.dev_type = dev_type bt_device.addr = addr bt_device.comment = comment bt_device.info = info debug(bt_device) continue try: key, val = line.split(":", maxsplit=1) key = key.strip() val = val.strip() debug(f"key={key}, val={val}") bt_device.props[key.strip()] = val hex_value_list = reset_value() continue except ValueError: # print(f"Could not use split(':') on line={line}") l2 = line if len(line) > 47: l2 = line[:47] t = re.findall(r"[0-9a-fA-F]+", l2) # Find the hexadecimal combinations # print("regex output: ", t) hex_value_list.extend(t) bt_device.props[key.strip()] = hex_value_list print() print(bt_device) return bt_device
def step_impl(context): # read watch time and program time press(context, keys.KEY_DOWN) cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(context.frame) counter = ocr(cv2.imread(context.frame), REGION_PROGRESS_BAR_TIME, grayscale=True, invert_grayscale=True, ocr_parameter='-c tessedit_char_whitelist=0123456789/:') debug(f"counter: {counter}\n") assert "/" in counter, "no counter found" play_time, program_time = parse_play_time_and_program_time(counter) debug(f"play time / program time: {play_time}/{program_time}") context.play_time = play_time context.program_time = program_time
def find_current_chapters(context): press(context, keys.KEY_SELECT, 1) # to show chapter grid cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(context.frame) frame = cv2.imread(context.frame) mp = (3, 0.8, *get_default_match_parameter()[2::] ) # use ccorr_normed method 0 and threshold =0.65 debug(f"match {playback_chapter['chapter']}") result = match(frame, cv2.imread(playback_chapter["chapter"]), REGION_CHAPTER_GRID, match_parameter=mp) print("\n\n") assert result[0] is True, f"fail to find current chapter" # find current chapter chapter_grids_width = REGION_CHAPTER_GRID.right - REGION_CHAPTER_GRID.x chapter_grids_height = REGION_CHAPTER_GRID.bottom - REGION_CHAPTER_GRID.top chapter_width = chapter_grids_width // CHAPTER_GRID_COLS chapter_height = chapter_grids_height // CHAPTER_GRID_ROWS chapters_regions = [] for row in range(CHAPTER_GRID_ROWS): for col in range(CHAPTER_GRID_COLS): chapters_regions.append( Region(REGION_CHAPTER_GRID.x + col * chapter_width, REGION_CHAPTER_GRID.y + row * chapter_height, REGION_CHAPTER_GRID.x + (col + 1) * chapter_width, REGION_CHAPTER_GRID.y + (row + 1) * chapter_height)) # debug(f"chapter: {len(chapters_regions) + 1}, {chapters_regions[-1]}") found_chapters = [] focused_chapter = 0 focused_chapter_area = 0 for chp in range(len(chapters_regions)): area = Region.intersect_area(result[1], chapters_regions[chp]) # debug(f"intersect area {result[1]} and {chapters_regions[chp]}, area: {area}") if area > 0: found_chapters.append((chp, area)) debug(f"chapter {chp + 1}, area: {area}") if area > focused_chapter_area: focused_chapter = chp + 1 focused_chapter_area = area return focused_chapter
def get_mac_addresses(data): r"""Process the output of 'bluetoothctl devices' :param data: string with the output of 'bluetoothctl devices' :returns: list of mac addresss found >>> get_mac_addresses(testdata) ['47:B6:7A:81:C4:BC', 'D8:DD:6B:81:74:8B', '24:FC:E5:8F:AB:89'] """ addr_list = [] for line in data.splitlines(): if not line: continue debug(f"line = '{line}'") dev, addr, value = line.split(" ", maxsplit=2) addr_list.append(addr) return addr_list
def step_impl(context): cam = context.cam frame = 0 new_frame = f"{WORK_DIR}/_frame{frame}.png" timeout = 30 start = time.time() prev_frame = f"{WORK_DIR}/_frame_{frame}.png" assert 200 == cam.get_frame(path=prev_frame) num_diffs = 0 while start + timeout > time.time(): frame += 1 new_frame = f"{WORK_DIR}/_frame_{frame}.png" assert 200 == cam.get_frame(path=new_frame) diff_percentage = image_diff(prev_frame, new_frame) if diff_percentage: debug(f"image_diff({num_diffs}/10):{diff_percentage}") num_diffs += 1 if num_diffs > 10: assert False, "video is not frozen" prev_frame = new_frame pass
def add(self, addr, name, info) -> bool: """Add an entry to the sqlite database :param addr: BT address :param name: Name of the BT device :param info: Info string of this BT device :return: True in case of success, False in case of an error. """ try: self.cur.execute( "insert into devices values (?, ?, ?)", (addr, name, info), ) except sqlite3.IntegrityError: debug(f'Cannot not add addr "{addr}" twice') return False self.add(addr, name, info) self.con.commit() return True
def find_video_quality_on_progress_bar(context): cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(context.frame) frame = cv2.imread(context.frame) mp = (3, 0.8, *get_default_match_parameter()[2::] ) # use ccorr_normed method 0 and threshold =0.65 video_quality = None found_uhd = match(frame, cv2.imread(playback_soft_buttons['uhd unhilite'][1]), REGION_PROGRESS_BAR, match_parameter=mp)[0] if found_uhd is True: video_quality = "uhd" debug(f"found uhd") found_hdx = match(frame, cv2.imread(playback_soft_buttons['hdx unhilite'][1]), REGION_PROGRESS_BAR, match_parameter=mp)[0] if found_hdx is True: video_quality = "hdx" debug(f"found hdx") found_sd = match(frame, cv2.imread(playback_soft_buttons['sd unhilite'][1]), REGION_PROGRESS_BAR, match_parameter=mp)[0] if found_sd is True: video_quality = "sd" debug(f"found sd") return video_quality
def step_impl(context): press(context, keys.KEY_DOWN, 1) # to show progress bar cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") assert 200 == cam.get_frame(context.frame) frame = cv2.imread(context.frame) mp = (0, 0.8, *get_default_match_parameter()[2::] ) # use ccorr_normed method 0 and threshold =0.65 video_quality = "None" result = 0.0 assert os.path.exists(playback_soft_buttons["play off unhilitefamily"][1]) found = match(frame, cv2.imread( playback_soft_buttons['family play off unhilite'][1]), REGION_PROGRESS_BAR, match_parameter=mp) if found[0] is True: debug(f"found family play off\n") return assert os.path.exists(playback_soft_buttons["family play on unhilite"][1]) found = match(frame, cv2.imread( playback_soft_buttons['family play on unhilite'][1]), REGION_PROGRESS_BAR, match_parameter=mp) if found[0] is True: debug(f"found family play on\n") return debug(f"fail to find family play on the progress barn\n") assert False, "fail to find family play on the progress bar"
def decode_cmd(cmdbyte) -> tuple: r"""Decode the given command byte. :param cmdbyte: The command byte to decode :returns: tuple of the command name string, and the port index. >>> decode_cmd(0x00) ('DATAFRAME', 0) >>> decode_cmd(0xFF) ('RETURN', 0) >>> decode_cmd(0x34) ('TXTAIL', 4) """ if cmdbyte == 0xFF: return "RETURN", 0 cmd = cmdbyte & 0x0F debug(f"{cmd=}") portindex = cmdbyte & 0xF0 >> 4 debug(f"{portindex=}") cmd_str = kiss_cmds.get(cmd, "--UNKNOWN--") debug(f"{cmd_str=}") return cmd_str, portindex
def step_impl(context): # read ads watch time and program time cam = context.cam context.frame = get_frame_name(context, f"{WORK_DIR}/_frame.png") start = time.time() success = 0 while True and time.time() < start + 180: press(context, keys.KEY_DOWN) # to show progress bar assert 200 == cam.get_frame(context.frame) region = Region(x=900, y=640, right=1110, bottom=720) ads = ocr(cv2.imread(context.frame), region, grayscale=True, invert_grayscale=True) debug(f"ads: {ads}") region = Region(x=1110, y=640, right=1280, bottom=720) counter = ocr(cv2.imread(context.frame), region, grayscale=True, invert_grayscale=True) debug(f"counter: {counter}") debug(f"ads/counter: {ads} / {counter}\n") if "Ad" not in ads and ":" not in counter: success += 1 if success > 3: return assert False, "ads is still playing"