Esempio n. 1
0
def part():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash], validator={'__parent__': seps_surround})

    prefixes = ['pt', 'part']

    def validate_roman(match):
        """
        Validate a roman match if surrounded by separators
        :param match:
        :type match:
        :return:
        :rtype:
        """
        if int_coercable(match.raw):
            return True
        return seps_surround(match)

    rebulk.regex(build_or_pattern(prefixes) + r'-?(?P<part>' + numeral + r')',
                 prefixes=prefixes, validate_all=True, private_parent=True, children=True, formatter=parse_numeral,
                 validator={'part': compose(validate_roman, lambda m: 0 < m.value < 100)})

    return rebulk
Esempio n. 2
0
def country(config, common_words):
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :param common_words: common words
    :type common_words: set
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'country'))
    rebulk = rebulk.defaults(name='country')

    def find_countries(string, context=None):
        """
        Find countries in given string.
        """
        allowed_countries = context.get('allowed_countries') if context else None
        return CountryFinder(allowed_countries, common_words).find(string)

    rebulk.functional(find_countries,
                      #  Prefer language and any other property over country if not US or GB.
                      conflict_solver=lambda match, other: match
                      if other.name != 'language' or match.value not in (babelfish.Country('US'),
                                                                         babelfish.Country('GB'))
                      else other,
                      properties={'country': [None]},
                      disabled=lambda context: not context.get('allowed_countries'))

    babelfish.country_converters['guessit'] = GuessitCountryConverter(config['synonyms'])

    return rebulk
Esempio n. 3
0
def country(config, common_words):
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :param common_words: common words
    :type common_words: set
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'country'))
    rebulk = rebulk.defaults(name='country')

    def find_countries(string, context=None):
        """
        Find countries in given string.
        """
        allowed_countries = context.get(
            'allowed_countries') if context else None
        return CountryFinder(allowed_countries, common_words).find(string)

    rebulk.functional(
        find_countries,
        #  Prefer language and any other property over country if not US or GB.
        conflict_solver=lambda match, other: match
        if other.name != 'language' or match.value not in
        (babelfish.Country('US'), babelfish.Country('GB')) else other,
        properties={'country': [None]},
        disabled=lambda context: not context.get('allowed_countries'))

    babelfish.country_converters['guessit'] = GuessitCountryConverter(
        config['synonyms'])

    return rebulk
Esempio n. 4
0
def complete_words(rebulk: Rebulk, season_words, complete_article_words):
    """
    Custom pattern to find complete seasons from words.
    """
    season_words_pattern = build_or_pattern(season_words)
    complete_article_words_pattern = build_or_pattern(complete_article_words)

    def validate_complete(match):
        """
        Make sure season word is are defined.
        :param match:
        :type match:
        :return:
        :rtype:
        """
        children = match.children
        if not children.named('completeWordsBefore') and not children.named(
                'completeWordsAfter'):
            return False
        return True

    rebulk.regex(
        '(?P<completeArticle>' + complete_article_words_pattern + '-)?' +
        '(?P<completeWordsBefore>' + season_words_pattern + '-)?' +
        'Complete' + '(?P<completeWordsAfter>-' + season_words_pattern + ')?',
        private_names=[
            'completeArticle', 'completeWordsBefore', 'completeWordsAfter'
        ],
        value={'other': 'Complete'},
        tags=['release-group-prefix'],
        validator={'__parent__': and_(seps_surround, validate_complete)})
Esempio n. 5
0
def cds():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash])

    rebulk.regex(
        r"cd-?(?P<cd>\d+)(?:-?of-?(?P<cd_count>\d+))?",
        validator={"cd": lambda match: 0 < match.value < 100, "cd_count": lambda match: 0 < match.value < 100},
        formatter={"cd": int, "cd_count": int},
        children=True,
        private_parent=True,
        properties={"cd": [None], "cd_count": [None]},
    )
    rebulk.regex(
        r"(?P<cd_count>\d+)-?cds?",
        validator={"cd": lambda match: 0 < match.value < 100, "cd_count": lambda match: 0 < match.value < 100},
        formatter={"cd_count": int},
        children=True,
        private_parent=True,
        properties={"cd": [None], "cd_count": [None]},
    )

    return rebulk
