def custom_operation(
            self,
            key: str,
            operator: Union[str, Callable],
            value,
            method: Union[str, Callable],
            word_level: Optional[bool] = None
    ) -> "WhisperResult":
        """
        Perform custom operation on words/segments that satisfy specified conditions.

        Parameters
        ----------
        key : str
            Attribute of words/segments to get value for comparing with ``value``.
            If ``key`` is empty string, directly use the instance of ``WordTiming``/``Segment`` object for comparison.
            All " " in ``key`` will be replaced with "_".
            Start with "len=" to get length of the attribute.
        operator : str or Callable
            Operator or function for comparing vaLue of ``key`` and ``value``.
            Built-in operators: ==, >, >=, <, <=, is, in, start, end
            Function must return a boolean and accept two arguments: vaLue of ``key``, ``value``.
        value : any
            Value to compare with vaLue of ``key``.
            To express multiple values as a string:
                -start string with "all=" or "any=" followed with values separated by ','
                -use "\" to escape values with "," in values
                -these values can only be conbination of str, int, float
                -start with "all=": all values are required to meet the condition to be satisfied
                -start with "any=": only require one of the values to meet the conditions to be satified
        method : str or Callable
            Operation or function to check if words/segments meet conditions.
            Built-in operations:
                mergeleft, mergeright, merge, lockright, lockleft, lock, splitright, splitleft, split, remove
            Function must accept three arguments: this instance of `WhisperResult`, segment index, word index.
        word_level : bool, default None, meaning True if this instance has word-timestamps
            Whether to operate on words. Operate on segments if ``False``.

        Returns
        -------
        stable_whisper.result.WhisperResult
            The current instance after the changes.

        Examples
        --------
        Split on the right of words that end with "." or "," or "?":
        >>>result.custom_operation('word', 'end', 'any=.,\,,?', 'splitright', word_level=True)

        Merge with next segment if the current segment is less than 15 chars:
        >>>result.custom_operation('len=text', '<', 15, 'mergeright', word_level=False)

        If there is split after the word " Mr." or " Ms." or " Mrs.", merge with next segment:
        >>>result.custom_operation('word', 'in', (' Mr.', ' Ms.', ' Mrs.'), 'mergeright', word_level=True)
        same as:
        >>>result.custom_operation('word', '==', 'any= Mr., Ms., Mrs', 'mergeright', word_level=True)

        Replace 'y' with 'ie' for capitalized words:
        >>>def is_match(word, value):
        >>>    text = word.word.strip().lower()
        >>>    return text == 'one' and word.probability < value
        >>>def replace_one(result, seg_index, word_index):
        >>>    word = result[seg_index][word_index]
        >>>    word.word = word.word.lower().replace('one', '1')
        >>>result.custom_operation('', is_match, 0.5, replace_one, word_level=True)

        """
        if self.has_words:
            if word_level is None:
                word_level = True
        elif word_level:
            raise ValueError(f'result is missing word timestamps and not compatible with ``word_level=True``')

        value = _get_content(self, value, strict=False)

        methods = (
            'mergeleft', 'mergeright', 'merge',
            'lockright', 'lockleft', 'lock',
            'splitright', 'splitleft', 'split',
            'remove'
        )
        method = _get_content(self, method)
        is_builtin_method = isinstance(method, str)
        if is_builtin_method:
            if method not in methods:
                raise ValueError(f"invalid method: '{method}'. Valid methods: {method}")
        elif not callable(method):
            raise TypeError(f"'{type(method)}' object is not callable")
        key = key.replace(' ', '_')
        operator = _get_content(self, operator)
        if isinstance(operator, str):
            operators = {
                '==': lambda a, b: a == b,
                '>': lambda a, b: a > b,
                '>=': lambda a, b: a >= b,
                '<': lambda a, b: a < b,
                '<=': lambda a, b: a <= b,
                'is': lambda a, b: a is b,
                'in': lambda a, b: a in b,
                'start': str.startswith,
                'end': str.endswith
            }
            if operator not in operators:
                raise ValueError(f"invalid operator: '{operators}'. Valid operators: {tuple(operators.keys())}")
            operator_str = operator
            operator = operators[operator]
        else:
            operator_str = _store_content(self, operator)

        method_str = method
        if not is_builtin_method:
            method_str = _store_content(self, method)
        elif method.startswith('merge'):

            def get_left_indices(result: "WhisperResult", seg_idx: int, word_idx: int = None):
                if seg_idx == 0 or (word_idx is not None and word_idx != 0):
                    return []
                return [(seg_idx - 1, seg_idx)]

            def get_right_indices(result: "WhisperResult", seg_idx: int, word_idx: int = None):
                if (
                        seg_idx + 1 >= len(result.segments) or
                        (word_idx is not None and word_idx != len(result[seg_idx].words) -1)
                ):
                    return []
                return [(seg_idx, seg_idx + 1)]

            if method == 'mergeright':
                get_indices = get_right_indices
            elif method == 'mergeleft':
                get_indices = get_left_indices
            else:
                def get_indices(*args):
                    return get_right_indices(*args) + get_left_indices(*args)

            def method(result: "WhisperResult", seg_idx: int, word_idx: int = None):
                indices = get_indices(result, seg_idx, word_idx)
                for idxs in indices:
                    result.add_segments(*idxs, inplace=True, reassign_ids=False)

        elif method.startswith('lock'):
            if method == 'lockright':
                def method(result: "WhisperResult", seg_idx: int, word_idx: int = None):
                    if word_idx is None:
                        result[seg_idx].lock_right()
                    else:
                        result[seg_idx][word_idx].lock_right()
            elif method == 'lockleft':
                def method(result: "WhisperResult", seg_idx: int, word_idx: int = None):
                    if word_idx is None:
                        result[seg_idx].lock_left()
                    else:
                        result[seg_idx][word_idx].lock_left()
            else:
                def method(result: "WhisperResult", seg_idx: int, word_idx: int = None):
                    if word_idx is None:
                        result[seg_idx].lock_right()
                        result[seg_idx].lock_left()
                    else:
                        result[seg_idx][word_idx].lock_right()
                        result[seg_idx][word_idx].lock_left()

        elif method.startswith('split'):
            if word_level is None:
                raise ValueError('Segment-level result is not compatible with split actions.')
            elif not word_level:
                raise ValueError('``word_level=False`` is not compatible with split actions.')

            if method == 'splitright':
                def method(result: "WhisperResult", seg_idx: int, word_idx: int = None):
                    if word_idx == len(result[seg_idx].words) + 1:
                        return
                    result.split_segment_by_index(result[seg_idx], word_idx, reassign_ids=False)
            elif method == 'splitleft':
                def method(result: "WhisperResult", seg_idx: int, word_idx: int = None):
                    if word_idx == 0:
                        return
                    result.split_segment_by_index(result[seg_idx], word_idx - 1, reassign_ids=False)
            else:
                def method(result: "WhisperResult", seg_idx: int, word_idx: int = None):
                    indices = []
                    if word_idx != 0:
                        indices.append(word_idx - 1)
                    if word_idx < len(result[seg_idx].words) + 1:
                        indices.append(word_idx)
                    result.split_segment_by_index(result[seg_idx], indices, reassign_ids=False)

        else:  # remove
            def method(result: "WhisperResult", seg_idx: int, word_idx: int = None):
                if word_idx is None:
                    result.remove_segment(result[seg_idx], reassign_ids=False, record=False)
                else:
                    result.remove_word(result[seg_idx][word_idx], reassign_ids=False, record=False)

        if key.startswith('len='):
            key_ = key[4:]

            def get_value(obj):
                return len(getattr(obj, key_))
        elif key == '':
            def get_value(obj):
                return obj
        else:
            def get_value(obj):
                return getattr(obj, key)

        if isinstance(value, str) and (value.startswith('all=') or value.startswith('any=')):
            check = any if value.startswith('any=') else all
            values = [val.replace('\\,', ',') for val in re.split(r'(?<!\\),', value[4:])]

            def is_satisfied(obj):
                return check(operator(get_value(obj), val) for val in values)
        else:
            def is_satisfied(obj):
                return operator(get_value(obj), value)

        if word_level:
            for segment_index in range(len(self.segments)-1, -1, -1):
                for word_index in range(len(self.segments[segment_index].words)-1, -1, -1):
                    word = self[segment_index][word_index]
                    if not is_satisfied(word):
                        continue
                    method(self, segment_index, word_index)
        else:
            for segment_index in range(len(self.segments) - 1, -1, -1):
                segment = self[segment_index]
                if not is_satisfied(segment):
                    continue
                method(self, segment_index, None)

        self.reassign_ids()
        if isinstance(value, bool) or not isinstance(value, (str, int, float)):
            if isinstance(value, bool):
                value = f'<{value}>'
            else:
                value = _store_content(self, value)
        self._update_history(
            f'co={key.replace("_", " ")}+{operator_str}+{value}+{method_str}+{int(word_level)}'
        )
        return self

        Perform custom operation on words/segments that satisfy specified conditions.

        Parameters
        ----------
        key : str
            Attribute of words/segments to get value for comparing with ``value``.
            If ``key`` is empty string, directly use the instance of ``WordTiming``/``Segment`` object for comparison.
            All " " in ``key`` will be replaced with "_".
            Start with "len=" to get length of the attribute.
        operator : str or Callable
            Operator or function for comparing vaLue of ``key`` and ``value``.
            Built-in operators: ==, >, >=, <, <=, is, in, start, end
            Function must return a boolean and accept two arguments: vaLue of ``key``, ``value``.
        value : any
            Value to compare with vaLue of ``key``.
            To express multiple values as a string:
                -start string with "all=" or "any=" followed with values separated by ','
                -use "" to escape values with "," in values
                -these values can only be conbination of str, int, float
                -start with "all=": all values are required to meet the condition to be satisfied
                -start with "any=": only require one of the values to meet the conditions to be satified
        method : str or Callable
            Operation or function to check if words/segments meet conditions.
            Built-in operations:
                mergeleft, mergeright, merge, lockright, lockleft, lock, splitright, splitleft, split, remove
            Function must accept three arguments: this instance of `WhisperResult`, segment index, word index.
        word_level : bool, default None, meaning True if this instance has word-timestamps
            Whether to operate on words. Operate on segments if ``False``.

        Returns
        -------
        stable_whisper.result.WhisperResult
            The current instance after the changes.

        Examples
        --------
        Split on the right of words that end with "." or "," or "?":
        >>>result.custom_operation('word', 'end', 'any=.,\,,?', 'splitright', word_level=True)

        Merge with next segment if the current segment is less than 15 chars:
        >>>result.custom_operation('len=text', '<', 15, 'mergeright', word_level=False)

        If there is split after the word " Mr." or " Ms." or " Mrs.", merge with next segment:
        >>>result.custom_operation('word', 'in', (' Mr.', ' Ms.', ' Mrs.'), 'mergeright', word_level=True)
        same as:
        >>>result.custom_operation('word', '==', 'any= Mr., Ms., Mrs', 'mergeright', word_level=True)

        Replace 'y' with 'ie' for capitalized words:
        >>>def is_match(word, value):
        >>>    text = word.word.strip().lower()
        >>>    return text == 'one' and word.probability < value
        >>>def replace_one(result, seg_index, word_index):
        >>>    word = result[seg_index][word_index]
        >>>    word.word = word.word.lower().replace('one', '1')
        >>>result.custom_operation('', is_match, 0.5, replace_one, word_level=True)

        
stable_whisper/result.py