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"
Example #3
0
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
Example #4
0
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 "
Example #7
0
 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()
Example #8
0
    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
Example #9
0
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]
Example #11
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")
Example #12
0
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
Example #13
0
    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
Example #14
0
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
Example #15
0
    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} "
Example #17
0
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"
Example #18
0
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"
Example #19
0
    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
Example #26
0
    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"
Example #29
0
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"