Esempio n. 6
0
def audio_codec(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk() \
        .regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]) \
        .string_defaults(ignore_case=True)

    def audio_codec_priority(match1, match2):
        """
        Gives priority to audio_codec
        :param match1:
        :type match1:
        :param match2:
        :type match2:
        :return:
        :rtype:
        """
        if match1.name == 'audio_codec' and match2.name in [
                'audio_profile', 'audio_channels'
        ]:
            return match2
        if match1.name in ['audio_profile', 'audio_channels'
                           ] and match2.name == 'audio_codec':
            return match1
        return '__default__'

    rebulk.defaults(
        name='audio_codec',
        conflict_solver=audio_codec_priority,
        disabled=lambda context: is_disabled(context, 'audio_codec'))

    load_config_patterns(rebulk, config.get('audio_codec'))

    rebulk.defaults(
        clear=True,
        name='audio_profile',
        disabled=lambda context: is_disabled(context, 'audio_profile'))

    load_config_patterns(rebulk, config.get('audio_profile'))

    rebulk.defaults(
        clear=True,
        name="audio_channels",
        disabled=lambda context: is_disabled(context, 'audio_channels'))

    load_config_patterns(rebulk, config.get('audio_channels'))

    rebulk.rules(DtsHDRule, DtsRule, AacRule, DolbyDigitalRule,
                 AudioValidatorRule, HqConflictRule,
                 AudioChannelsValidatorRule)

    return rebulk
Esempio n. 7
0
def bit_rate(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk(disabled=lambda context: (is_disabled(
        context, 'audio_bit_rate') and is_disabled(context, 'video_bit_rate')))
    rebulk = rebulk.regex_defaults(flags=re.IGNORECASE, abbreviations=[dash])
    rebulk.defaults(name='audio_bit_rate', validator=seps_surround)
    rebulk.regex(
        r'\d+-?[kmg]b(ps|its?)',
        r'\d+\.\d+-?[kmg]b(ps|its?)',
        conflict_solver=(lambda match, other: match
                         if other.name == 'audio_channels' and
                         'weak-audio_channels' not in other.tags else other),
        formatter=BitRate.fromstring,
        tags=['release-group-prefix'])

    rebulk.rules(BitRateTypeRule)

    return rebulk
Esempio n. 8
0
def release_group(config):
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    forbidden_groupnames = config['forbidden_names']

    groupname_ignore_seps = config['ignored_seps']
    groupname_seps = ''.join(
        [c for c in seps if c not in groupname_ignore_seps])

    def clean_groupname(string):
        """
        Removes and strip separators from input_string
        :param string:
        :type string:
        :return:
        :rtype:
        """
        string = string.strip(groupname_seps)
        if not (string.endswith(tuple(groupname_ignore_seps)) and string.startswith(tuple(groupname_ignore_seps))) \
                and not any(i in string.strip(groupname_ignore_seps) for i in groupname_ignore_seps):
            string = string.strip(groupname_ignore_seps)
        for forbidden in forbidden_groupnames:
            if string.lower().startswith(
                    forbidden) and string[len(forbidden):len(forbidden) +
                                          1] in seps:
                string = string[len(forbidden):]
                string = string.strip(groupname_seps)
            if string.lower().endswith(
                    forbidden) and string[-len(forbidden) -
                                          1:-len(forbidden)] in seps:
                string = string[:len(forbidden)]
                string = string.strip(groupname_seps)
        return string

    rebulk = Rebulk(
        disabled=lambda context: is_disabled(context, 'release_group'))

    expected_group = build_expected_function('expected_group')

    rebulk.functional(
        expected_group,
        name='release_group',
        tags=['expected'],
        validator=seps_surround,
        conflict_solver=lambda match, other: other,
        disabled=lambda context: not context.get('expected_group'))

    return rebulk.rules(DashSeparatedReleaseGroup(clean_groupname),
                        SceneReleaseGroup(clean_groupname), AnimeReleaseGroup)
Esempio n. 9
0
def film():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE)

    rebulk.regex(r'f(\d{1,2})', name='film', private_parent=True, children=True, formatter=int)

    rebulk.rules(FilmTitleRule)

    return rebulk
Esempio n. 10
0
def mimetype(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'mimetype'))
    rebulk.rules(Mimetype)

    return rebulk
