diff options
author | morpheus65535 <[email protected]> | 2021-03-22 10:26:26 -0400 |
---|---|---|
committer | morpheus65535 <[email protected]> | 2021-03-22 10:26:26 -0400 |
commit | 24c075051d7299190595437a5a733235b89ac739 (patch) | |
tree | da87f82f004acbfc672bed1f41732dad7e5e8042 /libs/guessit | |
parent | 762cd61b215e9f6aa8f0dfee9855f80bc7753f04 (diff) | |
download | bazarr-24c075051d7299190595437a5a733235b89ac739.tar.gz bazarr-24c075051d7299190595437a5a733235b89ac739.zip |
Updated guessit and rebulk to latest version
Diffstat (limited to 'libs/guessit')
34 files changed, 201 insertions, 261 deletions
diff --git a/libs/guessit/__main__.py b/libs/guessit/__main__.py index fad196d6b..1adf078a7 100644 --- a/libs/guessit/__main__.py +++ b/libs/guessit/__main__.py @@ -4,14 +4,12 @@ Entry point module """ # pragma: no cover -from __future__ import print_function - import json import logging -import os import sys -import six +from collections import OrderedDict + from rebulk.__version__ import __version__ as __rebulk_version__ from guessit import api @@ -20,12 +18,6 @@ from guessit.jsonutils import GuessitEncoder from guessit.options import argument_parser, parse_options, load_config, merge_options -try: - from collections import OrderedDict -except ImportError: # pragma: no-cover - from ordereddict import OrderedDict # pylint:disable=import-error - - def guess_filename(filename, options): """ Guess a single filename using given options @@ -48,6 +40,7 @@ def guess_filename(filename, options): if options.get('json'): print(json.dumps(guess, cls=GuessitEncoder, ensure_ascii=False)) elif options.get('yaml'): + # pylint:disable=import-outside-toplevel import yaml from guessit import yamlutils @@ -78,6 +71,7 @@ def display_properties(options): else: print(json.dumps(list(properties.keys()), cls=GuessitEncoder, ensure_ascii=False)) elif options.get('yaml'): + # pylint:disable=import-outside-toplevel import yaml from guessit import yamlutils if options.get('values'): @@ -97,24 +91,10 @@ def display_properties(options): print(4 * ' ' + '[!] %s' % (property_value,)) -def fix_argv_encoding(): - """ - Fix encoding of sys.argv on windows Python 2 - """ - if six.PY2 and os.name == 'nt': # pragma: no cover - # see http://bugs.python.org/issue2128 - import locale - - for i, j in enumerate(sys.argv): - sys.argv[i] = j.decode(locale.getpreferredencoding()) - - def main(args=None): # pylint:disable=too-many-branches """ Main function for entry point """ - fix_argv_encoding() - if args is None: # pragma: no cover options = parse_options() else: @@ -142,7 +122,7 @@ def main(args=None): # pylint:disable=too-many-branches if options.get('yaml'): try: - import yaml # pylint:disable=unused-variable,unused-import + import yaml # pylint:disable=unused-variable,unused-import,import-outside-toplevel except ImportError: # pragma: no cover del options['yaml'] print('PyYAML is not installed. \'--yaml\' option will be ignored ...', file=sys.stderr) @@ -156,10 +136,7 @@ def main(args=None): # pylint:disable=too-many-branches for filename in options.get('filename'): filenames.append(filename) if options.get('input_file'): - if six.PY2: - input_file = open(options.get('input_file'), 'r') - else: - input_file = open(options.get('input_file'), 'r', encoding='utf-8') + input_file = open(options.get('input_file'), 'r', encoding='utf-8') try: filenames.extend([line.strip() for line in input_file.readlines()]) finally: diff --git a/libs/guessit/__version__.py b/libs/guessit/__version__.py index e505897bb..357f94c75 100644 --- a/libs/guessit/__version__.py +++ b/libs/guessit/__version__.py @@ -4,4 +4,4 @@ Version module """ # pragma: no cover -__version__ = '3.1.1' +__version__ = '3.3.1' diff --git a/libs/guessit/api.py b/libs/guessit/api.py index 8e306340b..eeb8def1c 100644 --- a/libs/guessit/api.py +++ b/libs/guessit/api.py @@ -4,15 +4,12 @@ API functions that can be used by external software """ -try: - from collections import OrderedDict -except ImportError: # pragma: no-cover - from ordereddict import OrderedDict # pylint:disable=import-error +from collections import OrderedDict +from pathlib import Path import os import traceback -import six from rebulk.introspector import introspect from .__version__ import __version__ @@ -26,18 +23,18 @@ class GuessitException(Exception): """ def __init__(self, string, options): - super(GuessitException, self).__init__("An internal error has occured in guessit.\n" - "===================== Guessit Exception Report =====================\n" - "version=%s\n" - "string=%s\n" - "options=%s\n" - "--------------------------------------------------------------------\n" - "%s" - "--------------------------------------------------------------------\n" - "Please report at " - "https://github.com/guessit-io/guessit/issues.\n" - "====================================================================" % - (__version__, str(string), str(options), traceback.format_exc())) + super().__init__("An internal error has occured in guessit.\n" + "===================== Guessit Exception Report =====================\n" + "version=%s\n" + "string=%s\n" + "options=%s\n" + "--------------------------------------------------------------------\n" + "%s" + "--------------------------------------------------------------------\n" + "Please report at " + "https://github.com/guessit-io/guessit/issues.\n" + "====================================================================" % + (__version__, str(string), str(options), traceback.format_exc())) self.string = string self.options = options @@ -113,9 +110,7 @@ class GuessItApi(object): return [cls._fix_encoding(item) for item in value] if isinstance(value, dict): return {cls._fix_encoding(k): cls._fix_encoding(v) for k, v in value.items()} - if six.PY2 and isinstance(value, six.text_type): - return value.encode('utf-8') - if six.PY3 and isinstance(value, six.binary_type): + if isinstance(value, bytes): return value.decode('ascii') return value @@ -175,16 +170,12 @@ class GuessItApi(object): :return: :rtype: """ - try: - from pathlib import Path - if isinstance(string, Path): - try: - # Handle path-like object - string = os.fspath(string) - except AttributeError: - string = str(string) - except ImportError: - pass + if isinstance(string, Path): + try: + # Handle path-like object + string = os.fspath(string) + except AttributeError: + string = str(string) try: options = parse_options(options, True) @@ -194,32 +185,27 @@ class GuessItApi(object): result_decode = False result_encode = False - if six.PY2: - if isinstance(string, six.text_type): - string = string.encode("utf-8") - result_decode = True - elif isinstance(string, six.binary_type): - string = six.binary_type(string) - if six.PY3: - if isinstance(string, six.binary_type): - string = string.decode('ascii') - result_encode = True - elif isinstance(string, six.text_type): - string = six.text_type(string) + if isinstance(string, bytes): + string = string.decode('ascii') + result_encode = True matches = self.rebulk.matches(string, options) if result_decode: for match in matches: - if isinstance(match.value, six.binary_type): + if isinstance(match.value, bytes): match.value = match.value.decode("utf-8") if result_encode: for match in matches: - if isinstance(match.value, six.text_type): + if isinstance(match.value, str): match.value = match.value.encode("ascii") - return matches.to_dict(options.get('advanced', False), options.get('single_value', False), - options.get('enforce_list', False)) - except: - raise GuessitException(string, options) + matches_dict = matches.to_dict(options.get('advanced', False), options.get('single_value', False), + options.get('enforce_list', False)) + output_input_string = options.get('output_input_string', False) + if output_input_string: + matches_dict['input_string'] = matches.input_string + return matches_dict + except Exception as err: + raise GuessitException(string, options) from err def properties(self, options=None): """ @@ -235,8 +221,8 @@ class GuessItApi(object): options = merge_options(config, options) unordered = introspect(self.rebulk, options).properties ordered = OrderedDict() - for k in sorted(unordered.keys(), key=six.text_type): - ordered[k] = list(sorted(unordered[k], key=six.text_type)) + for k in sorted(unordered.keys(), key=str): + ordered[k] = list(sorted(unordered[k], key=str)) if hasattr(self.rebulk, 'customize_properties'): ordered = self.rebulk.customize_properties(ordered) return ordered diff --git a/libs/guessit/backports.py b/libs/guessit/backports.py deleted file mode 100644 index c149a6b5d..000000000 --- a/libs/guessit/backports.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Backports -""" -# pragma: no-cover -# pylint: skip-file - -def cmp_to_key(mycmp): - """functools.cmp_to_key backport""" - class KeyClass(object): - """Key class""" - def __init__(self, obj, *args): # pylint: disable=unused-argument - self.obj = obj - def __lt__(self, other): - return mycmp(self.obj, other.obj) < 0 - def __gt__(self, other): - return mycmp(self.obj, other.obj) > 0 - def __eq__(self, other): - return mycmp(self.obj, other.obj) == 0 - def __le__(self, other): - return mycmp(self.obj, other.obj) <= 0 - def __ge__(self, other): - return mycmp(self.obj, other.obj) >= 0 - def __ne__(self, other): - return mycmp(self.obj, other.obj) != 0 - return KeyClass diff --git a/libs/guessit/config/options.json b/libs/guessit/config/options.json index da7c70306..5a343bcb4 100644 --- a/libs/guessit/config/options.json +++ b/libs/guessit/config/options.json @@ -416,6 +416,10 @@ "Animal Planet": "ANPL", "AnimeLab": "ANLB", "AOL": "AOL", + "AppleTV": [ + "ATVP", + "ATV+" + ], "ARD": "ARD", "BBC iPlayer": [ "iP", @@ -482,6 +486,7 @@ "HBO", "re:HBO-?Go" ], + "HBO Max": "HMAX", "HGTV": "HGTV", "History": [ "HIST", @@ -490,7 +495,10 @@ "Hulu": "HULU", "Investigation Discovery": "ID", "IFC": "IFC", - "iTunes": "iTunes", + "iTunes": [ + "iTunes", + {"pattern": "iT", "ignore_case": false} + ], "ITV": "ITV", "Knowledge Network": "KNOW", "Lifetime": "LIFE", @@ -537,6 +545,7 @@ "SeeSo" ], "Shomi": "SHMI", + "Showtime": "SHO", "Spike": "SPIK", "Spike TV": [ "SPKE", diff --git a/libs/guessit/tlds-alpha-by-domain.txt b/libs/guessit/data/tlds-alpha-by-domain.txt index 280c794c5..280c794c5 100644 --- a/libs/guessit/tlds-alpha-by-domain.txt +++ b/libs/guessit/data/tlds-alpha-by-domain.txt diff --git a/libs/guessit/monkeypatch.py b/libs/guessit/monkeypatch.py index 33e7c46ee..14ddf6e89 100644 --- a/libs/guessit/monkeypatch.py +++ b/libs/guessit/monkeypatch.py @@ -4,10 +4,7 @@ Monkeypatch initialisation functions """ -try: - from collections import OrderedDict -except ImportError: # pragma: no-cover - from ordereddict import OrderedDict # pylint:disable=import-error +from collections import OrderedDict from rebulk.match import Match diff --git a/libs/guessit/options.py b/libs/guessit/options.py index 8fa6825cc..d1a76b037 100644 --- a/libs/guessit/options.py +++ b/libs/guessit/options.py @@ -11,8 +11,6 @@ import shlex from argparse import ArgumentParser -import six - def build_argument_parser(): """ @@ -68,6 +66,8 @@ def build_argument_parser(): help='Display information for filename guesses as json output') output_opts.add_argument('-y', '--yaml', dest='yaml', action='store_true', default=None, help='Display information for filename guesses as yaml output') + output_opts.add_argument('-i', '--output-input-string', dest='output_input_string', action='store_true', + default=False, help='Add input_string property in the output') conf_opts = opts.add_argument_group("Configuration") conf_opts.add_argument('-c', '--config', dest='config', action='append', default=None, @@ -108,7 +108,7 @@ def parse_options(options=None, api=False): :return: :rtype: """ - if isinstance(options, six.string_types): + if isinstance(options, str): args = shlex.split(options) options = vars(argument_parser.parse_args(args)) elif options is None: @@ -153,7 +153,7 @@ def load_config(options): cwd = os.getcwd() yaml_supported = False try: - import yaml # pylint:disable=unused-variable,unused-import + import yaml # pylint:disable=unused-variable,unused-import,import-outside-toplevel yaml_supported = True except ImportError: pass @@ -225,7 +225,7 @@ def merge_option_value(option, value, merged): if value is not None and option != 'pristine': if option in merged.keys() and isinstance(merged[option], list): for val in value: - if val not in merged[option]: + if val not in merged[option] and val is not None: merged[option].append(val) elif option in merged.keys() and isinstance(merged[option], dict): merged[option] = merge_options(merged[option], value) @@ -250,13 +250,13 @@ def load_config_file(filepath): return json.load(config_file_data) if filepath.endswith('.yaml') or filepath.endswith('.yml'): try: - import yaml + import yaml # pylint:disable=import-outside-toplevel with open(filepath) as config_file_data: return yaml.load(config_file_data, yaml.SafeLoader) - except ImportError: # pragma: no cover + except ImportError as err: # pragma: no cover raise ConfigurationException('Configuration file extension is not supported. ' 'PyYAML should be installed to support "%s" file' % ( - filepath,)) + filepath,)) from err try: # Try to load input as JSON diff --git a/libs/guessit/rules/common/__init__.py b/libs/guessit/rules/common/__init__.py index 444dc72a9..24f9433dd 100644 --- a/libs/guessit/rules/common/__init__.py +++ b/libs/guessit/rules/common/__init__.py @@ -3,7 +3,7 @@ """ Common module """ -import re +from rebulk.remodule import re seps = r' [](){}+*|=-_~#/\\.,;:' # list of tags/words separators seps_no_groups = seps.replace('[](){}', '') diff --git a/libs/guessit/rules/common/comparators.py b/libs/guessit/rules/common/comparators.py index f46f0c119..277493144 100644 --- a/libs/guessit/rules/common/comparators.py +++ b/libs/guessit/rules/common/comparators.py @@ -3,10 +3,8 @@ """ Comparators """ -try: - from functools import cmp_to_key -except ImportError: - from ...backports import cmp_to_key + +from functools import cmp_to_key def marker_comparator_predicate(match): @@ -14,10 +12,10 @@ def marker_comparator_predicate(match): Match predicate used in comparator """ return ( - not match.private - and match.name not in ('proper_count', 'title') - and not (match.name == 'container' and 'extension' in match.tags) - and not (match.name == 'other' and match.value == 'Rip') + not match.private + and match.name not in ('proper_count', 'title') + and not (match.name == 'container' and 'extension' in match.tags) + and not (match.name == 'other' and match.value == 'Rip') ) diff --git a/libs/guessit/rules/common/expected.py b/libs/guessit/rules/common/expected.py index eae562a2d..19f2f8874 100644 --- a/libs/guessit/rules/common/expected.py +++ b/libs/guessit/rules/common/expected.py @@ -3,7 +3,7 @@ """ Expected property factory """ -import re +from rebulk.remodule import re from rebulk import Rebulk from rebulk.utils import find_all diff --git a/libs/guessit/rules/common/quantity.py b/libs/guessit/rules/common/quantity.py index bbd41fbb9..8d3f21d4a 100644 --- a/libs/guessit/rules/common/quantity.py +++ b/libs/guessit/rules/common/quantity.py @@ -3,10 +3,9 @@ """ Quantities: Size """ -import re from abc import abstractmethod -import six +from rebulk.remodule import re from ..common import seps @@ -50,7 +49,7 @@ class Quantity(object): return hash(str(self)) def __eq__(self, other): - if isinstance(other, six.string_types): + if isinstance(other, str): return str(self) == other if not isinstance(other, self.__class__): return NotImplemented diff --git a/libs/guessit/rules/markers/groups.py b/libs/guessit/rules/markers/groups.py index 4716d15d7..ac66f0443 100644 --- a/libs/guessit/rules/markers/groups.py +++ b/libs/guessit/rules/markers/groups.py @@ -5,6 +5,7 @@ Groups markers (...), [...] and {...} """ from rebulk import Rebulk +from ...options import ConfigurationException def groups(config): """ @@ -21,6 +22,9 @@ def groups(config): starting = config['starting'] ending = config['ending'] + if len(starting) != len(ending): + raise ConfigurationException("Starting and ending groups must have the same length") + def mark_groups(input_string): """ Functional pattern to mark groups (...), [...] and {...}. @@ -28,7 +32,7 @@ def groups(config): :param input_string: :return: """ - openings = ([], [], []) + openings = ([], ) * len(starting) i = 0 ret = [] diff --git a/libs/guessit/rules/processors.py b/libs/guessit/rules/processors.py index 5b018140c..069e75d2d 100644 --- a/libs/guessit/rules/processors.py +++ b/libs/guessit/rules/processors.py @@ -6,8 +6,6 @@ Processors from collections import defaultdict import copy -import six - from rebulk import Rebulk, Rule, CustomRule, POST_PROCESS, PRE_PROCESS, AppendMatch, RemoveMatch from .common import seps_no_groups @@ -68,7 +66,7 @@ class EquivalentHoles(Rule): for name in matches.names: for hole in list(holes): for current_match in matches.named(name): - if isinstance(current_match.value, six.string_types) and \ + if isinstance(current_match.value, str) and \ hole.value.lower() == current_match.value.lower(): if 'equivalent-ignore' in current_match.tags: continue @@ -96,7 +94,7 @@ class RemoveAmbiguous(Rule): consequence = RemoveMatch def __init__(self, sort_function=marker_sorted, predicate=None): - super(RemoveAmbiguous, self).__init__() + super().__init__() self.sort_function = sort_function self.predicate = predicate @@ -131,7 +129,7 @@ class RemoveLessSpecificSeasonEpisode(RemoveAmbiguous): keep the one tagged as 'SxxExx' or in the rightmost filepart. """ def __init__(self, name): - super(RemoveLessSpecificSeasonEpisode, self).__init__( + super().__init__( sort_function=(lambda markers, matches: marker_sorted(list(reversed(markers)), matches, lambda match: match.name == name and 'SxxExx' in match.tags)), diff --git a/libs/guessit/rules/properties/audio_codec.py b/libs/guessit/rules/properties/audio_codec.py index 815caff99..0aa7d31bc 100644 --- a/libs/guessit/rules/properties/audio_codec.py +++ b/libs/guessit/rules/properties/audio_codec.py @@ -130,7 +130,7 @@ class AudioProfileRule(Rule): consequence = RemoveMatch def __init__(self, codec): - super(AudioProfileRule, self).__init__() + super().__init__() self.codec = codec def enabled(self, context): @@ -166,7 +166,7 @@ class DtsHDRule(AudioProfileRule): """ def __init__(self): - super(DtsHDRule, self).__init__('DTS-HD') + super().__init__('DTS-HD') class DtsRule(AudioProfileRule): @@ -175,7 +175,7 @@ class DtsRule(AudioProfileRule): """ def __init__(self): - super(DtsRule, self).__init__('DTS') + super().__init__('DTS') class AacRule(AudioProfileRule): @@ -184,7 +184,7 @@ class AacRule(AudioProfileRule): """ def __init__(self): - super(AacRule, self).__init__('AAC') + super().__init__('AAC') class DolbyDigitalRule(AudioProfileRule): @@ -193,7 +193,7 @@ class DolbyDigitalRule(AudioProfileRule): """ def __init__(self): - super(DolbyDigitalRule, self).__init__('Dolby Digital') + super().__init__('Dolby Digital') class HqConflictRule(Rule): diff --git a/libs/guessit/rules/properties/bit_rate.py b/libs/guessit/rules/properties/bit_rate.py index d279c9f1c..640a6a2e4 100644 --- a/libs/guessit/rules/properties/bit_rate.py +++ b/libs/guessit/rules/properties/bit_rate.py @@ -3,7 +3,7 @@ """ video_bit_rate and audio_bit_rate properties """ -import re +from rebulk.remodule import re from rebulk import Rebulk from rebulk.rules import Rule, RemoveMatch, RenameMatch diff --git a/libs/guessit/rules/properties/episode_title.py b/libs/guessit/rules/properties/episode_title.py index ece8921d2..2c4fab669 100644 --- a/libs/guessit/rules/properties/episode_title.py +++ b/libs/guessit/rules/properties/episode_title.py @@ -47,7 +47,7 @@ class RemoveConflictsWithEpisodeTitle(Rule): consequence = RemoveMatch def __init__(self, previous_names): - super(RemoveConflictsWithEpisodeTitle, self).__init__() + super().__init__() self.previous_names = previous_names self.next_names = ('streaming_service', 'screen_size', 'source', 'video_codec', 'audio_codec', 'other', 'container') @@ -129,7 +129,7 @@ class EpisodeTitleFromPosition(TitleBaseRule): dependency = TitleToEpisodeTitle def __init__(self, previous_names): - super(EpisodeTitleFromPosition, self).__init__('episode_title', ['title']) + super().__init__('episode_title', ['title']) self.previous_names = previous_names def hole_filter(self, hole, matches): @@ -150,12 +150,12 @@ class EpisodeTitleFromPosition(TitleBaseRule): def should_remove(self, match, matches, filepart, hole, context): if match.name == 'episode_details': return False - return super(EpisodeTitleFromPosition, self).should_remove(match, matches, filepart, hole, context) + return super().should_remove(match, matches, filepart, hole, context) def when(self, matches, context): # pylint:disable=inconsistent-return-statements if matches.named('episode_title'): return - return super(EpisodeTitleFromPosition, self).when(matches, context) + return super().when(matches, context) class AlternativeTitleReplace(Rule): @@ -166,7 +166,7 @@ class AlternativeTitleReplace(Rule): consequence = RenameMatch def __init__(self, previous_names): - super(AlternativeTitleReplace, self).__init__() + super().__init__() self.previous_names = previous_names def when(self, matches, context): # pylint:disable=inconsistent-return-statements diff --git a/libs/guessit/rules/properties/episodes.py b/libs/guessit/rules/properties/episodes.py index 345c785de..7aa2245ae 100644 --- a/libs/guessit/rules/properties/episodes.py +++ b/libs/guessit/rules/properties/episodes.py @@ -479,7 +479,7 @@ class SeePatternRange(Rule): consequence = [RemoveMatch, AppendMatch] def __init__(self, range_separators): - super(SeePatternRange, self).__init__() + super().__init__() self.range_separators = range_separators def when(self, matches, context): @@ -516,7 +516,7 @@ class AbstractSeparatorRange(Rule): consequence = [RemoveMatch, AppendMatch] def __init__(self, range_separators, property_name): - super(AbstractSeparatorRange, self).__init__() + super().__init__() self.range_separators = range_separators self.property_name = property_name @@ -608,7 +608,7 @@ class EpisodeNumberSeparatorRange(AbstractSeparatorRange): """ def __init__(self, range_separators): - super(EpisodeNumberSeparatorRange, self).__init__(range_separators, "episode") + super().__init__(range_separators, "episode") class SeasonSeparatorRange(AbstractSeparatorRange): @@ -617,7 +617,7 @@ class SeasonSeparatorRange(AbstractSeparatorRange): """ def __init__(self, range_separators): - super(SeasonSeparatorRange, self).__init__(range_separators, "season") + super().__init__(range_separators, "season") class RemoveWeakIfMovie(Rule): @@ -662,7 +662,7 @@ class RemoveWeak(Rule): consequence = RemoveMatch, AppendMatch def __init__(self, episode_words): - super(RemoveWeak, self).__init__() + super().__init__() self.episode_words = episode_words def when(self, matches, context): diff --git a/libs/guessit/rules/properties/language.py b/libs/guessit/rules/properties/language.py index 3f83bc344..c1f9e6a17 100644 --- a/libs/guessit/rules/properties/language.py +++ b/libs/guessit/rules/properties/language.py @@ -396,7 +396,7 @@ class SubtitlePrefixLanguageRule(Rule): def then(self, matches, when_response, context): to_rename, to_remove = when_response - super(SubtitlePrefixLanguageRule, self).then(matches, to_remove, context) + super().then(matches, to_remove, context) for prefix, match in to_rename: # Remove suffix equivalent of prefix. suffix = copy.copy(prefix) @@ -435,7 +435,7 @@ class SubtitleSuffixLanguageRule(Rule): def then(self, matches, when_response, context): to_rename, to_remove = when_response - super(SubtitleSuffixLanguageRule, self).then(matches, to_remove, context) + super().then(matches, to_remove, context) for match in to_rename: matches.remove(match) match.name = 'subtitle_language' @@ -488,7 +488,7 @@ class RemoveInvalidLanguages(Rule): def __init__(self, common_words): """Constructor.""" - super(RemoveInvalidLanguages, self).__init__() + super().__init__() self.common_words = common_words def when(self, matches, context): diff --git a/libs/guessit/rules/properties/other.py b/libs/guessit/rules/properties/other.py index c7dc9a88e..bfcbe680f 100644 --- a/libs/guessit/rules/properties/other.py +++ b/libs/guessit/rules/properties/other.py @@ -86,7 +86,7 @@ def other(config): # pylint:disable=unused-argument,too-many-statements rebulk.regex('(HD)(?P<another>Rip)', value={'other': 'HD', 'another': 'Rip'}, private_parent=True, children=True, validator={'__parent__': seps_surround}, validate_all=True) - for value in ('Screener', 'Remux', 'PAL', 'SECAM', 'NTSC', 'XXX'): + for value in ('Screener', 'Remux', 'Hybrid', 'PAL', 'SECAM', 'NTSC', 'XXX'): rebulk.string(value, value=value) rebulk.string('3D', value='3D', tags='has-neighbor') diff --git a/libs/guessit/rules/properties/release_group.py b/libs/guessit/rules/properties/release_group.py index ecff808b4..09e845f56 100644 --- a/libs/guessit/rules/properties/release_group.py +++ b/libs/guessit/rules/properties/release_group.py @@ -98,7 +98,7 @@ class DashSeparatedReleaseGroup(Rule): def __init__(self, value_formatter): """Default constructor.""" - super(DashSeparatedReleaseGroup, self).__init__() + super().__init__() self.value_formatter = value_formatter @classmethod @@ -212,7 +212,7 @@ class SceneReleaseGroup(Rule): def __init__(self, value_formatter): """Default constructor.""" - super(SceneReleaseGroup, self).__init__() + super().__init__() self.value_formatter = value_formatter @staticmethod @@ -321,7 +321,6 @@ class AnimeReleaseGroup(Rule): for filepart in marker_sorted(matches.markers.named('path'), matches): - # pylint:disable=bad-continuation empty_group = matches.markers.range(filepart.start, filepart.end, lambda marker: (marker.name == 'group' diff --git a/libs/guessit/rules/properties/screen_size.py b/libs/guessit/rules/properties/screen_size.py index 77d5d0521..966fc3c1d 100644 --- a/libs/guessit/rules/properties/screen_size.py +++ b/libs/guessit/rules/properties/screen_size.py @@ -69,7 +69,7 @@ class PostProcessScreenSize(Rule): consequence = AppendMatch def __init__(self, standard_heights, min_ar, max_ar): - super(PostProcessScreenSize, self).__init__() + super().__init__() self.standard_heights = standard_heights self.min_ar = min_ar self.max_ar = max_ar diff --git a/libs/guessit/rules/properties/size.py b/libs/guessit/rules/properties/size.py index c61580c04..0a0970020 100644 --- a/libs/guessit/rules/properties/size.py +++ b/libs/guessit/rules/properties/size.py @@ -3,7 +3,7 @@ """ size property """ -import re +from rebulk.remodule import re from rebulk import Rebulk diff --git a/libs/guessit/rules/properties/streaming_service.py b/libs/guessit/rules/properties/streaming_service.py index f467f20a6..b27fc4401 100644 --- a/libs/guessit/rules/properties/streaming_service.py +++ b/libs/guessit/rules/properties/streaming_service.py @@ -3,7 +3,7 @@ """ streaming_service property """ -import re +from rebulk.remodule import re from rebulk import Rebulk from rebulk.rules import Rule, RemoveMatch @@ -25,13 +25,22 @@ def streaming_service(config): # pylint: disable=too-many-statements,unused-arg rebulk = rebulk.string_defaults(ignore_case=True).regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]) rebulk.defaults(name='streaming_service', tags=['source-prefix']) + regex_prefix = 're:' + 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) + if isinstance(pattern, dict): + pattern_value = pattern.pop('pattern') + kwargs = pattern + pattern = pattern_value + else: + kwargs = {} + regex = kwargs.pop('regex', False) + if regex or pattern.startswith(regex_prefix): + rebulk.regex(pattern[len(regex_prefix):], value=value, **kwargs) else: - rebulk.string(pattern, value=value) + rebulk.string(pattern, value=value, **kwargs) rebulk.rules(ValidateStreamingService) diff --git a/libs/guessit/rules/properties/title.py b/libs/guessit/rules/properties/title.py index 0d2630167..2a065cb04 100644 --- a/libs/guessit/rules/properties/title.py +++ b/libs/guessit/rules/properties/title.py @@ -53,7 +53,7 @@ class TitleBaseRule(Rule): consequence = [AppendMatch, RemoveMatch] def __init__(self, match_name, match_tags=None, alternative_match_name=None): - super(TitleBaseRule, self).__init__() + super().__init__() self.match_name = match_name self.match_tags = match_tags self.alternative_match_name = alternative_match_name @@ -299,7 +299,7 @@ class TitleFromPosition(TitleBaseRule): properties = {'title': [None], 'alternative_title': [None]} def __init__(self): - super(TitleFromPosition, self).__init__('title', ['title'], 'alternative_title') + super().__init__('title', ['title'], 'alternative_title') def enabled(self, context): return not is_disabled(context, 'alternative_title') diff --git a/libs/guessit/rules/properties/website.py b/libs/guessit/rules/properties/website.py index c19653117..96bed6400 100644 --- a/libs/guessit/rules/properties/website.py +++ b/libs/guessit/rules/properties/website.py @@ -27,7 +27,7 @@ def website(config): rebulk = rebulk.regex_defaults(flags=re.IGNORECASE).string_defaults(ignore_case=True) rebulk.defaults(name="website") - with resource_stream('guessit', 'tlds-alpha-by-domain.txt') as tld_file: + with resource_stream('guessit', 'data/tlds-alpha-by-domain.txt') as tld_file: tlds = [ tld.strip().decode('utf-8') for tld in tld_file.readlines() diff --git a/libs/guessit/test/movies.yml b/libs/guessit/test/movies.yml index a534ca0f2..6b503d13a 100644 --- a/libs/guessit/test/movies.yml +++ b/libs/guessit/test/movies.yml @@ -1752,6 +1752,7 @@ year: 2018 other: - 3D + - Hybrid - Proper - Remux proper_count: 1 diff --git a/libs/guessit/test/rules/other.yml b/libs/guessit/test/rules/other.yml index 447f1787d..0ae0c990f 100644 --- a/libs/guessit/test/rules/other.yml +++ b/libs/guessit/test/rules/other.yml @@ -80,6 +80,9 @@ ? Remux : other: Remux +? Hybrid +: other: Hybrid + ? 3D.2019 : other: 3D diff --git a/libs/guessit/test/streaming_services.yaml b/libs/guessit/test/streaming_services.yaml index adf52e715..227d72d75 100644 --- a/libs/guessit/test/streaming_services.yaml +++ b/libs/guessit/test/streaming_services.yaml @@ -577,13 +577,13 @@ release_group: BTW type: episode -# Streaming service: RTÉ One +# Streaming service: RTE One ? Show.Name.S10E01.576p.RTE.WEBRip.AAC2.0.H.264-RTN : title: Show Name season: 10 episode: 1 screen_size: 576p - streaming_service: RTÉ One + streaming_service: RTE One source: Web other: Rip audio_codec: AAC @@ -818,7 +818,6 @@ episode: 0 episode_details: Pilot episode_title: Pilot - language: zh other: - Proper - Rip @@ -862,7 +861,6 @@ ? What.The.Fuck.France.S01E01.Le.doublage.CNLP.WEBRip.AAC2.0.x264-TURTLE : audio_channels: '2.0' audio_codec: AAC - country: FR episode: 1 episode_title: Le doublage other: Rip @@ -870,7 +868,7 @@ season: 1 source: Web streaming_service: Canal+ - title: What The Fuck + title: What The Fuck France type: episode video_codec: H.264 @@ -943,14 +941,13 @@ ? The.Amazing.Race.Canada.S03.720p.CTV.WEBRip.AAC2.0.H.264-BTW : audio_channels: '2.0' audio_codec: AAC - country: CA other: Rip release_group: BTW screen_size: 720p season: 3 source: Web streaming_service: CTV - title: The Amazing Race + title: The Amazing Race Canada type: episode video_codec: H.264 @@ -1240,13 +1237,12 @@ ? Big.Brother.Canada.S05.GLBL.WEBRip.AAC2.0.H.264-RTN : audio_channels: '2.0' audio_codec: AAC - country: CA other: Rip release_group: RTN season: 5 source: Web streaming_service: Global - title: Big Brother + title: Big Brother Canada type: episode video_codec: H.264 @@ -1330,7 +1326,6 @@ ? Handmade.in.Japan.S01E01.720p.iP.WEBRip.AAC2.0.H.264-SUP : audio_channels: '2.0' audio_codec: AAC - country: JP episode: 1 other: Rip release_group: SUP @@ -1338,7 +1333,7 @@ season: 1 source: Web streaming_service: BBC iPlayer - title: Handmade in + title: Handmade in Japan type: episode video_codec: H.264 @@ -1463,9 +1458,8 @@ ? Bunsen.is.a.Beast.S01E23.Guinea.Some.Lovin.1080p.NICK.WEBRip.AAC2.0.x264-TVSmash : audio_channels: '2.0' audio_codec: AAC - country: GN episode: 23 - episode_title: Some Lovin + episode_title: Guinea Some Lovin other: Rip release_group: TVSmash screen_size: 1080p @@ -1538,13 +1532,14 @@ episode_title: The Masquerade other: Rip part: 2 - release_group: VP9-BTW + release_group: BTW screen_size: 1080p season: 2 source: Web streaming_service: YouTube Red title: Escape The Night type: episode + video_codec: VP9 ? Escape.The.Night.S02E02.The.Masquerade.Part.II.2160p.RED.WEBRip.AAC5.1.VP9-BTW : audio_channels: '5.1' @@ -1553,13 +1548,14 @@ episode_title: The Masquerade other: Rip part: 2 - release_group: VP9-BTW + release_group: BTW screen_size: 2160p season: 2 source: Web streaming_service: YouTube Red title: Escape The Night type: episode + video_codec: VP9 ? Escape.The.Night.S02E02.The.Masquerade.Part.II.720p.RED.WEBRip.AAC5.1.VP9-BTW : audio_channels: '5.1' @@ -1568,13 +1564,14 @@ episode_title: The Masquerade other: Rip part: 2 - release_group: VP9-BTW + release_group: BTW screen_size: 720p season: 2 source: Web streaming_service: YouTube Red title: Escape The Night type: episode + video_codec: VP9 ? The.Family.Law.S02E01.720p.SBS.WEB-DL.AAC2.0.H.264-BTN : audio_channels: '2.0' @@ -1892,7 +1889,7 @@ season: 1 source: Web streaming_service: Vimeo - title: '555' + # title: '555' type: episode video_codec: H.264 diff --git a/libs/guessit/test/test_api.py b/libs/guessit/test/test_api.py index 391dbced8..542292580 100644 --- a/libs/guessit/test/test_api.py +++ b/libs/guessit/test/test_api.py @@ -3,10 +3,9 @@ # pylint: disable=no-self-use, pointless-statement, missing-docstring, invalid-name, pointless-string-statement import json import os -import sys +from pathlib import Path import pytest -import six from ..api import guessit, properties, suggested_expected, GuessitException @@ -19,25 +18,19 @@ def test_default(): def test_forced_unicode(): - ret = guessit(u'Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') - assert ret and 'title' in ret and isinstance(ret['title'], six.text_type) + ret = guessit('Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') + assert ret and 'title' in ret and isinstance(ret['title'], str) def test_forced_binary(): ret = guessit(b'Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') - assert ret and 'title' in ret and isinstance(ret['title'], six.binary_type) + assert ret and 'title' in ret and isinstance(ret['title'], bytes) [email protected](sys.version_info < (3, 4), reason="Path is not available") def test_pathlike_object(): - try: - from pathlib import Path - - path = Path('Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') - ret = guessit(path) - assert ret and 'title' in ret - except ImportError: # pragma: no-cover - pass + path = Path('Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') + ret = guessit(path) + assert ret and 'title' in ret def test_unicode_japanese(): @@ -51,16 +44,8 @@ def test_unicode_japanese_options(): def test_forced_unicode_japanese_options(): - ret = guessit(u"[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi", options={"expected_title": [u"阿维达"]}) - assert ret and 'title' in ret and ret['title'] == u"阿维达" - -# TODO: This doesn't compile on python 3, but should be tested on python 2. -""" -if six.PY2: - def test_forced_binary_japanese_options(): - ret = guessit(b"[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi", options={"expected_title": [b"阿维达"]}) - assert ret and 'title' in ret and ret['title'] == b"阿维达" -""" + ret = guessit("[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi", options={"expected_title": ["阿维达"]}) + assert ret and 'title' in ret and ret['title'] == "阿维达" def test_properties(): diff --git a/libs/guessit/test/test_api_unicode_literals.py b/libs/guessit/test/test_api_unicode_literals.py index 826f7cd16..79bbbca15 100644 --- a/libs/guessit/test/test_api_unicode_literals.py +++ b/libs/guessit/test/test_api_unicode_literals.py @@ -3,12 +3,9 @@ # pylint: disable=no-self-use, pointless-statement, missing-docstring, invalid-name, pointless-string-statement -from __future__ import unicode_literals - import os import pytest -import six from ..api import guessit, properties, GuessitException @@ -21,13 +18,13 @@ def test_default(): def test_forced_unicode(): - ret = guessit(u'Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') - assert ret and 'title' in ret and isinstance(ret['title'], six.text_type) + ret = guessit('Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') + assert ret and 'title' in ret and isinstance(ret['title'], str) def test_forced_binary(): ret = guessit(b'Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') - assert ret and 'title' in ret and isinstance(ret['title'], six.binary_type) + assert ret and 'title' in ret and isinstance(ret['title'], bytes) def test_unicode_japanese(): @@ -41,24 +38,18 @@ def test_unicode_japanese_options(): def test_forced_unicode_japanese_options(): - ret = guessit(u"[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi", options={"expected_title": [u"阿维达"]}) - assert ret and 'title' in ret and ret['title'] == u"阿维达" - -# TODO: This doesn't compile on python 3, but should be tested on python 2. -""" -if six.PY2: - def test_forced_binary_japanese_options(): - ret = guessit(b"[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi", options={"expected_title": [b"阿维达"]}) - assert ret and 'title' in ret and ret['title'] == b"阿维达" -""" + ret = guessit("[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi", options={"expected_title": ["阿维达"]}) + assert ret and 'title' in ret and ret['title'] == "阿维达" -def test_ensure_standard_string_class(): +def test_ensure_custom_string_class(): class CustomStr(str): pass - ret = guessit(CustomStr('1080p'), options={'advanced': True}) - assert ret and 'screen_size' in ret and not isinstance(ret['screen_size'].input_string, CustomStr) + ret = guessit(CustomStr('some.title.1080p.mkv'), options={'advanced': True}) + assert ret and 'screen_size' in ret and isinstance(ret['screen_size'].input_string, CustomStr) + assert ret and 'title' in ret and isinstance(ret['title'].input_string, CustomStr) + assert ret and 'container' in ret and isinstance(ret['container'].input_string, CustomStr) def test_properties(): diff --git a/libs/guessit/test/test_main.py b/libs/guessit/test/test_main.py index cbdba7aa4..30de4ccae 100644 --- a/libs/guessit/test/test_main.py +++ b/libs/guessit/test/test_main.py @@ -1,16 +1,25 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=no-self-use, pointless-statement, missing-docstring, invalid-name - +import json import os +import sys import pytest +from _pytest.capture import CaptureFixture from ..__main__ import main __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) +# Prevent output from spamming the console [email protected](scope="function", autouse=True) +def no_stdout(monkeypatch): + with open(os.devnull, "w") as f: + monkeypatch.setattr(sys, "stdout", f) + yield + def test_main_no_args(): main([]) @@ -24,7 +33,7 @@ def test_main_unicode(): def test_main_forced_unicode(): - main([u'Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv']) + main(['Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv']) def test_main_verbose(): @@ -70,3 +79,22 @@ def test_main_help(): def test_main_version(): main(['--version']) + + +def test_json_output_input_string(capsys: CaptureFixture): + main(['--json', '--output-input-string', 'test.avi']) + + outerr = capsys.readouterr() + data = json.loads(outerr.out) + + assert 'input_string' in data + assert data['input_string'] == 'test.avi' + + +def test_json_no_output_input_string(capsys: CaptureFixture): + main(['--json', 'test.avi']) + + outerr = capsys.readouterr() + data = json.loads(outerr.out) + + assert 'input_string' not in data diff --git a/libs/guessit/test/test_yml.py b/libs/guessit/test/test_yml.py index 040796dea..d7cb4b5e0 100644 --- a/libs/guessit/test/test_yml.py +++ b/libs/guessit/test/test_yml.py @@ -7,7 +7,6 @@ import os from io import open # pylint: disable=redefined-builtin import babelfish -import six # pylint:disable=wrong-import-order import yaml # pylint:disable=wrong-import-order from rebulk.remodule import re from rebulk.utils import is_iterable @@ -53,16 +52,16 @@ class EntryResult(object): if self.ok: return self.string + ': OK!' if self.warning: - return '%s%s: WARNING! (valid=%i, extra=%i)' % ('-' if self.negates else '', self.string, len(self.valid), - len(self.extra)) + return '%s%s: WARNING! (valid=%i, extra=%s)' % ('-' if self.negates else '', self.string, len(self.valid), + self.extra) if self.error: - return '%s%s: ERROR! (valid=%i, missing=%i, different=%i, extra=%i, others=%i)' % \ - ('-' if self.negates else '', self.string, len(self.valid), len(self.missing), len(self.different), - len(self.extra), len(self.others)) + return '%s%s: ERROR! (valid=%i, extra=%s, missing=%s, different=%s, others=%s)' % \ + ('-' if self.negates else '', self.string, len(self.valid), self.extra, self.missing, + self.different, self.others) - return '%s%s: UNKOWN! (valid=%i, missing=%i, different=%i, extra=%i, others=%i)' % \ - ('-' if self.negates else '', self.string, len(self.valid), len(self.missing), len(self.different), - len(self.extra), len(self.others)) + return '%s%s: UNKOWN! (valid=%i, extra=%s, missing=%s, different=%s, others=%s)' % \ + ('-' if self.negates else '', self.string, len(self.valid), self.extra, self.missing, self.different, + self.others) @property def details(self): @@ -110,7 +109,7 @@ def files_and_ids(predicate=None): for filename in filenames: name, ext = os.path.splitext(filename) filepath = os.path.join(dirpath_rel, filename) - if ext == '.yml' and (not predicate or predicate(filepath)): + if ext in ['.yml', '.yaml'] and (not predicate or predicate(filepath)): files.append(filepath) ids.append(os.path.join(dirpath_rel, name)) @@ -161,7 +160,7 @@ class TestYml(object): for string, expected in data.items(): TestYml.set_default(expected, default) - string = TestYml.fix_encoding(string, expected) + string = TestYml.fix_encoding(string) entries.append((filename, string, expected)) unique_id = self._get_unique_id(entry_set, '[' + filename + '] ' + str(string)) @@ -178,17 +177,7 @@ class TestYml(object): expected[k] = v @classmethod - def fix_encoding(cls, string, expected): - if six.PY2: - if isinstance(string, six.text_type): - string = string.encode('utf-8') - converts = [] - for k, v in expected.items(): - if isinstance(v, six.text_type): - v = v.encode('utf-8') - converts.append((k, v)) - for k, v in converts: - expected[k] = v + def fix_encoding(cls, string): if not isinstance(string, str): string = str(string) return string diff --git a/libs/guessit/yamlutils.py b/libs/guessit/yamlutils.py index d04be641b..f038a99bc 100644 --- a/libs/guessit/yamlutils.py +++ b/libs/guessit/yamlutils.py @@ -4,10 +4,7 @@ Options """ -try: - from collections import OrderedDict -except ImportError: # pragma: no-cover - from ordereddict import OrderedDict # pylint:disable=import-error +from collections import OrderedDict import babelfish import yaml # pylint:disable=wrong-import-order @@ -24,8 +21,8 @@ class OrderedDictYAMLLoader(yaml.SafeLoader): def __init__(self, *args, **kwargs): yaml.SafeLoader.__init__(self, *args, **kwargs) - self.add_constructor(u'tag:yaml.org,2002:map', type(self).construct_yaml_map) - self.add_constructor(u'tag:yaml.org,2002:omap', type(self).construct_yaml_map) + self.add_constructor('tag:yaml.org,2002:map', type(self).construct_yaml_map) + self.add_constructor('tag:yaml.org,2002:omap', type(self).construct_yaml_map) def construct_yaml_map(self, node): data = OrderedDict() |