Hello community, here is the log from the commit of package python-guessit for openSUSE:Factory checked in at 2019-09-04 09:14:22 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-guessit (Old) and /work/SRC/openSUSE:Factory/.python-guessit.new.7948 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-guessit" Wed Sep 4 09:14:22 2019 rev:8 rq:727929 version:3.1.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-guessit/python-guessit.changes 2019-06-12 13:16:09.940699246 +0200 +++ /work/SRC/openSUSE:Factory/.python-guessit.new.7948/python-guessit.changes 2019-09-04 09:15:44.198934857 +0200 @@ -1,0 +2,17 @@ +Tue Sep 3 06:44:13 UTC 2019 - Luigi Baldoni <[email protected]> + +- Update to version 3.1.0 + * Add python `3.8` support + * Use rebulk `2.*` + * Remove `v` from `subtitle_language` prefix in default + configuration + * Add `Variable Frame Rate` value to `other` property (VFR tag) + * Use episode words defined in configuration in a rebulk rule + * Avoid trigger of useless rules consequences + * Fix possible crash in weak episode removal + * Fix issue caused by `streaming_service` property conflicts + * Fix source validation when more than one pattern match + * Fix issue with some titles on multiple fileparts + * Fix issue related to website exclusion inside title + +------------------------------------------------------------------- Old: ---- guessit-3.0.4.tar.gz New: ---- guessit-3.1.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-guessit.spec ++++++ --- /var/tmp/diff_new_pack.guy7TA/_old 2019-09-04 09:15:45.050934739 +0200 +++ /var/tmp/diff_new_pack.guy7TA/_new 2019-09-04 09:15:45.054934738 +0200 @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-guessit -Version: 3.0.4 +Version: 3.1.0 Release: 0 Summary: A library for guessing information from video files License: LGPL-3.0-only @@ -30,13 +30,13 @@ BuildRequires: %{python_module pytest-benchmark} BuildRequires: %{python_module pytest-runner} BuildRequires: %{python_module python-dateutil} -BuildRequires: %{python_module rebulk >= 0.9.0} +BuildRequires: %{python_module rebulk >= 2.0.0} BuildRequires: %{python_module setuptools} BuildRequires: fdupes BuildRequires: python-rpm-macros Requires: python-babelfish >= 0.5.5 Requires: python-python-dateutil -Requires: python-rebulk >= 0.9.0 +Requires: python-rebulk >= 2.0.0 BuildArch: noarch %python_subpackages @@ -56,7 +56,7 @@ for i in {'common/comparators','common/date','common/expected','common/formatters','common/__init__','common/numeral','common/pattern','common/quantity','common/validators','common/words','__init__','markers/groups','markers/__init__','markers/path','processors'}; do sed -i -e "1d" "guessit/rules/$i.py" done -for i in {'api','backports','__init__','jsonutils','__main__','options','reutils','test/__init__','test/rules/__init__','test/rules/processors_test','test/test_api','test/test_api_unicode_literals','test/test_benchmark','test/test_main','test/test_options','test/test_yml','__version__','yamlutils'}; do +for i in {'api','backports','__init__','jsonutils','__main__','monkeypatch','options','reutils','test/__init__','test/rules/__init__','test/rules/processors_test','test/test_api','test/test_api_unicode_literals','test/test_benchmark','test/test_main','test/test_options','test/test_yml','__version__','yamlutils'}; do sed -i -e "1d" "guessit/$i.py" done ++++++ guessit-3.0.4.tar.gz -> guessit-3.1.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/HISTORY.rst new/guessit-3.1.0/HISTORY.rst --- old/guessit-3.0.4/HISTORY.rst 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/HISTORY.rst 2019-09-02 22:11:13.000000000 +0200 @@ -1,6 +1,22 @@ History ======= +3.1.0 (2019-09-02) +------------------ + +- Add python `3.8` support +- Use rebulk `2.*` +- Remove `v` from `subtitle_language` prefix in default configuration +- Add `Variable Frame Rate` value to `other` property (VFR tag) +- Use episode words defined in configuration in a rebulk rule +- Avoid trigger of useless rules consequences +- Fix possible crash in weak episode removal +- Fix issue caused by `streaming_service` property conflicts +- Fix source validation when more than one pattern match +- Fix issue with some titles on multiple fileparts +- Fix issue related to website exclusion inside title + + 3.0.4 (2019-06-04) ------------------ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/PKG-INFO new/guessit-3.1.0/PKG-INFO --- old/guessit-3.0.4/PKG-INFO 2019-06-04 23:30:58.000000000 +0200 +++ new/guessit-3.1.0/PKG-INFO 2019-09-02 22:11:14.000000000 +0200 @@ -1,12 +1,12 @@ Metadata-Version: 2.1 Name: guessit -Version: 3.0.4 +Version: 3.1.0 Summary: GuessIt - a library for guessing information from video filenames. Home-page: http://guessit.readthedocs.org/ Author: Rémi Alvergnat Author-email: [email protected] License: LGPLv3 -Download-URL: https://pypi.python.org/packages/source/g/guessit/guessit-3.0.4.tar.gz +Download-URL: https://pypi.python.org/packages/source/g/guessit/guessit-3.1.0.tar.gz Description: GuessIt ======= @@ -210,6 +210,22 @@ History ======= + 3.1.0 (2019-09-02) + ------------------ + + - Add python `3.8` support + - Use rebulk `2.*` + - Remove `v` from `subtitle_language` prefix in default configuration + - Add `Variable Frame Rate` value to `other` property (VFR tag) + - Use episode words defined in configuration in a rebulk rule + - Avoid trigger of useless rules consequences + - Fix possible crash in weak episode removal + - Fix issue caused by `streaming_service` property conflicts + - Fix source validation when more than one pattern match + - Fix issue with some titles on multiple fileparts + - Fix issue related to website exclusion inside title + + 3.0.4 (2019-06-04) ------------------ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/__version__.py new/guessit-3.1.0/guessit/__version__.py --- old/guessit-3.0.4/guessit/__version__.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/__version__.py 2019-09-02 22:11:13.000000000 +0200 @@ -4,4 +4,4 @@ Version module """ # pragma: no cover -__version__ = '3.0.4' +__version__ = '3.1.0' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/config/options.json new/guessit-3.1.0/guessit/config/options.json --- old/guessit-3.0.4/guessit/config/options.json 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/config/options.json 2019-09-02 22:11:13.000000000 +0200 @@ -290,7 +290,6 @@ ], "subtitle_prefixes": [ "st", - "v", "vost", "subforced", "fansub", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/rules/common/validators.py new/guessit-3.1.0/guessit/rules/common/validators.py --- old/guessit-3.0.4/guessit/rules/common/validators.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/rules/common/validators.py 2019-09-02 22:11:13.000000000 +0200 @@ -28,7 +28,7 @@ return False -def compose(*validators): +def and_(*validators): """ Compose validators functions :param validators: @@ -49,3 +49,26 @@ return False return True return composed + + +def or_(*validators): + """ + Compose validators functions + :param validators: + :type validators: + :return: + :rtype: + """ + def composed(string): + """ + Composed validators function + :param string: + :type string: + :return: + :rtype: + """ + for validator in validators: + if validator(string): + return True + return False + return composed diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/rules/match_processors.py new/guessit-3.1.0/guessit/rules/match_processors.py --- old/guessit-3.0.4/guessit/rules/match_processors.py 1970-01-01 01:00:00.000000000 +0100 +++ new/guessit-3.1.0/guessit/rules/match_processors.py 2019-09-02 22:11:13.000000000 +0200 @@ -0,0 +1,20 @@ +""" +Match processors +""" +from guessit.rules.common import seps + + +def strip(match, chars=seps): + """ + Strip given characters from match. + + :param chars: + :param match: + :return: + """ + while match.input_string[match.start] in chars: + match.start += 1 + while match.input_string[match.end - 1] in chars: + match.end -= 1 + if not match: + return False diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/rules/processors.py new/guessit-3.1.0/guessit/rules/processors.py --- old/guessit-3.0.4/guessit/rules/processors.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/rules/processors.py 2019-09-02 22:11:13.000000000 +0200 @@ -34,7 +34,9 @@ for match in matches.ending(group.end - 1): ending.append(match) - return starting, ending + if starting or ending: + return starting, ending + return False def then(self, matches, when_response, context): starting, ending = when_response diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/rules/properties/audio_codec.py new/guessit-3.1.0/guessit/rules/properties/audio_codec.py --- old/guessit-3.0.4/guessit/rules/properties/audio_codec.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/rules/properties/audio_codec.py 2019-09-02 22:11:13.000000000 +0200 @@ -3,9 +3,8 @@ """ audio_codec, audio_profile and audio_channels property """ -from rebulk.remodule import re - from rebulk import Rebulk, Rule, RemoveMatch +from rebulk.remodule import re from ..common import dash from ..common.pattern import is_disabled @@ -23,7 +22,9 @@ :return: Created Rebulk object :rtype: Rebulk """ - rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]).string_defaults(ignore_case=True) + rebulk = Rebulk()\ + .regex_defaults(flags=re.IGNORECASE, abbreviations=[dash])\ + .string_defaults(ignore_case=True) def audio_codec_priority(match1, match2): """ @@ -61,7 +62,9 @@ rebulk.string('PCM', value='PCM') rebulk.string('LPCM', value='LPCM') - rebulk.defaults(name='audio_profile', disabled=lambda context: is_disabled(context, 'audio_profile')) + rebulk.defaults(clear=True, + name='audio_profile', + disabled=lambda context: is_disabled(context, 'audio_profile')) rebulk.string('MA', value='Master Audio', tags=['audio_profile.rule', 'DTS-HD']) rebulk.string('HR', 'HRA', value='High Resolution Audio', tags=['audio_profile.rule', 'DTS-HD']) rebulk.string('ES', value='Extended Surround', tags=['audio_profile.rule', 'DTS']) @@ -70,7 +73,9 @@ rebulk.string('HQ', value='High Quality', tags=['audio_profile.rule', 'Dolby Digital']) rebulk.string('EX', value='EX', tags=['audio_profile.rule', 'Dolby Digital']) - rebulk.defaults(name="audio_channels", disabled=lambda context: is_disabled(context, 'audio_channels')) + rebulk.defaults(clear=True, + name="audio_channels", + disabled=lambda context: is_disabled(context, 'audio_channels')) rebulk.regex('7[01]', value='7.1', validator=seps_after, tags='weak-audio_channels') rebulk.regex('5[01]', value='5.1', validator=seps_after, tags='weak-audio_channels') rebulk.string('20', value='2.0', validator=seps_after, tags='weak-audio_channels') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/rules/properties/bit_rate.py new/guessit-3.1.0/guessit/rules/properties/bit_rate.py --- old/guessit-3.0.4/guessit/rules/properties/bit_rate.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/rules/properties/bit_rate.py 2019-09-02 22:11:13.000000000 +0200 @@ -69,4 +69,6 @@ else: to_rename.append(match) - return to_rename, to_remove + if to_rename or to_remove: + return to_rename, to_remove + return False diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/rules/properties/container.py new/guessit-3.1.0/guessit/rules/properties/container.py --- old/guessit-3.0.4/guessit/rules/properties/container.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/rules/properties/container.py 2019-09-02 22:11:13.000000000 +0200 @@ -44,7 +44,8 @@ rebulk.regex(r'\.'+build_or_pattern(torrent)+'$', exts=torrent, tags=['extension', 'torrent']) rebulk.regex(r'\.'+build_or_pattern(nzb)+'$', exts=nzb, tags=['extension', 'nzb']) - rebulk.defaults(name='container', + rebulk.defaults(clear=True, + name='container', validator=seps_surround, formatter=lambda s: s.lower(), conflict_solver=lambda match, other: match diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/rules/properties/episode_title.py new/guessit-3.1.0/guessit/rules/properties/episode_title.py --- old/guessit-3.0.4/guessit/rules/properties/episode_title.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/rules/properties/episode_title.py 2019-09-02 22:11:13.000000000 +0200 @@ -10,6 +10,7 @@ from ..common import seps, title_seps from ..common.formatters import cleanup from ..common.pattern import is_disabled +from ..common.validators import or_ from ..properties.title import TitleFromPosition, TitleBaseRule from ..properties.type import TypeProcessor @@ -133,8 +134,7 @@ def hole_filter(self, hole, matches): episode = matches.previous(hole, - lambda previous: any(name in previous.names - for name in self.previous_names), + lambda previous: previous.named(*self.previous_names), 0) crc32 = matches.named('crc32') @@ -179,8 +179,7 @@ predicate=lambda match: 'title' in match.tags, index=0) if main_title: episode = matches.previous(main_title, - lambda previous: any(name in previous.names - for name in self.previous_names), + lambda previous: previous.named(*self.previous_names), 0) crc32 = matches.named('crc32') @@ -249,7 +248,7 @@ if season: hole = matches.holes(subdirectory.start, subdirectory.end, - ignore=lambda match: 'weak-episode' in match.tags, + ignore=or_(lambda match: 'weak-episode' in match.tags, TitleBaseRule.is_ignored), formatter=cleanup, seps=title_seps, predicate=lambda match: match.value, index=0) if hole: @@ -292,7 +291,8 @@ season = (matches.range(directory.start, directory.end, lambda match: match.name == 'season', 0) or matches.range(filename.start, filename.end, lambda match: match.name == 'season', 0)) if season: - hole = matches.holes(directory.start, directory.end, ignore=lambda match: 'weak-episode' in match.tags, + hole = matches.holes(directory.start, directory.end, + ignore=or_(lambda match: 'weak-episode' in match.tags, TitleBaseRule.is_ignored), formatter=cleanup, seps=title_seps, predicate=lambda match: match.value, index=0) if hole: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/rules/properties/episodes.py new/guessit-3.1.0/guessit/rules/properties/episodes.py --- old/guessit-3.0.4/guessit/rules/properties/episodes.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/rules/properties/episodes.py 2019-09-02 22:11:13.000000000 +0200 @@ -11,12 +11,13 @@ from rebulk.remodule import re from rebulk.utils import is_iterable +from guessit.rules import match_processors +from guessit.rules.common.numeral import parse_numeral, numeral from .title import TitleFromPosition from ..common import dash, alt_dash, seps, seps_no_fs from ..common.formatters import strip -from ..common.numeral import numeral, parse_numeral from ..common.pattern import is_disabled -from ..common.validators import compose, seps_surround, seps_before, int_coercable +from ..common.validators import seps_surround, int_coercable, and_ from ...reutils import build_or_pattern @@ -29,17 +30,12 @@ :return: Created Rebulk object :rtype: Rebulk """ + # pylint: disable=too-many-branches,too-many-statements,too-many-locals def is_season_episode_disabled(context): """Whether season and episode rules should be enabled.""" return is_disabled(context, 'episode') or is_disabled(context, 'season') - rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE).string_defaults(ignore_case=True) - rebulk.defaults(private_names=['episodeSeparator', 'seasonSeparator', 'episodeMarker', 'seasonMarker']) - - episode_max_range = config['episode_max_range'] - season_max_range = config['season_max_range'] - def episodes_season_chain_breaker(matches): """ Break chains if there's more than 100 offset between two neighbor values. @@ -57,8 +53,6 @@ return True return False - rebulk.chain_defaults(chain_breaker=episodes_season_chain_breaker) - def season_episode_conflict_solver(match, other): """ Conflict solver for episode/season patterns @@ -76,7 +70,6 @@ if (other.name == 'audio_channels' and 'weak-audio_channels' not in other.tags and not match.initiator.children.named(match.name + 'Marker')) or ( other.name == 'screen_size' and not int_coercable(other.raw)): - return match if other.name in ('season', 'episode') and match.initiator != other.initiator: if (match.initiator.name in ('weak_episode', 'weak_duplicate') @@ -87,21 +80,6 @@ return current return '__default__' - season_words = config['season_words'] - episode_words = config['episode_words'] - of_words = config['of_words'] - all_words = config['all_words'] - season_markers = config['season_markers'] - season_ep_markers = config['season_ep_markers'] - disc_markers = config['disc_markers'] - episode_markers = config['episode_markers'] - range_separators = config['range_separators'] - weak_discrete_separators = list(sep for sep in seps_no_fs if sep not in range_separators) - strong_discrete_separators = config['discrete_separators'] - discrete_separators = strong_discrete_separators + weak_discrete_separators - - max_range_gap = config['max_range_gap'] - def ordering_validator(match): """ Validator for season list. They should be in natural order to be validated. @@ -135,55 +113,101 @@ lambda m: m.name == property_name + 'Separator') separator = match.children.previous(current_match, lambda m: m.name == property_name + 'Separator', 0) - if separator.raw not in range_separators and separator.raw in weak_discrete_separators: - if not 0 < current_match.value - previous_match.value <= max_range_gap + 1: - valid = False - if separator.raw in strong_discrete_separators: - valid = True - break + if separator: + if separator.raw not in range_separators and separator.raw in weak_discrete_separators: + if not 0 < current_match.value - previous_match.value <= max_range_gap + 1: + valid = False + if separator.raw in strong_discrete_separators: + valid = True + break previous_match = current_match return valid return is_consecutive('episode') and is_consecutive('season') + 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) + + season_words = config['season_words'] + episode_words = config['episode_words'] + of_words = config['of_words'] + all_words = config['all_words'] + season_markers = config['season_markers'] + season_ep_markers = config['season_ep_markers'] + disc_markers = config['disc_markers'] + episode_markers = config['episode_markers'] + range_separators = config['range_separators'] + weak_discrete_separators = list(sep for sep in seps_no_fs if sep not in range_separators) + strong_discrete_separators = config['discrete_separators'] + discrete_separators = strong_discrete_separators + weak_discrete_separators + episode_max_range = config['episode_max_range'] + season_max_range = config['season_max_range'] + max_range_gap = config['max_range_gap'] + + rebulk = Rebulk() \ + .regex_defaults(flags=re.IGNORECASE) \ + .string_defaults(ignore_case=True) \ + .chain_defaults(chain_breaker=episodes_season_chain_breaker) \ + .defaults(private_names=['episodeSeparator', 'seasonSeparator', 'episodeMarker', 'seasonMarker'], + formatter={'season': int, 'episode': int, 'version': int, 'count': int}, + children=True, + private_parent=True, + conflict_solver=season_episode_conflict_solver, + abbreviations=[alt_dash]) + # S01E02, 01x02, S01S02S03 - rebulk.chain(formatter={'season': int, 'episode': int}, - tags=['SxxExx'], - abbreviations=[alt_dash], - children=True, - private_parent=True, - validate_all=True, - validator={'__parent__': ordering_validator}, - conflict_solver=season_episode_conflict_solver, - disabled=is_season_episode_disabled) \ + rebulk.chain( + tags=['SxxExx'], + validate_all=True, + validator={'__parent__': and_(seps_surround, ordering_validator)}, + disabled=is_season_episode_disabled) \ + .defaults(tags=['SxxExx']) \ .regex(build_or_pattern(season_markers, name='seasonMarker') + r'(?P<season>\d+)@?' + - build_or_pattern(episode_markers + disc_markers, name='episodeMarker') + r'@?(?P<episode>\d+)', - validate_all=True, - validator={'__parent__': seps_before}).repeater('+') \ + build_or_pattern(episode_markers + disc_markers, name='episodeMarker') + r'@?(?P<episode>\d+)')\ + .repeater('+') \ .regex(build_or_pattern(episode_markers + disc_markers + discrete_separators + range_separators, name='episodeSeparator', escape=True) + - r'(?P<episode>\d+)').repeater('*') \ - .chain() \ + r'(?P<episode>\d+)').repeater('*') + + rebulk.chain(tags=['SxxExx'], + validate_all=True, + validator={'__parent__': and_(seps_surround, ordering_validator)}, + disabled=is_season_episode_disabled) \ + .defaults(tags=['SxxExx']) \ .regex(r'(?P<season>\d+)@?' + build_or_pattern(season_ep_markers, name='episodeMarker') + - r'@?(?P<episode>\d+)', - validate_all=True, - validator={'__parent__': seps_before}) \ - .chain() \ + r'@?(?P<episode>\d+)').repeater('+') \ + + rebulk.chain(tags=['SxxExx'], + validate_all=True, + validator={'__parent__': and_(seps_surround, ordering_validator)}, + disabled=is_season_episode_disabled) \ + .defaults(tags=['SxxExx']) \ .regex(r'(?P<season>\d+)@?' + build_or_pattern(season_ep_markers, name='episodeMarker') + - r'@?(?P<episode>\d+)', - validate_all=True, - validator={'__parent__': seps_before}) \ + r'@?(?P<episode>\d+)') \ .regex(build_or_pattern(season_ep_markers + discrete_separators + range_separators, name='episodeSeparator', escape=True) + - r'(?P<episode>\d+)').repeater('*') \ - .chain() \ - .regex(build_or_pattern(season_markers, name='seasonMarker') + r'(?P<season>\d+)', - validate_all=True, - validator={'__parent__': seps_before}) \ + r'(?P<episode>\d+)').repeater('*') + + rebulk.chain(tags=['SxxExx'], + validate_all=True, + validator={'__parent__': and_(seps_surround, ordering_validator)}, + disabled=is_season_episode_disabled) \ + .defaults(tags=['SxxExx']) \ + .regex(build_or_pattern(season_markers, name='seasonMarker') + r'(?P<season>\d+)') \ + .regex('(?P<other>Extras)', name='other', value='Extras', tags=['no-release-group-prefix']).repeater('?') \ .regex(build_or_pattern(season_markers + discrete_separators + range_separators, name='seasonSeparator', escape=True) + @@ -191,132 +215,125 @@ # episode_details property for episode_detail in ('Special', 'Pilot', 'Unaired', 'Final'): - rebulk.string(episode_detail, value=episode_detail, name='episode_details', + rebulk.string(episode_detail, + private_parent=False, + children=False, + value=episode_detail, + name='episode_details', disabled=lambda context: is_disabled(context, 'episode_details')) - 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.defaults(private_names=['episodeSeparator', 'seasonSeparator', 'episodeMarker', 'seasonMarker'], - validate_all=True, validator={'__parent__': seps_surround}, children=True, private_parent=True, + validate_all=True, + validator={'__parent__': and_(seps_surround, ordering_validator)}, + children=True, + private_parent=True, conflict_solver=season_episode_conflict_solver) - rebulk.chain(abbreviations=[alt_dash], + rebulk.chain(validate_all=True, + conflict_solver=season_episode_conflict_solver, formatter={'season': parse_numeral, 'count': parse_numeral}, - validator={'__parent__': compose(seps_surround, ordering_validator), + validator={'__parent__': and_(seps_surround, ordering_validator), 'season': validate_roman, 'count': validate_roman}, disabled=lambda context: context.get('type') == 'movie' or is_disabled(context, 'season')) \ - .defaults(validator=None) \ + .defaults(formatter={'season': parse_numeral, 'count': parse_numeral}, + validator={'season': validate_roman, 'count': validate_roman}, + conflict_solver=season_episode_conflict_solver) \ .regex(build_or_pattern(season_words, name='seasonMarker') + '@?(?P<season>' + numeral + ')') \ .regex(r'' + build_or_pattern(of_words) + '@?(?P<count>' + numeral + ')').repeater('?') \ .regex(r'@?' + build_or_pattern(range_separators + discrete_separators + ['@'], name='seasonSeparator', escape=True) + r'@?(?P<season>\d+)').repeater('*') + rebulk.defaults(abbreviations=[dash]) + rebulk.regex(build_or_pattern(episode_words, name='episodeMarker') + r'-?(?P<episode>\d+)' + r'(?:v(?P<version>\d+))?' + r'(?:-?' + build_or_pattern(of_words) + r'-?(?P<count>\d+))?', # Episode 4 - abbreviations=[dash], formatter={'episode': int, 'version': int, 'count': int}, disabled=lambda context: context.get('type') == 'episode' or is_disabled(context, 'episode')) rebulk.regex(build_or_pattern(episode_words, name='episodeMarker') + r'-?(?P<episode>' + numeral + ')' + r'(?:v(?P<version>\d+))?' + r'(?:-?' + build_or_pattern(of_words) + r'-?(?P<count>\d+))?', # Episode 4 - abbreviations=[dash], validator={'episode': validate_roman}, - formatter={'episode': parse_numeral, 'version': int, 'count': int}, + formatter={'episode': parse_numeral}, disabled=lambda context: context.get('type') != 'episode' or is_disabled(context, 'episode')) rebulk.regex(r'S?(?P<season>\d+)-?(?:xE|Ex|E|x)-?(?P<other>' + build_or_pattern(all_words) + ')', tags=['SxxExx'], - abbreviations=[dash], - validator=None, - formatter={'season': int, 'other': lambda match: 'Complete'}, + formatter={'other': lambda match: 'Complete'}, disabled=lambda context: is_disabled(context, 'season')) # 12, 13 - rebulk.chain(tags=['weak-episode'], formatter={'episode': int, 'version': int}, + rebulk.chain(tags=['weak-episode'], disabled=lambda context: context.get('type') == 'movie' or is_disabled(context, 'episode')) \ - .defaults(validator=None) \ + .defaults(validator=None, tags=['weak-episode']) \ .regex(r'(?P<episode>\d{2})') \ .regex(r'v(?P<version>\d+)').repeater('?') \ - .regex(r'(?P<episodeSeparator>[x-])(?P<episode>\d{2})').repeater('*') + .regex(r'(?P<episodeSeparator>[x-])(?P<episode>\d{2})', abbreviations=None).repeater('*') # 012, 013 - rebulk.chain(tags=['weak-episode'], formatter={'episode': int, 'version': int}, + rebulk.chain(tags=['weak-episode'], disabled=lambda context: context.get('type') == 'movie' or is_disabled(context, 'episode')) \ - .defaults(validator=None) \ + .defaults(validator=None, tags=['weak-episode']) \ .regex(r'0(?P<episode>\d{1,2})') \ .regex(r'v(?P<version>\d+)').repeater('?') \ - .regex(r'(?P<episodeSeparator>[x-])0(?P<episode>\d{1,2})').repeater('*') + .regex(r'(?P<episodeSeparator>[x-])0(?P<episode>\d{1,2})', abbreviations=None).repeater('*') # 112, 113 rebulk.chain(tags=['weak-episode'], - formatter={'episode': int, 'version': int}, name='weak_episode', disabled=lambda context: context.get('type') == 'movie' or is_disabled(context, 'episode')) \ - .defaults(validator=None) \ + .defaults(validator=None, tags=['weak-episode'], name='weak_episode') \ .regex(r'(?P<episode>\d{3,4})') \ .regex(r'v(?P<version>\d+)').repeater('?') \ - .regex(r'(?P<episodeSeparator>[x-])(?P<episode>\d{3,4})').repeater('*') + .regex(r'(?P<episodeSeparator>[x-])(?P<episode>\d{3,4})', abbreviations=None).repeater('*') # 1, 2, 3 - rebulk.chain(tags=['weak-episode'], formatter={'episode': int, 'version': int}, + rebulk.chain(tags=['weak-episode'], disabled=lambda context: context.get('type') != 'episode' or is_disabled(context, 'episode')) \ - .defaults(validator=None) \ + .defaults(validator=None, tags=['weak-episode']) \ .regex(r'(?P<episode>\d)') \ .regex(r'v(?P<version>\d+)').repeater('?') \ - .regex(r'(?P<episodeSeparator>[x-])(?P<episode>\d{1,2})').repeater('*') + .regex(r'(?P<episodeSeparator>[x-])(?P<episode>\d{1,2})', abbreviations=None).repeater('*') # e112, e113, 1e18, 3e19 - # TODO: Enhance rebulk for validator to be used globally (season_episode_validator) - rebulk.chain(formatter={'season': int, 'episode': int, 'version': int}, - disabled=lambda context: is_disabled(context, 'episode')) \ + rebulk.chain(disabled=lambda context: is_disabled(context, 'episode')) \ .defaults(validator=None) \ .regex(r'(?P<season>\d{1,2})?(?P<episodeMarker>e)(?P<episode>\d{1,4})') \ .regex(r'v(?P<version>\d+)').repeater('?') \ - .regex(r'(?P<episodeSeparator>e|x|-)(?P<episode>\d{1,4})').repeater('*') + .regex(r'(?P<episodeSeparator>e|x|-)(?P<episode>\d{1,4})', abbreviations=None).repeater('*') # ep 112, ep113, ep112, ep113 - rebulk.chain(abbreviations=[dash], formatter={'episode': int, 'version': int}, - disabled=lambda context: is_disabled(context, 'episode')) \ + rebulk.chain(disabled=lambda context: is_disabled(context, 'episode')) \ .defaults(validator=None) \ .regex(r'ep-?(?P<episode>\d{1,4})') \ .regex(r'v(?P<version>\d+)').repeater('?') \ - .regex(r'(?P<episodeSeparator>ep|e|x|-)(?P<episode>\d{1,4})').repeater('*') + .regex(r'(?P<episodeSeparator>ep|e|x|-)(?P<episode>\d{1,4})', abbreviations=None).repeater('*') # cap 112, cap 112_114 - rebulk.chain(abbreviations=[dash], - tags=['see-pattern'], - formatter={'season': int, 'episode': int}, + rebulk.chain(tags=['see-pattern'], disabled=is_season_episode_disabled) \ - .defaults(validator=None) \ + .defaults(validator=None, tags=['see-pattern']) \ .regex(r'(?P<seasonMarker>cap)-?(?P<season>\d{1,2})(?P<episode>\d{2})') \ .regex(r'(?P<episodeSeparator>-)(?P<season>\d{1,2})(?P<episode>\d{2})').repeater('?') # 102, 0102 rebulk.chain(tags=['weak-episode', 'weak-duplicate'], - formatter={'season': int, 'episode': int, 'version': int}, name='weak_duplicate', conflict_solver=season_episode_conflict_solver, disabled=lambda context: (context.get('episode_prefer_number', False) or context.get('type') == 'movie') or is_season_episode_disabled(context)) \ - .defaults(validator=None) \ + .defaults(tags=['weak-episode', 'weak-duplicate'], + name='weak_duplicate', + validator=None, + conflict_solver=season_episode_conflict_solver) \ .regex(r'(?P<season>\d{1,2})(?P<episode>\d{2})') \ .regex(r'v(?P<version>\d+)').repeater('?') \ - .regex(r'(?P<episodeSeparator>x|-)(?P<episode>\d{2})').repeater('*') + .regex(r'(?P<episodeSeparator>x|-)(?P<episode>\d{2})', abbreviations=None).repeater('*') - rebulk.regex(r'v(?P<version>\d+)', children=True, private_parent=True, formatter=int, + rebulk.regex(r'v(?P<version>\d+)', + formatter=int, disabled=lambda context: is_disabled(context, 'version')) rebulk.defaults(private_names=['episodeSeparator', 'seasonSeparator']) @@ -325,18 +342,23 @@ # detached of X count (season/episode) rebulk.regex(r'(?P<episode>\d+)-?' + build_or_pattern(of_words) + r'-?(?P<count>\d+)-?' + build_or_pattern(episode_words) + '?', - abbreviations=[dash], children=True, private_parent=True, formatter=int, + formatter=int, + pre_match_processor=match_processors.strip, disabled=lambda context: is_disabled(context, 'episode')) - rebulk.regex(r'Minisodes?', name='episode_format', value="Minisode", + rebulk.regex(r'Minisodes?', + children=False, + private_parent=False, + name='episode_format', + value="Minisode", disabled=lambda context: is_disabled(context, 'episode_format')) rebulk.rules(WeakConflictSolver, RemoveInvalidSeason, RemoveInvalidEpisode, SeePatternRange(range_separators + ['_']), EpisodeNumberSeparatorRange(range_separators), - SeasonSeparatorRange(range_separators), RemoveWeakIfMovie, RemoveWeakIfSxxExx, - RemoveWeakDuplicate, EpisodeDetailValidator, RemoveDetachedEpisodeNumber, VersionValidator, - RemoveWeak, RenameToAbsoluteEpisode, CountValidator, EpisodeSingleDigitValidator, RenameToDiscMatch) + SeasonSeparatorRange(range_separators), RemoveWeakIfMovie, RemoveWeakIfSxxExx, RemoveWeakDuplicate, + EpisodeDetailValidator, RemoveDetachedEpisodeNumber, VersionValidator, RemoveWeak(episode_words), + RenameToAbsoluteEpisode, CountValidator, EpisodeSingleDigitValidator, RenameToDiscMatch) return rebulk @@ -416,7 +438,9 @@ if to_append: to_remove.extend(weak_dup_matches) - return to_remove, to_append + if to_remove or to_append: + return to_remove, to_append + return False class CountValidator(Rule): @@ -442,7 +466,9 @@ season_count.append(count) else: to_remove.append(count) - return to_remove, episode_count, season_count + if to_remove or episode_count or season_count: + return to_remove, episode_count, season_count + return False class SeePatternRange(Rule): @@ -477,7 +503,9 @@ to_remove.append(separator) - return to_remove, to_append + if to_remove or to_append: + return to_remove, to_append + return False class AbstractSeparatorRange(Rule): @@ -533,7 +561,9 @@ previous_match = next_match - return to_remove, to_append + if to_remove or to_append: + return to_remove, to_append + return False class RenameToAbsoluteEpisode(Rule): @@ -631,29 +661,39 @@ priority = 16 consequence = RemoveMatch, AppendMatch + def __init__(self, episode_words): + super(RemoveWeak, self).__init__() + self.episode_words = episode_words + def when(self, matches, context): to_remove = [] to_append = [] for filepart in matches.markers.named('path'): weaks = matches.range(filepart.start, filepart.end, predicate=lambda m: 'weak-episode' in m.tags) if weaks: - previous = matches.previous(weaks[0], predicate=lambda m: m.name in ( + weak = weaks[0] + previous = matches.previous(weak, predicate=lambda m: m.name in ( 'audio_codec', 'screen_size', 'streaming_service', 'source', 'video_profile', 'audio_channels', 'audio_profile'), index=0) if previous and not matches.holes( - previous.end, weaks[0].start, predicate=lambda m: m.raw.strip(seps)): - if previous.raw.lower() == 'ep': - episode = copy.copy(weaks[0]) - episode.name = 'episode' - episode.value = int(weaks[0].value) - episode.start = previous.start - episode.private = False - episode.tags = [] - - to_append.append(episode) + previous.end, weak.start, predicate=lambda m: m.raw.strip(seps)): + if previous.raw.lower() in self.episode_words: + try: + episode = copy.copy(weak) + episode.name = 'episode' + episode.value = int(weak.value) + episode.start = previous.start + episode.private = False + episode.tags = [] + + to_append.append(episode) + except ValueError: + pass to_remove.extend(weaks) - return to_remove, to_append + if to_remove or to_append: + return to_remove, to_append + return False class RemoveWeakIfSxxExx(Rule): @@ -867,4 +907,6 @@ markers.append(marker) discs.extend(sorted(marker.initiator.children.named('episode'), key=lambda m: m.value)) - return discs, markers, to_remove + if discs or markers or to_remove: + return discs, markers, to_remove + return False diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/rules/properties/language.py new/guessit-3.1.0/guessit/rules/properties/language.py --- old/guessit-3.0.4/guessit/rules/properties/language.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/rules/properties/language.py 2019-09-02 22:11:13.000000000 +0200 @@ -390,7 +390,9 @@ to_remove.extend(matches.conflicting(lang)) if prefix in to_remove: to_remove.remove(prefix) - return to_rename, to_remove + if to_rename or to_remove: + return to_rename, to_remove + return False def then(self, matches, when_response, context): to_rename, to_remove = when_response @@ -427,7 +429,9 @@ to_append.append(lang) if suffix in to_remove: to_remove.remove(suffix) - return to_append, to_remove + if to_append or to_remove: + return to_append, to_remove + return False def then(self, matches, when_response, context): to_rename, to_remove = when_response diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/rules/properties/other.py new/guessit-3.1.0/guessit/rules/properties/other.py --- old/guessit-3.0.4/guessit/rules/properties/other.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/rules/properties/other.py 2019-09-02 22:11:13.000000000 +0200 @@ -11,7 +11,7 @@ from ..common import dash from ..common import seps from ..common.pattern import is_disabled -from ..common.validators import seps_after, seps_before, seps_surround, compose +from ..common.validators import seps_after, seps_before, seps_surround, and_ from ...reutils import build_or_pattern from ...rules.common.formatters import raw_cleanup @@ -77,11 +77,12 @@ private_names=['completeArticle', 'completeWordsBefore', 'completeWordsAfter'], value={'other': 'Complete'}, tags=['release-group-prefix'], - validator={'__parent__': compose(seps_surround, validate_complete)}) + validator={'__parent__': and_(seps_surround, validate_complete)}) rebulk.string('R5', value='Region 5') rebulk.string('RC', value='Region C') rebulk.regex('Pre-?Air', value='Preair') - rebulk.regex('(?:PS-?)?Vita', value='PS Vita') + rebulk.regex('(?:PS-?)Vita', value='PS Vita') + rebulk.regex('Vita', value='PS Vita', tags='has-neighbor') rebulk.regex('(HD)(?P<another>Rip)', value={'other': 'HD', 'another': 'Rip'}, private_parent=True, children=True, validator={'__parent__': seps_surround}, validate_all=True) @@ -96,6 +97,7 @@ rebulk.string('mHD', 'HDLight', value='Micro HD') rebulk.string('LDTV', value='Low Definition') rebulk.string('HFR', value='High Frame Rate') + rebulk.string('VFR', value='Variable Frame Rate') rebulk.string('HD', value='HD', validator=None, tags=['streaming_service.prefix', 'streaming_service.suffix']) rebulk.regex('Full-?HD', 'FHD', value='Full HD', validator=None, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/rules/properties/part.py new/guessit-3.1.0/guessit/rules/properties/part.py --- old/guessit-3.0.4/guessit/rules/properties/part.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/rules/properties/part.py 2019-09-02 22:11:13.000000000 +0200 @@ -8,7 +8,7 @@ from rebulk import Rebulk from ..common import dash from ..common.pattern import is_disabled -from ..common.validators import seps_surround, int_coercable, compose +from ..common.validators import seps_surround, int_coercable, and_ from ..common.numeral import numeral, parse_numeral from ...reutils import build_or_pattern @@ -41,6 +41,6 @@ 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)}) + validator={'part': and_(validate_roman, lambda m: 0 < m.value < 100)}) return rebulk diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/rules/properties/release_group.py new/guessit-3.1.0/guessit/rules/properties/release_group.py --- old/guessit-3.0.4/guessit/rules/properties/release_group.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/rules/properties/release_group.py 2019-09-02 22:11:13.000000000 +0200 @@ -9,8 +9,8 @@ from rebulk.match import Match from ..common import seps -from ..common.expected import build_expected_function from ..common.comparators import marker_sorted +from ..common.expected import build_expected_function from ..common.formatters import cleanup from ..common.pattern import is_disabled from ..common.validators import int_coercable, seps_surround @@ -72,7 +72,9 @@ 'audio_channels', 'screen_size', 'other', 'container', 'language', 'subtitle_language', 'subtitle_language.suffix', 'subtitle_language.prefix', 'language.suffix') -_scene_previous_tags = ('release-group-prefix', ) +_scene_previous_tags = ('release-group-prefix',) + +_scene_no_previous_tags = ('no-release-group-prefix',) class DashSeparatedReleaseGroup(Rule): @@ -193,7 +195,8 @@ if releasegroup.value: to_append.append(releasegroup) - return to_remove, to_append + if to_remove or to_append: + return to_remove, to_append class SceneReleaseGroup(Rule): @@ -212,6 +215,17 @@ super(SceneReleaseGroup, self).__init__() self.value_formatter = value_formatter + @staticmethod + def is_previous_match(match): + """ + Check if match can precede release_group + + :param match: + :return: + """ + return not match.tagged(*_scene_no_previous_tags) if match.name in _scene_previous_names else \ + match.tagged(*_scene_previous_tags) + def when(self, matches, context): # pylint:disable=too-many-locals # If a release_group is found before, ignore this kind of release_group rule. @@ -253,13 +267,12 @@ if match.start < filepart.start: return False - return not match.private or match.name in _scene_previous_names + return not match.private or self.is_previous_match(match) previous_match = matches.previous(last_hole, previous_match_filter, index=0) - if previous_match and (previous_match.name in _scene_previous_names or - any(tag in previous_match.tags for tag in _scene_previous_tags)) and \ + if previous_match and (self.is_previous_match(previous_match)) and \ not matches.input_string[previous_match.end:last_hole.start].strip(seps) \ and not int_coercable(last_hole.value.strip(seps)): @@ -300,11 +313,11 @@ # If a release_group is found before, ignore this kind of release_group rule. if matches.named('release_group'): - return to_remove, to_append + return False if not matches.named('episode') and not matches.named('season') and matches.named('release_group'): # This doesn't seems to be an anime, and we already found another release_group. - return to_remove, to_append + return False for filepart in marker_sorted(matches.markers.named('path'), matches): @@ -328,4 +341,7 @@ to_append.append(group) to_remove.extend(matches.range(empty_group.start, empty_group.end, lambda m: 'weak-language' in m.tags)) - return to_remove, to_append + + if to_remove or to_append: + return to_remove, to_append + return False diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/rules/properties/source.py new/guessit-3.1.0/guessit/rules/properties/source.py --- old/guessit-3.0.4/guessit/rules/properties/source.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/rules/properties/source.py 2019-09-02 22:11:13.000000000 +0200 @@ -12,7 +12,7 @@ from .audio_codec import HqConflictRule from ..common import dash, seps from ..common.pattern import is_disabled -from ..common.validators import seps_before, seps_after +from ..common.validators import seps_before, seps_after, or_ def source(config): # pylint:disable=unused-argument @@ -26,7 +26,10 @@ """ rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'source')) rebulk = rebulk.regex_defaults(flags=re.IGNORECASE, abbreviations=[dash], private_parent=True, children=True) - rebulk.defaults(name='source', tags=['video-codec-prefix', 'streaming_service.suffix']) + rebulk = rebulk.defaults(name='source', + tags=['video-codec-prefix', 'streaming_service.suffix'], + validate_all=True, + validator={'__parent__': or_(seps_before, seps_after)}) rip_prefix = '(?P<other>Rip)-?' rip_suffix = '-?(?P<other>Rip)' @@ -42,7 +45,7 @@ def demote_other(match, other): # pylint: disable=unused-argument """Default conflict solver with 'other' property.""" - return other if other.name == 'other' else '__default__' + return other if other.name == 'other' or other.name == 'release_group' else '__default__' rebulk.regex(*build_source_pattern('VHS', suffix=rip_optional_suffix), value={'source': 'VHS', 'other': 'Rip'}) @@ -119,7 +122,7 @@ rebulk.regex(*build_source_pattern('DSR?', 'SAT', suffix=rip_suffix), value={'source': 'Satellite', 'other': 'Rip'}) - rebulk.rules(ValidateSource, UltraHdBlurayRule) + rebulk.rules(ValidateSourcePrefixSuffix, ValidateWeakSource, UltraHdBlurayRule) return rebulk @@ -171,10 +174,12 @@ to_remove.append(match) to_append.append(new_source) - return to_remove, to_append + if to_remove or to_append: + return to_remove, to_append + return False -class ValidateSource(Rule): +class ValidateSourcePrefixSuffix(Rule): """ Validate source with source prefix, source suffix. """ @@ -201,6 +206,21 @@ ret.append(match) continue + return ret + + +class ValidateWeakSource(Rule): + """ + Validate weak source + """ + dependency = [ValidateSourcePrefixSuffix] + priority = 64 + consequence = RemoveMatch + + def when(self, matches, context): + ret = [] + for filepart in matches.markers.named('path'): + for match in matches.range(filepart.start, filepart.end, predicate=lambda m: m.name == 'source'): # if there are more than 1 source in this filepart, just before the year and with holes for the title # most likely the source is part of the title if 'weak.source' in match.tags \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/rules/properties/streaming_service.py new/guessit-3.1.0/guessit/rules/properties/streaming_service.py --- old/guessit-3.0.4/guessit/rules/properties/streaming_service.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/rules/properties/streaming_service.py 2019-09-02 22:11:13.000000000 +0200 @@ -41,7 +41,7 @@ class ValidateStreamingService(Rule): """Validate streaming service matches.""" - priority = 32 + priority = 128 consequence = RemoveMatch def when(self, matches, context): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/rules/properties/title.py new/guessit-3.1.0/guessit/rules/properties/title.py --- old/guessit-3.0.4/guessit/rules/properties/title.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/rules/properties/title.py 2019-09-02 22:11:13.000000000 +0200 @@ -104,7 +104,8 @@ return cropped_holes - def is_ignored(self, match): + @staticmethod + def is_ignored(match): """ Ignore matches when scanning for title (hole). @@ -251,7 +252,7 @@ to_remove = [] if matches.named(self.match_name, lambda match: 'expected' in match.tags): - return ret, to_remove + return False fileparts = [filepart for filepart in list(marker_sorted(matches.markers.named('path'), matches)) if not self.filepart_filter or self.filepart_filter(filepart, matches)] @@ -284,7 +285,9 @@ ret.extend(titles) to_remove.extend(to_remove_c) - return ret, to_remove + if ret or to_remove: + return ret, to_remove + return False class TitleFromPosition(TitleBaseRule): @@ -341,4 +344,6 @@ for title_match in titles: if title_match.value not in title_values: to_remove.append(title_match) - return to_remove, to_tag + if to_remove or to_tag: + return to_remove, to_tag + return False diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/rules/properties/video_codec.py new/guessit-3.1.0/guessit/rules/properties/video_codec.py --- old/guessit-3.0.4/guessit/rules/properties/video_codec.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/rules/properties/video_codec.py 2019-09-02 22:11:13.000000000 +0200 @@ -3,9 +3,8 @@ """ video_codec and video_profile property """ -from rebulk.remodule import re - from rebulk import Rebulk, Rule, RemoveMatch +from rebulk.remodule import re from ..common import dash from ..common.pattern import is_disabled @@ -43,7 +42,8 @@ # http://blog.mediacoderhq.com/h264-profiles-and-levels/ # https://en.wikipedia.org/wiki/H.264/MPEG-4_AVC - rebulk.defaults(name="video_profile", + rebulk.defaults(clear=True, + name="video_profile", validator=seps_surround, disabled=lambda context: is_disabled(context, 'video_profile')) @@ -66,7 +66,8 @@ rebulk.string('DXVA', value='DXVA', name='video_api', disabled=lambda context: is_disabled(context, 'video_api')) - rebulk.defaults(name='color_depth', + rebulk.defaults(clear=True, + name='color_depth', validator=seps_surround, disabled=lambda context: is_disabled(context, 'color_depth')) rebulk.regex('12.?bits?', value='12-bit') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/rules/properties/website.py new/guessit-3.1.0/guessit/rules/properties/website.py --- old/guessit-3.0.4/guessit/rules/properties/website.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/rules/properties/website.py 2019-09-02 22:11:13.000000000 +0200 @@ -67,7 +67,7 @@ """ Validator for next website matches """ - return any(name in ['season', 'episode', 'year'] for name in match.names) + return match.named('season', 'episode', 'year') def when(self, matches, context): to_remove = [] @@ -80,7 +80,9 @@ if not safe: suffix = matches.next(website_match, PreferTitleOverWebsite.valid_followers, 0) if suffix: - to_remove.append(website_match) + group = matches.markers.at_match(website_match, lambda marker: marker.name == 'group', 0) + if not group: + to_remove.append(website_match) return to_remove rebulk.rules(PreferTitleOverWebsite, ValidateWebsitePrefix) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/test/episodes.yml new/guessit-3.1.0/guessit/test/episodes.yml --- old/guessit-3.0.4/guessit/test/episodes.yml 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/test/episodes.yml 2019-09-02 22:11:13.000000000 +0200 @@ -201,9 +201,9 @@ ? Series/My Name Is Earl/My.Name.Is.Earl.S01Extras.-.Bad.Karma.DVDRip.XviD.avi : title: My Name Is Earl season: 1 - episode_title: Extras - Bad Karma + episode_title: Bad Karma source: DVD - other: Rip + other: [Extras, Rip] video_codec: Xvid ? series/Freaks And Geeks/Season 1/Episode 4 - Kim Kelly Is My Friend-eng(1).srt @@ -3029,7 +3029,7 @@ title: Show Name episode: [493, 494, 495, 496, 497, 498, 500, 501, 502, 503, 504, 505, 506, 507] screen_size: 720p - subtitle_language: fr + other: Variable Frame Rate video_codec: H.264 audio_codec: AAC type: episode diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/test/test_yml.py new/guessit-3.1.0/guessit/test/test_yml.py --- old/guessit-3.0.4/guessit/test/test_yml.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/test/test_yml.py 2019-09-02 22:11:13.000000000 +0200 @@ -7,7 +7,6 @@ from io import open # pylint: disable=redefined-builtin import babelfish -import pytest # pylint:disable=wrong-import-order import six # pylint:disable=wrong-import-order import yaml # pylint:disable=wrong-import-order from rebulk.remodule import re @@ -21,13 +20,6 @@ __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) -filename_predicate = None -string_predicate = None - - -# filename_predicate = lambda filename: 'episode_title' in filename -# string_predicate = lambda string: '-DVD.BlablaBla.Fix.Blablabla.XVID' in string - class EntryResult(object): def __init__(self, string, negates=False): @@ -134,7 +126,49 @@ options_re = re.compile(r'^([ +-]+)(.*)') - files, ids = files_and_ids(filename_predicate) + def _get_unique_id(self, collection, base_id): + ret = base_id + i = 2 + while ret in collection: + suffix = "-" + str(i) + ret = base_id + suffix + i += 1 + return ret + + def pytest_generate_tests(self, metafunc): + if 'yml_test_case' in metafunc.fixturenames: + entries = [] + entry_ids = [] + entry_set = set() + + for filename, _ in zip(*files_and_ids()): + with open(os.path.join(__location__, filename), 'r', encoding='utf-8') as infile: + data = yaml.load(infile, OrderedDictYAMLLoader) + + last_expected = None + for string, expected in reversed(list(data.items())): + if expected is None: + data[string] = last_expected + else: + last_expected = expected + + default = None + try: + default = data['__default__'] + del data['__default__'] + except KeyError: + pass + + for string, expected in data.items(): + TestYml.set_default(expected, default) + string = TestYml.fix_encoding(string, expected) + + entries.append((filename, string, expected)) + unique_id = self._get_unique_id(entry_set, '[' + filename + '] ' + str(string)) + entry_set.add(unique_id) + entry_ids.append(unique_id) + + metafunc.parametrize('yml_test_case', entries, ids=entry_ids) @staticmethod def set_default(expected, default): @@ -143,34 +177,8 @@ if k not in expected: expected[k] = v - @pytest.mark.parametrize('filename', files, ids=ids) - def test(self, filename, caplog): - caplog.set_level(logging.INFO) - with open(os.path.join(__location__, filename), 'r', encoding='utf-8') as infile: - data = yaml.load(infile, OrderedDictYAMLLoader) - entries = Results() - - last_expected = None - for string, expected in reversed(list(data.items())): - if expected is None: - data[string] = last_expected - else: - last_expected = expected - - default = None - try: - default = data['__default__'] - del data['__default__'] - except KeyError: - pass - - for string, expected in data.items(): - TestYml.set_default(expected, default) - entry = self.check_data(filename, string, expected) - entries.append(entry) - entries.assert_ok() - - def check_data(self, filename, string, expected): + @classmethod + def fix_encoding(cls, string, expected): if six.PY2: if isinstance(string, six.text_type): string = string.encode('utf-8') @@ -183,16 +191,23 @@ expected[k] = v if not isinstance(string, str): string = str(string) - if not string_predicate or string_predicate(string): # pylint: disable=not-callable - entry = self.check(string, expected) - if entry.ok: - logger.debug('[%s] %s', filename, entry) - elif entry.warning: - logger.warning('[%s] %s', filename, entry) - elif entry.error: - logger.error('[%s] %s', filename, entry) - for line in entry.details: - logger.error('[%s] %s', filename, ' ' * 4 + line) + return string + + def test_entry(self, yml_test_case): + filename, string, expected = yml_test_case + result = self.check_data(filename, string, expected) + assert not result.error + + def check_data(self, filename, string, expected): + entry = self.check(string, expected) + if entry.ok: + logger.debug('[%s] %s', filename, entry) + elif entry.warning: + logger.warning('[%s] %s', filename, entry) + elif entry.error: + logger.error('[%s] %s', filename, entry) + for line in entry.details: + logger.error('[%s] %s', filename, ' ' * 4 + line) return entry def check(self, string, expected): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit/test/various.yml new/guessit-3.1.0/guessit/test/various.yml --- old/guessit-3.0.4/guessit/test/various.yml 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/guessit/test/various.yml 2019-09-02 22:11:13.000000000 +0200 @@ -1114,3 +1114,86 @@ video_codec: H.264 release_group: W4F type: episode + +? NOS4A2.S01E01.The.Shorter.Way.REPACK.720p.AMZN.WEB-DL.DDP5.1.H.264-NTG.mkv +: title: NOS4A2 + season: 1 + episode: 1 + episode_title: The Shorter Way + other: Proper + proper_count: 1 + screen_size: 720p + streaming_service: Amazon Prime + source: Web + audio_codec: Dolby Digital Plus + audio_channels: '5.1' + video_codec: H.264 + release_group: NTG + container: mkv + type: episode + +? Star Trek DS9 Ep 2x03 The Siege (Part III) +: title: Star Trek DS9 + season: 2 + episode: 3 + episode_title: The Siege + part: 3 + type: episode + +? The.Red.Line.S01E01 +: title: The Red Line + season: 1 + episode: 1 + type: episode + +? Show.S01E01.WEB.x264-METCON.mkv +: title: Show + season: 1 + episode: 1 + source: Web + video_codec: H.264 + release_group: METCON + container: mkv + type: episode + +? Show.S01E01.WEB.x264-TCMEON.mkv +: title: Show + season: 1 + episode: 1 + source: Web + video_codec: H.264 + release_group: TCMEON + container: mkv + type: episode + +? Show.S01E01.WEB.x264-MEONTC.mkv +: title: Show + season: 1 + episode: 1 + source: Web + video_codec: H.264 + release_group: MEONTC + container: mkv + type: episode + +? '[TorrentCouch.com].Westworld.S02.Complete.720p.WEB-DL.x264.[MP4].[5.3GB].[Season.2.Full]/[TorrentCouch.com].Westworld.S02E03.720p.WEB-DL.x264.mp4' +: website: TorrentCouch.com + title: Westworld + season: 2 + other: Complete + screen_size: 720p + source: Web + video_codec: H.264 + container: mp4 + size: 5.3GB + episode: 3 + type: episode + +? Vita.&.Virginia.2018.720p.H.264.YTS.LT.mp4 +: title: Vita & Virginia + year: 2018 + screen_size: 720p + video_codec: H.264 + release_group: YTS.LT + container: mp4 + type: movie \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit.egg-info/PKG-INFO new/guessit-3.1.0/guessit.egg-info/PKG-INFO --- old/guessit-3.0.4/guessit.egg-info/PKG-INFO 2019-06-04 23:30:58.000000000 +0200 +++ new/guessit-3.1.0/guessit.egg-info/PKG-INFO 2019-09-02 22:11:14.000000000 +0200 @@ -1,12 +1,12 @@ Metadata-Version: 2.1 Name: guessit -Version: 3.0.4 +Version: 3.1.0 Summary: GuessIt - a library for guessing information from video filenames. Home-page: http://guessit.readthedocs.org/ Author: Rémi Alvergnat Author-email: [email protected] License: LGPLv3 -Download-URL: https://pypi.python.org/packages/source/g/guessit/guessit-3.0.4.tar.gz +Download-URL: https://pypi.python.org/packages/source/g/guessit/guessit-3.1.0.tar.gz Description: GuessIt ======= @@ -210,6 +210,22 @@ History ======= + 3.1.0 (2019-09-02) + ------------------ + + - Add python `3.8` support + - Use rebulk `2.*` + - Remove `v` from `subtitle_language` prefix in default configuration + - Add `Variable Frame Rate` value to `other` property (VFR tag) + - Use episode words defined in configuration in a rebulk rule + - Avoid trigger of useless rules consequences + - Fix possible crash in weak episode removal + - Fix issue caused by `streaming_service` property conflicts + - Fix source validation when more than one pattern match + - Fix issue with some titles on multiple fileparts + - Fix issue related to website exclusion inside title + + 3.0.4 (2019-06-04) ------------------ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit.egg-info/SOURCES.txt new/guessit-3.1.0/guessit.egg-info/SOURCES.txt --- old/guessit-3.0.4/guessit.egg-info/SOURCES.txt 2019-06-04 23:30:58.000000000 +0200 +++ new/guessit-3.1.0/guessit.egg-info/SOURCES.txt 2019-09-02 22:11:14.000000000 +0200 @@ -32,6 +32,7 @@ guessit.egg-info/zip-safe guessit/config/options.json guessit/rules/__init__.py +guessit/rules/match_processors.py guessit/rules/processors.py guessit/rules/common/__init__.py guessit/rules/common/comparators.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/guessit.egg-info/requires.txt new/guessit-3.1.0/guessit.egg-info/requires.txt --- old/guessit-3.0.4/guessit.egg-info/requires.txt 2019-06-04 23:30:58.000000000 +0200 +++ new/guessit-3.1.0/guessit.egg-info/requires.txt 2019-09-02 22:11:14.000000000 +0200 @@ -1,6 +1,7 @@ -rebulk +rebulk==2.* babelfish python-dateutil +six [dev] zest.releaser[recommended] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/setup.py new/guessit-3.1.0/setup.py --- old/guessit-3.0.4/setup.py 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/setup.py 2019-09-02 22:11:13.000000000 +0200 @@ -16,7 +16,7 @@ with io.open(os.path.join(here, 'HISTORY.rst'), encoding='utf-8') as f: history = f.read() -install_requires = ['rebulk', 'babelfish', 'python-dateutil'] +install_requires = ['rebulk==2.*', 'babelfish', 'python-dateutil', 'six'] setup_requires = ['pytest-runner'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/guessit-3.0.4/tox.ini new/guessit-3.1.0/tox.ini --- old/guessit-3.0.4/tox.ini 2019-06-04 23:30:57.000000000 +0200 +++ new/guessit-3.1.0/tox.ini 2019-09-02 22:11:13.000000000 +0200 @@ -1,5 +1,10 @@ [tox] -envlist = py27,py34,py35,py36,py37,pypy2,pypy +envlist = py27,py34,py35,py36,py37,py38,pypy2,pypy + +[testenv:py38] +commands = + {envbindir}/pip install -e .[dev] + {envpython} setup.py test [testenv] commands =