Esempio n. 11
0
def type_(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'type'))
    rebulk = rebulk.rules(TypeProcessor)

    return rebulk
Esempio n. 12
0
def part():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash], validator={'__parent__': seps_surround})

    prefixes = ['pt', 'part']

    rebulk.regex(build_or_pattern(prefixes) + r'-?(' + numeral + r')', prefixes=prefixes,
                 name='part', validate_all=True, private_parent=True, children=True, formatter=parse_numeral)

    return rebulk
Esempio n. 13
0
def film(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE, validate_all=True, validator={'__parent__': seps_surround})

    rebulk.regex(r'f(\d{1,2})', name='film', private_parent=True, children=True, formatter=int,
                 disabled=lambda context: is_disabled(context, 'film'))

    rebulk.rules(FilmTitleRule)

    return rebulk
Esempio n. 14
0
def release_group(config):
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    forbidden_groupnames = config['forbidden_names']

    groupname_ignore_seps = config['ignored_seps']
    groupname_seps = ''.join([c for c in seps if c not in groupname_ignore_seps])

    def clean_groupname(string):
        """
        Removes and strip separators from input_string
        :param string:
        :type string:
        :return:
        :rtype:
        """
        string = string.strip(groupname_seps)
        if not (string.endswith(tuple(groupname_ignore_seps)) and string.startswith(tuple(groupname_ignore_seps))) \
                and not any(i in string.strip(groupname_ignore_seps) for i in groupname_ignore_seps):
            string = string.strip(groupname_ignore_seps)
        for forbidden in forbidden_groupnames:
            if string.lower().startswith(forbidden) and string[len(forbidden):len(forbidden) + 1] in seps:
                string = string[len(forbidden):]
                string = string.strip(groupname_seps)
            if string.lower().endswith(forbidden) and string[-len(forbidden) - 1:-len(forbidden)] in seps:
                string = string[:len(forbidden)]
                string = string.strip(groupname_seps)
        return string

    rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'release_group'))

    expected_group = build_expected_function('expected_group')

    rebulk.functional(expected_group, name='release_group', tags=['expected'],
                      validator=seps_surround,
                      conflict_solver=lambda match, other: other,
                      disabled=lambda context: not context.get('expected_group'))

    return rebulk.rules(
        DashSeparatedReleaseGroup(clean_groupname),
        SceneReleaseGroup(clean_groupname),
        AnimeReleaseGroup
    )
Esempio n. 15
0
def cd(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'cd'))
    rebulk = rebulk.regex_defaults(flags=re.IGNORECASE, abbreviations=[dash])

    load_config_patterns(rebulk, config)

    return rebulk
Esempio n. 16
0
 def expected(input_string, context):
     """
     Expected property functional pattern.
     :param input_string:
     :type input_string:
     :param context:
     :type context:
     :return:
     :rtype:
     """
     ret = []
     for search in context.get(context_key):
         if search.startswith('re:'):
             search = search[3:]
             search = search.replace(' ', '-')
             matches = Rebulk().regex(search, abbreviations=[dash], flags=re.IGNORECASE) \
                 .matches(input_string, context)
             for match in matches:
                 ret.append(match.span)
         else:
             value = search
             for sep in seps:
                 input_string = input_string.replace(sep, ' ')
                 search = search.replace(sep, ' ')
             for start in find_all(input_string, search, ignore_case=True):
                 ret.append({'start': start, 'end': start + len(search), 'value': value})
     return ret
Esempio n. 17
0
def type_():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    return Rebulk().rules(TypeProcessor)
Esempio n. 18
0
def language():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk()

    rebulk.string(*subtitle_prefixes, name="subtitle_language.prefix", ignore_case=True, private=True,
                  validator=seps_surround)
    rebulk.string(*subtitle_suffixes, name="subtitle_language.suffix", ignore_case=True, private=True,
                  validator=seps_surround)
    rebulk.functional(find_languages, properties={'language': [None]})
    rebulk.rules(SubtitlePrefixLanguageRule, SubtitleSuffixLanguageRule, SubtitleExtensionRule)

    return rebulk
Esempio n. 19
0
def processors():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    return Rebulk().rules(EnlargeGroupMatches, EquivalentHoles, RemoveAmbiguous, SeasonYear, Processors)
Esempio n. 20
0
def size():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """

    def format_size(value):
        """Format size using uppercase and no space."""
        return re.sub(r'(?<=\d)[.](?=[^\d])', '', value.upper())

    rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash])
    rebulk.defaults(name='size', validator=seps_surround)
    rebulk.regex(r'\d+\.?[mgt]b', r'\d+\.\d+[mgt]b', formatter=format_size, tags=['release-group-prefix'])

    return rebulk
Esempio n. 21
0
def release_group():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    return Rebulk().rules(SceneReleaseGroup, AnimeReleaseGroup, ExpectedReleaseGroup)
Esempio n. 22
0
def mimetype():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    return Rebulk().rules(Mimetype)
Esempio n. 23
0
def release_group():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk()

    expected_group = build_expected_function('expected_group')

    rebulk.functional(expected_group, name='release_group', tags=['expected'],
                      validator=seps_surround,
                      conflict_solver=lambda match, other: other,
                      disabled=lambda context: not context.get('expected_group'))

    return rebulk.rules(SceneReleaseGroup, AnimeReleaseGroup)
Esempio n. 24
0
def bonus():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE)

    rebulk.regex(r'x(\d+)', name='bonus', private_parent=True, children=True, formatter=int,
                 validator={'__parent__': lambda match: seps_surround},
                 conflict_solver=lambda match, conflicting: match
                 if conflicting.name in ['video_codec', 'episode'] and 'bonus-conflict' not in conflicting.tags
                 else '__default__')

    rebulk.rules(BonusTitleRule)

    return rebulk
Esempio n. 25
0
def country():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk().defaults(name='country')

    rebulk.functional(find_countries,
                      #  Prefer language and any other property over country if not US or GB.
                      conflict_solver=lambda match, other: match
                      if other.name != 'language' or match.value not in [babelfish.Country('US'),
                                                                         babelfish.Country('GB')]
                      else other,
                      properties={'country': [None]})

    return rebulk
Esempio n. 26
0
def title():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk().rules(TitleFromPosition, PreferTitleWithYear)

    expected_title = build_expected_function('expected_title')

    rebulk.functional(expected_title, name='title', tags=['expected', 'title'],
                      validator=seps_surround,
                      formatter=formatters(cleanup, reorder_title),
                      conflict_solver=lambda match, other: other,
                      disabled=lambda context: not context.get('expected_title'))

    return rebulk
Esempio n. 27
0
def bonus(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'bonus'))
    rebulk = rebulk.regex_defaults(name='bonus', flags=re.IGNORECASE)

    load_config_patterns(rebulk, config.get('bonus'))

    rebulk.rules(BonusTitleRule)

    return rebulk
Esempio n. 28
0
def date():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk().defaults(validator=seps_surround)

    rebulk.regex(
        r"\d{4}", name="year", formatter=int, validator=lambda match: seps_surround(match) and valid_year(match.value)
    )

    def date_functional(string, context):
        """
        Search for date in the string and retrieves match

        :param string:
        :return:
        """

        ret = search_date(string, context.get("date_year_first"), context.get("date_day_first"))
        if ret:
            return ret[0], ret[1], {"value": ret[2]}

    rebulk.functional(
        date_functional,
        name="date",
        properties={"date": [None]},
        conflict_solver=lambda match, other: other if other.name in ["episode", "season"] else "__default__",
    )

    rebulk.rules(KeepMarkedYearInFilepart)

    return rebulk
Esempio n. 29
0
def bonus(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'bonus'))
    rebulk = rebulk.regex_defaults(flags=re.IGNORECASE)

    rebulk.regex(r'x(\d+)',
                 name='bonus',
                 private_parent=True,
                 children=True,
                 formatter=int,
                 validator={'__parent__': seps_surround},
                 validate_all=True,
                 conflict_solver=lambda match, conflicting: match
                 if conflicting.name in ('video_codec', 'episode') and
                 'weak-episode' not in conflicting.tags else '__default__')

    rebulk.rules(BonusTitleRule)

    return rebulk
Esempio n. 30
0
def rules():
    """Return all our custom rules to be applied to the guessit api.

    IMPORTANT:
    - DO NOT define priority or dependency in each rule. Just define order here.
    - Only allowed dependency is TypeProcessor because we want to apply rules for certain types only
    """
    return Rebulk().rules(RenamePartsToEpisodeNumbers, AppendPartToMovieTile, AppendLineToMovieTitle,
                          AppendUsToMovieTitle, PrependXxxToMovieTitle, VhsAsMovieTitle)
Esempio n. 31
0
def episode_title():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk().rules(EpisodeTitleFromPosition, AlternativeTitleReplace,
                            TitleToEpisodeTitle, Filepart3EpisodeTitle,
                            Filepart2EpisodeTitle)
    return rebulk
Esempio n. 32
0
def language():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk()

    rebulk.string(*subtitle_prefixes, name="subtitle_language.prefix", ignore_case=True, private=True,
                  validator=seps_surround)
    rebulk.string(*subtitle_suffixes, name="subtitle_language.suffix", ignore_case=True, private=True,
                  validator=seps_surround)
    rebulk.functional(find_languages, properties={'language': [None]})
    rebulk.rules(SubtitlePrefixLanguageRule, SubtitleSuffixLanguageRule, SubtitleExtensionRule)

    return rebulk
Esempio n. 33
0
def title():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk().rules(TitleFromPosition, PreferTitleWithYear)

    def expected_title(input_string, context):
        """
        Expected title functional pattern.
        :param input_string:
        :type input_string:
        :param context:
        :type context:
        :return:
        :rtype:
        """
        ret = []
        for search in context.get('expected_title'):
            if search.startswith('re:'):
                search = search[3:]
                search = search.replace(' ', '-')
                matches = RePattern(search,
                                    abbreviations=[dash],
                                    flags=re.IGNORECASE).matches(
                                        input_string, context)
                for match in matches:
                    ret.append(match.span)
            else:
                for start in find_all(input_string, search, ignore_case=True):
                    ret.append((start, start + len(search)))
        return ret

    rebulk.functional(
        expected_title,
        name='title',
        tags=['expected'],
        conflict_solver=lambda match, other: other,
        disabled=lambda context: not context.get('expected_title'))

    return rebulk
Esempio n. 34
0
def title():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk().rules(TitleFromPosition, PreferTitleWithYear)

    expected_title = build_expected_function('expected_title')

    rebulk.functional(
        expected_title,
        name='title',
        tags=['expected', 'title'],
        validator=seps_surround,
        formatter=formatters(cleanup, reorder_title),
        conflict_solver=lambda match, other: other,
        disabled=lambda context: not context.get('expected_title'))

    return rebulk
Esempio n. 35
0
def processors():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    return Rebulk().rules(EnlargeGroupMatches, EquivalentHoles,
                          RemoveLessSpecificSeasonEpisode('season'),
                          RemoveLessSpecificSeasonEpisode('episode'),
                          RemoveAmbiguous, SeasonYear, Processors,
                          StripSeparators)
Esempio n. 36
0
def groups():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk()
    rebulk.defaults(name="group", marker=True)

    starting = '([{'
    ending = ')]}'

    def mark_groups(input_string):
        """
        Functional pattern to mark groups (...), [...] and {...}.

        :param input_string:
        :return:
        """
        openings = ([], [], [])
        i = 0

        ret = []
        for char in input_string:
            start_type = starting.find(char)
            if start_type > -1:
                openings[start_type].append(i)

            i += 1

            end_type = ending.find(char)
            if end_type > -1:
                try:
                    start_index = openings[end_type].pop()
                    ret.append((start_index, i))
                except IndexError:
                    pass
        return ret

    rebulk.functional(mark_groups)
    return rebulk
Esempio n. 37
0
def title(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'title'))
    rebulk.rules(TitleFromPosition, PreferTitleWithYear)

    expected_title = build_expected_function('expected_title')

    rebulk.functional(expected_title, name='title', tags=['expected', 'title'],
                      validator=seps_surround,
                      formatter=formatters(cleanup, reorder_title),
                      conflict_solver=lambda match, other: other,
                      disabled=lambda context: not context.get('expected_title'))

    return rebulk
Esempio n. 38
0
def episode_title(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    previous_names = ('episode', 'episode_count', 'season', 'season_count',
                      'date', 'title', 'year')

    rebulk = Rebulk(
        disabled=lambda context: is_disabled(context, 'episode_title'))
    rebulk = rebulk.rules(RemoveConflictsWithEpisodeTitle(previous_names),
                          EpisodeTitleFromPosition(previous_names),
                          AlternativeTitleReplace(previous_names),
                          TitleToEpisodeTitle, Filepart3EpisodeTitle,
                          Filepart2EpisodeTitle,
                          RenameEpisodeTitleWhenMovieType)
    return rebulk
Esempio n. 39
0
def episode_title(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    previous_names = ('episode', 'episode_count',
                      'season', 'season_count', 'date', 'title', 'year')

    rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'episode_title'))
    rebulk = rebulk.rules(RemoveConflictsWithEpisodeTitle(previous_names),
                          EpisodeTitleFromPosition(previous_names),
                          AlternativeTitleReplace(previous_names),
                          TitleToEpisodeTitle,
                          Filepart3EpisodeTitle,
                          Filepart2EpisodeTitle,
                          RenameEpisodeTitleWhenMovieType)
    return rebulk
Esempio n. 40
0
def part():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE,
                                     abbreviations=[dash],
                                     validator={'__parent__': seps_surround})

    prefixes = ['pt', 'part']

    if REGEX_AVAILABLE:
        rebulk.regex(r'\L<prefixes>-?(' + numeral + r')',
                     prefixes=prefixes,
                     name='part',
                     validate_all=True,
                     private_parent=True,
                     children=True,
                     formatter=parse_numeral)
    else:
        rebulk.regex(build_or_pattern(prefixes) + r'-?(' + numeral + r')',
                     prefixes=prefixes,
                     name='part',
                     validate_all=True,
                     private_parent=True,
                     children=True,
                     formatter=parse_numeral)

    return rebulk
Esempio n. 41
0
def cds(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'cd'))
    rebulk = rebulk.regex_defaults(flags=re.IGNORECASE, abbreviations=[dash])

    rebulk.regex(r'cd-?(?P<cd>\d+)(?:-?of-?(?P<cd_count>\d+))?',
                 validator={'cd': lambda match: 0 < match.value < 100,
                            'cd_count': lambda match: 0 < match.value < 100},
                 formatter={'cd': int, 'cd_count': int},
                 children=True,
                 private_parent=True,
                 properties={'cd': [None], 'cd_count': [None]})
    rebulk.regex(r'(?P<cd_count>\d+)-?cds?',
                 validator={'cd': lambda match: 0 < match.value < 100,
                            'cd_count': lambda match: 0 < match.value < 100},
                 formatter={'cd_count': int},
                 children=True,
                 private_parent=True,
                 properties={'cd': [None], 'cd_count': [None]})

    return rebulk
Esempio n. 42
0
def part(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'part'))
    rebulk.regex_defaults(flags=re.IGNORECASE, abbreviations=[dash], validator={'__parent__': seps_surround})

    prefixes = config['prefixes']

    def validate_roman(match):
        """
        Validate a roman match if surrounded by separators
        :param match:
        :type match:
        :return:
        :rtype:
        """
        if int_coercable(match.raw):
            return True
        return seps_surround(match)

    rebulk.regex(build_or_pattern(prefixes) + r'-?(?P<part>' + numeral + r')',
                 prefixes=prefixes, validate_all=True, private_parent=True, children=True, formatter=parse_numeral,
                 validator={'part': compose(validate_roman, lambda m: 0 < m.value < 100)})

    return rebulk
Esempio n. 43
0
def path():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk()
    rebulk.defaults(name="path", marker=True)

    def mark_path(input_string, context):
        """
        Functional pattern to mark path elements.

        :param input_string:
        :return:
        """
        ret = []
        if context.get('name_only', False):
            ret.append((0, len(input_string)))
        else:
            indices = list(find_all(input_string, '/'))
            indices += list(find_all(input_string, '\\'))
            indices += [-1, len(input_string)]

            indices.sort()

            for i in range(0, len(indices) - 1):
                ret.append((indices[i] + 1, indices[i + 1]))

        return ret

    rebulk.functional(mark_path)
    return rebulk
Esempio n. 44
0
def processors(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    return Rebulk().rules(EnlargeGroupMatches, EquivalentHoles,
                          RemoveLessSpecificSeasonEpisode('season'),
                          RemoveLessSpecificSeasonEpisode('episode'),
                          RemoveAmbiguous, SeasonYear, Processors, StripSeparators)
Esempio n. 45
0
def cds():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash])

    rebulk.regex(r'cd-?(?P<cd>\d+)(?:-?of-?(?P<cd_count>\d+))?',
                 validator={'cd': lambda match: match.value > 0, 'cd_count': lambda match: match.value > 0},
                 formatter={'cd': int, 'cd_count': int},
                 children=True,
                 private_parent=True,
                 properties={'cd': [None], 'cd_count': [None]})
    rebulk.regex(r'(?P<cd_count>\d+)-?cds?',
                 validator={'cd': lambda match: match.value > 0, 'cd_count': lambda match: match.value > 0},
                 formatter={'cd_count': int},
                 children=True,
                 private_parent=True,
                 properties={'cd': [None], 'cd_count': [None]})

    return rebulk
Esempio n. 46
0
def size(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'size'))
    rebulk.regex_defaults(flags=re.IGNORECASE, abbreviations=[dash])
    rebulk.defaults(name='size', validator=seps_surround)
    rebulk.regex(r'\d+-?[mgt]b', r'\d+\.\d+-?[mgt]b', formatter=Size.fromstring, tags=['release-group-prefix'])

    return rebulk
Esempio n. 47
0
def title():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk().rules(TitleFromPosition, PreferTitleWithYear)

    def expected_title(input_string, context):
        """
        Expected title functional pattern.
        :param input_string:
        :type input_string:
        :param context:
        :type context:
        :return:
        :rtype:
        """
        ret = []
        for search in context.get('expected_title'):
            if search.startswith('re:'):
                search = search[3:]
                search = search.replace(' ', '-')
                matches = RePattern(search, abbreviations=[dash], flags=re.IGNORECASE).matches(input_string, context)
                for match in matches:
                    # Instance of 'list' has no 'span' member (no-member). Seems to be a pylint bug.
                    # pylint: disable=no-member
                    ret.append(match.span)
            else:
                for start in find_all(input_string, search, ignore_case=True):
                    ret.append((start, start+len(search)))
        return ret

    rebulk.functional(expected_title, name='title', tags=['expected'],
                      conflict_solver=lambda match, other: other,
                      disabled=lambda context: not context.get('expected_title'))

    return rebulk
Esempio n. 48
0
def streaming_service(config):  # pylint: disable=too-many-statements,unused-argument
    """Streaming service property.

    :param config: rule configuration
    :type config: dict
    :return:
    :rtype: Rebulk
    """
    rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'streaming_service'))
    rebulk = rebulk.string_defaults(ignore_case=True).regex_defaults(flags=re.IGNORECASE, abbreviations=[dash])
    rebulk.defaults(name='streaming_service', tags=['source-prefix'])

    for value, items in config.items():
        patterns = items if isinstance(items, list) else [items]
        for pattern in patterns:
            if pattern.startswith('re:'):
                rebulk.regex(pattern, value=value)
            else:
                rebulk.string(pattern, value=value)

    rebulk.rules(ValidateStreamingService)

    return rebulk
Esempio n. 49
0
    def when(self, matches, context):
        expected_rebulk = Rebulk().defaults(name='release_group')

        for expected_group in context.get('expected_group'):
            if expected_group.startswith('re:'):
                expected_group = expected_group[3:]
                expected_group = expected_group.replace(' ', '-')
                expected_rebulk.regex(expected_group, abbreviations=[dash], flags=re.IGNORECASE)
            else:
                expected_rebulk.string(expected_group, ignore_case=True)

        matches = expected_rebulk.matches(matches.input_string, context)
        return matches
Esempio n. 50
0
def film(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'film'))
    rebulk.regex_defaults(flags=re.IGNORECASE,
                          abbreviations=[dash
                                         ]).string_defaults(ignore_case=True)
    rebulk.defaults(name='film', validator=seps_surround)

    load_config_patterns(rebulk, config.get('film'))

    rebulk.rules(FilmTitleRule)

    return rebulk
Esempio n. 51
0
    def when(self, matches, context):
        expected_rebulk = Rebulk().defaults(name='release_group')

        for expected_group in context.get('expected_group'):
            if expected_group.startswith('re:'):
                expected_group = expected_group[3:]
                expected_group = expected_group.replace(' ', '-')
                expected_rebulk.regex(expected_group, abbreviations=[dash], flags=re.IGNORECASE)
            else:
                expected_rebulk.string(expected_group, ignore_case=True)

        matches = expected_rebulk.matches(matches.input_string, context)
        return matches
Esempio n. 52
0
def crc():
    """
    Builder for rebulk object.
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE)
    rebulk.defaults(validator=seps_surround)

    rebulk.regex('(?:[a-fA-F]|[0-9]){8}', name='crc32',
                 conflict_solver=lambda match, other: match
                 if other.name in ['episode', 'season']
                 else '__default__')

    rebulk.functional(guess_idnumber, name='uuid',
                      conflict_solver=lambda match, other: match
                      if other.name in ['episode', 'season']
                      else '__default__')
    return rebulk
Esempio n. 53
0
def crc(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'crc32'))
    rebulk = rebulk.regex_defaults(flags=re.IGNORECASE)
    rebulk.defaults(validator=seps_surround)

    rebulk.regex('(?:[a-fA-F]|[0-9]){8}', name='crc32',
                 conflict_solver=lambda match, other: other
                 if other.name in ['episode', 'season']
                 else '__default__')

    rebulk.functional(guess_idnumber, name='uuid',
                      conflict_solver=lambda match, other: match
                      if other.name in ['episode', 'season']
                      else '__default__')
    return rebulk
Esempio n. 54
0
def bit_rate(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk(disabled=lambda context: (is_disabled(context, 'audio_bit_rate')
                                              and is_disabled(context, 'video_bit_rate')))
    rebulk = rebulk.regex_defaults(flags=re.IGNORECASE, abbreviations=[dash])
    rebulk.defaults(name='audio_bit_rate', validator=seps_surround)
    rebulk.regex(r'\d+-?[kmg]b(ps|its?)', r'\d+\.\d+-?[kmg]b(ps|its?)',
                 conflict_solver=(
                     lambda match, other: match
                     if other.name == 'audio_channels' and 'weak-audio_channels' not in other.tags
                     else other
                 ),
                 formatter=BitRate.fromstring, tags=['release-group-prefix'])

    rebulk.rules(BitRateTypeRule)

    return rebulk
Esempio n. 55
0
def date(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk().defaults(validator=seps_surround)

    rebulk.regex(r"\d{4}", name="year", formatter=int,
                 disabled=lambda context: is_disabled(context, 'year'),
                 conflict_solver=lambda match, other: other
                 if other.name in ('episode', 'season') and len(other.raw) < len(match.raw)
                 else '__default__',
                 validator=lambda match: seps_surround(match) and valid_year(match.value))

    def date_functional(string, context):  # pylint:disable=inconsistent-return-statements
        """
        Search for date in the string and retrieves match

        :param string:
        :return:
        """

        ret = search_date(string, context.get('date_year_first'), context.get('date_day_first'))
        if ret:
            return ret[0], ret[1], {'value': ret[2]}

    rebulk.functional(date_functional, name="date", properties={'date': [None]},
                      disabled=lambda context: is_disabled(context, 'date'),
                      conflict_solver=lambda match, other: other
                      if other.name in ('episode', 'season', 'crc32')
                      else '__default__')

    rebulk.rules(KeepMarkedYearInFilepart)

    return rebulk
Esempio n. 56
0
def bonus(config):  # pylint:disable=unused-argument
    """
    Builder for rebulk object.

    :param config: rule configuration
    :type config: dict
    :return: Created Rebulk object
    :rtype: Rebulk
    """
    rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'bonus'))
    rebulk = rebulk.regex_defaults(flags=re.IGNORECASE)

    rebulk.regex(r'x(\d+)', name='bonus', private_parent=True, children=True, formatter=int,
                 validator={'__parent__': lambda match: seps_surround},
                 conflict_solver=lambda match, conflicting: match
                 if conflicting.name in ('video_codec', 'episode') and 'weak-episode' not in conflicting.tags
                 else '__default__')

    rebulk.rules(BonusTitleRule)

    return rebulk