diff options
Diffstat (limited to 'libs/guessit')
-rw-r--r-- | libs/guessit/__version__.py | 2 | ||||
-rw-r--r-- | libs/guessit/config/options.json | 21 | ||||
-rw-r--r-- | libs/guessit/options.py | 20 | ||||
-rw-r--r-- | libs/guessit/rules/common/date.py | 5 | ||||
-rw-r--r-- | libs/guessit/rules/common/expected.py | 8 | ||||
-rw-r--r-- | libs/guessit/rules/match_processors.py | 1 | ||||
-rw-r--r-- | libs/guessit/rules/properties/date.py | 17 | ||||
-rw-r--r-- | libs/guessit/rules/properties/episodes.py | 46 | ||||
-rw-r--r-- | libs/guessit/rules/properties/release_group.py | 6 | ||||
-rw-r--r-- | libs/guessit/rules/properties/title.py | 81 | ||||
-rw-r--r-- | libs/guessit/rules/properties/website.py | 14 | ||||
-rw-r--r-- | libs/guessit/test/episodes.yml | 30 | ||||
-rw-r--r-- | libs/guessit/test/movies.yml | 23 | ||||
-rw-r--r-- | libs/guessit/test/rules/audio_codec.yml | 5 | ||||
-rw-r--r-- | libs/guessit/test/rules/edition.yml | 16 | ||||
-rw-r--r-- | libs/guessit/test/rules/episodes.yml | 14 | ||||
-rw-r--r-- | libs/guessit/test/rules/release_group.yml | 9 | ||||
-rw-r--r-- | libs/guessit/test/rules/title.yml | 5 | ||||
-rw-r--r-- | libs/guessit/test/streaming_services.yaml | 1 |
19 files changed, 259 insertions, 65 deletions
diff --git a/libs/guessit/__version__.py b/libs/guessit/__version__.py index 71a006230..e35c1f96f 100644 --- a/libs/guessit/__version__.py +++ b/libs/guessit/__version__.py @@ -4,4 +4,4 @@ Version module """ # pragma: no cover -__version__ = '3.5.0' +__version__ = '3.8.0' diff --git a/libs/guessit/config/options.json b/libs/guessit/config/options.json index a5a00d2d3..1eeaff84e 100644 --- a/libs/guessit/config/options.json +++ b/libs/guessit/config/options.json @@ -62,6 +62,7 @@ "FLAC": "Flac", "DTS": "DTS", "DTS-HD": {"regex": ["DTS-?HD", "DTS(?=-?MA)"], "conflict_solver": "lambda match, other: other if other.name == 'audio_codec' else '__default__'"}, + "DTS:X": {"string": ["DTS:X", "DTS-X", "DTSX"] }, "Dolby TrueHD": {"regex": ["True-?HD"] }, "Opus": "Opus", "Vorbis": "Vorbis", @@ -189,6 +190,7 @@ "ram", "rm", "ts", + "m2ts", "vob", "wav", "webm", @@ -241,12 +243,10 @@ "Extended": {"string": ["extended"], "regex": ["extended-?cut", "extended-?version"], "tags": ["has-neighbor", "release-group-prefix"]}, "Alternative Cut": {"regex": ["alternat(e|ive)(?:-?Cut)?"], "tags": ["has-neighbor", "release-group-prefix"]}, "Remastered": [ - {"string": "Remastered", "tags": ["has-neighbor", "release-group-prefix"]}, - {"regex": "4k-remaster(?:ed)?", "tags": ["release-group-prefix"]} + {"regex": "(?:4k.)?remaster(?:ed)?", "tags": ["release-group-prefix"]} ], "Restored": [ - {"string": "Restored", "tags": ["has-neighbor", "release-group-prefix"]}, - {"regex": "4k-restore(?:d)?", "tags": ["release-group-prefix"]} + {"regex": "(?:4k.)?restore(?:d)?", "tags": ["release-group-prefix"]} ], "Uncensored": {"string": "Uncensored", "tags": ["has-neighbor", "release-group-prefix"]}, "Uncut": {"string": "Uncut", "tags": ["has-neighbor", "release-group-prefix"]}, @@ -516,7 +516,7 @@ "Hardcoded Subtitles": ["HC", "vost"], "Standard Dynamic Range": {"string": "SDR", "tags": "uhdbluray-neighbor"}, "HDR10": {"regex": "HDR(?:10)?", "tags": "uhdbluray-neighbor"}, - "Dolby Vision": {"regex": "Dolby-?Vision", "tags": "uhdbluray-neighbor"}, + "Dolby Vision": {"regex": "(?:Dolby-?Vision|DV)", "tags": "uhdbluray-neighbor"}, "BT.2020": {"regex": "BT-?2020","tags": "uhdbluray-neighbor"}, "Sample": {"string": "Sample", "tags": ["at-end", "not-a-release-group"]}, "Extras": [ @@ -617,6 +617,7 @@ "AMC": "AMC", "Amazon Prime": [ "AMZN", + "AMZN-CBR", "Amazon", "re:Amazon-?Prime" ], @@ -771,6 +772,13 @@ ], "Opto": "OPTO", "Oprah Winfrey Network": "OWN", + "Paramount+": [ + "PMTP", + "PMNP", + "PMT+", + "Paramount+", + "ParamountPlus" + ], "PBS": "PBS", "PBS Kids": "PBSK", "Peacock": [ @@ -839,6 +847,9 @@ "Yahoo": "YHOO", "YouTube Red": "RED", "ZDF": "ZDF" + }, + "date": { + "week_words": ["week"] } } } diff --git a/libs/guessit/options.py b/libs/guessit/options.py index 1e1ede1ac..521c9f338 100644 --- a/libs/guessit/options.py +++ b/libs/guessit/options.py @@ -7,12 +7,24 @@ import copy import json import os import shlex +import sys from argparse import ArgumentParser -try: - from importlib.resources import read_text -except ImportError: - from importlib_resources import read_text +# importlib.resources.read_text() is deprecated since Python 3.11. +# importlib.resources.files() is new in Python 3.9. +if sys.version_info >= (3, 9, 0): + from importlib.resources import files + + def read_text(package, filename): + """ + Should behave like deprecated importlib.resources.read_text() + """ + return files(package).joinpath(filename).read_text() # pylint:disable=unspecified-encoding +else: + try: + from importlib.resources import read_text + except ImportError: + from importlib_resources import read_text def build_argument_parser(): diff --git a/libs/guessit/rules/common/date.py b/libs/guessit/rules/common/date.py index 1e114569d..5850e9d4e 100644 --- a/libs/guessit/rules/common/date.py +++ b/libs/guessit/rules/common/date.py @@ -34,6 +34,11 @@ def valid_year(year): return 1920 <= year < 2030 +def valid_week(week): + """Check if number is a valid week""" + return 1 <= week < 53 + + def _is_int(string): """ Check if the input string is an integer diff --git a/libs/guessit/rules/common/expected.py b/libs/guessit/rules/common/expected.py index 19f2f8874..000fcbfa8 100644 --- a/libs/guessit/rules/common/expected.py +++ b/libs/guessit/rules/common/expected.py @@ -3,9 +3,8 @@ """ Expected property factory """ -from rebulk.remodule import re - from rebulk import Rebulk +from rebulk.remodule import re from rebulk.utils import find_all from . import dash, seps @@ -42,12 +41,13 @@ def build_expected_function(context_key): for match in matches: ret.append(match.span) else: - value = search for sep in seps: input_string = input_string.replace(sep, ' ') search = search.replace(sep, ' ') for start in find_all(input_string, search, ignore_case=True): - ret.append({'start': start, 'end': start + len(search), 'value': value}) + end = start + len(search) + value = input_string[start:end] + ret.append({'start': start, 'end': end, 'value': value}) return ret return expected diff --git a/libs/guessit/rules/match_processors.py b/libs/guessit/rules/match_processors.py index 0b49372fe..7cdb06179 100644 --- a/libs/guessit/rules/match_processors.py +++ b/libs/guessit/rules/match_processors.py @@ -18,3 +18,4 @@ def strip(match, chars=seps): match.end -= 1 if not match: return False + return None diff --git a/libs/guessit/rules/properties/date.py b/libs/guessit/rules/properties/date.py index e50cdfa3f..4f322c30d 100644 --- a/libs/guessit/rules/properties/date.py +++ b/libs/guessit/rules/properties/date.py @@ -1,13 +1,17 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- """ -date and year properties +date, week and year properties """ +import re + from rebulk import Rebulk, RemoveMatch, Rule -from ..common.date import search_date, valid_year +from ..common import dash +from ..common.date import search_date, valid_year, valid_week from ..common.pattern import is_disabled from ..common.validators import seps_surround +from ...reutils import build_or_pattern def date(config): # pylint:disable=unused-argument @@ -28,6 +32,15 @@ def date(config): # pylint:disable=unused-argument else '__default__', validator=lambda match: seps_surround(match) and valid_year(match.value)) + rebulk.regex(build_or_pattern(config.get('week_words')) + r"-?(\d{1,2})", + name="week", formatter=int, + children=True, + flags=re.IGNORECASE, abbreviations=[dash], + conflict_solver=lambda match, other: other + if other.name in ('episode', 'season') and len(other.raw) < len(match.raw) + else '__default__', + validator=lambda match: seps_surround(match) and valid_week(match.value)) + def date_functional(string, context): # pylint:disable=inconsistent-return-statements """ Search for date in the string and retrieves match diff --git a/libs/guessit/rules/properties/episodes.py b/libs/guessit/rules/properties/episodes.py index 0fbffc27a..cbb05b891 100644 --- a/libs/guessit/rules/properties/episodes.py +++ b/libs/guessit/rules/properties/episodes.py @@ -68,8 +68,8 @@ def episodes(config): if other.name in ('video_codec', 'audio_codec', 'container', 'date'): return match 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)): + 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') @@ -172,7 +172,7 @@ def episodes(config): 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+)')\ + 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', @@ -186,7 +186,7 @@ def episodes(config): .defaults(tags=['SxxExx']) \ .regex(r'(?P<season>\d+)@?' + build_or_pattern(season_ep_markers, name='episodeMarker') + - r'@?(?P<episode>\d+)').repeater('+') \ + r'@?(?P<episode>\d+)').repeater('+') rebulk.chain(tags=['SxxExx'], validate_all=True, @@ -511,7 +511,6 @@ class AbstractSeparatorRange(Rule): """ Remove separator matches and create matches for season range. """ - priority = 128 consequence = [RemoveMatch, AppendMatch] def __init__(self, range_separators, property_name): @@ -519,6 +518,9 @@ class AbstractSeparatorRange(Rule): self.range_separators = range_separators self.property_name = property_name + def _can_start_range(self, match): # pylint: disable=unused-argument + return True + def when(self, matches, context): to_remove = [] to_append = [] @@ -539,7 +541,10 @@ class AbstractSeparatorRange(Rule): to_remove.append(separator) previous_match = None - for next_match in matches.named(self.property_name): + sorted_matches = sorted(matches.named(self.property_name), key=lambda x: x.span[0]) + for next_match in sorted_matches: + if not previous_match and not self._can_start_range(next_match): + continue if previous_match: separator = matches.input_string[previous_match.initiator.end:next_match.initiator.start] if separator not in self.range_separators: @@ -577,17 +582,25 @@ class RenameToAbsoluteEpisode(Rule): The matches in the group with higher episode values are renamed to absolute_episode. """ - consequence = RenameMatch('absolute_episode') + consequence = [RenameMatch('absolute_episode'), RemoveMatch] def when(self, matches, context): # pylint:disable=inconsistent-return-statements initiators = {match.initiator for match in matches.named('episode') if len(match.initiator.children.named('episode')) > 1} if len(initiators) != 2: - ret = [] + ret = ([], []) for filepart in matches.markers.named('path'): + sxxexx_episode_matches = matches.range(filepart.start + 1, filepart.end, + predicate=lambda m: m.name == 'episode' and + 'SxxExx' in m.tags) if matches.range(filepart.start + 1, filepart.end, predicate=lambda m: m.name == 'episode'): - ret.extend( - matches.starting(filepart.start, predicate=lambda m: m.initiator.name == 'weak_episode')) + absolute_episode_candidate = matches.starting(filepart.start, + predicate=lambda + m: m.initiator.name == 'weak_episode') + if sxxexx_episode_matches: + ret[1].extend(absolute_episode_candidate) + else: + ret[0].extend(absolute_episode_candidate) return ret initiators = sorted(initiators, key=lambda item: item.end) @@ -596,24 +609,29 @@ class RenameToAbsoluteEpisode(Rule): second_range = matches.named('episode', predicate=lambda m: m.initiator == initiators[1]) if len(first_range) == len(second_range): if second_range[0].value > first_range[0].value: - return second_range + return second_range, [] if first_range[0].value > second_range[0].value: - return first_range + return first_range, [] class EpisodeNumberSeparatorRange(AbstractSeparatorRange): """ Remove separator matches and create matches for episoderNumber range. """ + priority = 128 def __init__(self, range_separators): super().__init__(range_separators, "episode") + def _can_start_range(self, match): + return 'weak-episode' not in match.tags + class SeasonSeparatorRange(AbstractSeparatorRange): """ Remove separator matches and create matches for season range. """ + priority = 128 def __init__(self, range_separators): super().__init__(range_separators, "season") @@ -727,7 +745,7 @@ class RemoveInvalidSeason(Rule): for filepart in matches.markers.named('path'): strong_season = matches.range(filepart.start, filepart.end, index=0, predicate=lambda m: m.name == 'season' - and not m.private and 'SxxExx' in m.tags) + and not m.private and 'SxxExx' in m.tags) if strong_season: if strong_season.initiator.children.named('episode'): for season in matches.range(strong_season.end, filepart.end, @@ -755,7 +773,7 @@ class RemoveInvalidEpisode(Rule): for filepart in matches.markers.named('path'): strong_episode = matches.range(filepart.start, filepart.end, index=0, predicate=lambda m: m.name == 'episode' - and not m.private and 'SxxExx' in m.tags) + and not m.private and 'SxxExx' in m.tags) if strong_episode: strong_ep_marker = RemoveInvalidEpisode.get_episode_prefix(matches, strong_episode) for episode in matches.range(strong_episode.end, filepart.end, diff --git a/libs/guessit/rules/properties/release_group.py b/libs/guessit/rules/properties/release_group.py index 09e845f56..4f5286ca7 100644 --- a/libs/guessit/rules/properties/release_group.py +++ b/libs/guessit/rules/properties/release_group.py @@ -4,6 +4,7 @@ release_group property """ import copy +import re from rebulk import Rebulk, Rule, AppendMatch, RemoveMatch from rebulk.match import Match @@ -50,7 +51,10 @@ def release_group(config): if string.lower().endswith(forbidden) and string[-len(forbidden) - 1:-len(forbidden)] in seps: string = string[:len(forbidden)] string = string.strip(groupname_seps) - return string.strip() + + # Release groups that credit individual members often use a format like "Title (MediaInfo Individual) [Group]". + # This results in a group name of "Individual) [Group]", which should be transformed to "Individual Group". + return re.sub(r'(.+)\)\s?\[(.+)\]', r'\1 \2', string.strip()) rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'release_group')) diff --git a/libs/guessit/rules/properties/title.py b/libs/guessit/rules/properties/title.py index 77f5bd113..fad8484d6 100644 --- a/libs/guessit/rules/properties/title.py +++ b/libs/guessit/rules/properties/title.py @@ -142,8 +142,8 @@ class TitleBaseRule(Rule): for outside in outside_matches: other_languages.extend(matches.range(outside.start, outside.end, lambda c_match: c_match.name == match.name and - c_match not in to_keep and - c_match.value not in NON_SPECIFIC_LANGUAGES)) + c_match not in to_keep and + c_match.value not in NON_SPECIFIC_LANGUAGES)) if not other_languages and (not starting or len(match.raw) <= 3): return True @@ -163,7 +163,8 @@ class TitleBaseRule(Rule): return match.start >= hole.start and match.end <= hole.end return True - def check_titles_in_filepart(self, filepart, matches, context): # pylint:disable=inconsistent-return-statements + def check_titles_in_filepart(self, filepart, matches, context, # pylint:disable=inconsistent-return-statements + additional_ignored=None): """ Find title in filepart (ignoring language) """ @@ -171,7 +172,8 @@ class TitleBaseRule(Rule): start, end = filepart.span holes = matches.holes(start, end + 1, formatter=formatters(cleanup, reorder_title), - ignore=self.is_ignored, + ignore=self.is_ignored if additional_ignored is None else lambda m: self.is_ignored( + m) or additional_ignored(m), predicate=lambda m: m.value) holes = self.holes_process(holes, matches) @@ -247,8 +249,46 @@ class TitleBaseRule(Rule): titles = [hole] return titles, to_remove + def _serie_name_filepart(self, matches, fileparts): + # Try to get show title from subdirectory of a season only directory (Show Name/Season 1/episode_title.avi) + for index in range(len(fileparts) - 1): + if index == 0: + continue + filepart = fileparts[index] + filepart_matches = [m for m in matches.range(filepart.start, filepart.end) if not m.private] + if len(filepart_matches) == 1 and filepart_matches[0].name == 'season' and \ + (filepart_matches[0].span == filepart.span or + filepart_matches[0].parent and filepart_matches[0].parent.span == filepart.span): + # Filepath match season match exactly + return fileparts[index + 1] + return None + + def _serie_name_filepart_match(self, matches, context, serie_name_filepart, to_append, to_remove): + def serie_name_filepart_ignored(match): + for tag in match.tags: + if tag == 'weak' or tag.startswith('weak-'): + return True + return False + + titles = self.check_titles_in_filepart(serie_name_filepart, matches, context, serie_name_filepart_ignored) + if titles: + titles, to_remove_c = titles + if len(titles) == 1: + to_append.extend(titles) + to_remove.extend(to_remove_c) + return titles[0] + return None + + def _year_fileparts(self, matches, fileparts): + year_fileparts = [] + for filepart in fileparts: + year_match = matches.range(filepart.start, filepart.end, lambda match: match.name == 'year', 0) + if year_match: + year_fileparts.append(filepart) + return year_fileparts + def when(self, matches, context): - ret = [] + to_append = [] to_remove = [] if matches.named(self.match_name, lambda match: 'expected' in match.tags): @@ -257,36 +297,43 @@ class TitleBaseRule(Rule): fileparts = [filepart for filepart in list(marker_sorted(matches.markers.named('path'), matches)) if not self.filepart_filter or self.filepart_filter(filepart, matches)] - # Priorize fileparts containing the year - years_fileparts = [] - for filepart in fileparts: - year_match = matches.range(filepart.start, filepart.end, lambda match: match.name == 'year', 0) - if year_match: - years_fileparts.append(filepart) + serie_name_filepart = self._serie_name_filepart(matches, fileparts) + + serie_name_filepath_match = None + if serie_name_filepart: + serie_name_filepath_match = self._serie_name_filepart_match(matches, context, serie_name_filepart, + to_append, to_remove) + + # Force inclusion of fileparts containing the year + year_fileparts = self._year_fileparts(matches, fileparts) for filepart in fileparts: try: - years_fileparts.remove(filepart) + year_fileparts.remove(filepart) except ValueError: pass titles = self.check_titles_in_filepart(filepart, matches, context) if titles: titles, to_remove_c = titles - ret.extend(titles) + if serie_name_filepath_match: + for title_match in titles: + if title_match.value != serie_name_filepath_match.value: + title_match.name = 'episode_title' + to_append.extend(titles) to_remove.extend(to_remove_c) break # Add title match in all fileparts containing the year. - for filepart in years_fileparts: + for filepart in year_fileparts: titles = self.check_titles_in_filepart(filepart, matches, context) if titles: # pylint:disable=unbalanced-tuple-unpacking titles, to_remove_c = titles - ret.extend(titles) + to_append.extend(titles) to_remove.extend(to_remove_c) - if ret or to_remove: - return ret, to_remove + if to_append or to_remove: + return to_append, to_remove return False diff --git a/libs/guessit/rules/properties/website.py b/libs/guessit/rules/properties/website.py index e5cea22ab..4df443d83 100644 --- a/libs/guessit/rules/properties/website.py +++ b/libs/guessit/rules/properties/website.py @@ -31,13 +31,13 @@ def website(config): rebulk = rebulk.regex_defaults(flags=re.IGNORECASE).string_defaults(ignore_case=True) rebulk.defaults(name="website") - with files('guessit.data') as data_files: - tld_file = data_files.joinpath('tlds-alpha-by-domain.txt').read_text(encoding='utf-8') - tlds = [ - tld.strip() - for tld in tld_file.split('\n') - if '--' not in tld - ][1:] # All registered domain extension + data_files = files('guessit.data') + tld_file = data_files.joinpath('tlds-alpha-by-domain.txt').read_text(encoding='utf-8') + tlds = [ + tld.strip() + for tld in tld_file.split('\n') + if '--' not in tld + ][1:] # All registered domain extension safe_tlds = config['safe_tlds'] # For sure a website extension safe_subdomains = config['safe_subdomains'] # For sure a website subdomain diff --git a/libs/guessit/test/episodes.yml b/libs/guessit/test/episodes.yml index 52e29ecaa..7de7da15c 100644 --- a/libs/guessit/test/episodes.yml +++ b/libs/guessit/test/episodes.yml @@ -2340,7 +2340,7 @@ ? /11.22.63/Season 1/11.22.63.106.hdtv-abc : options: -T 11.22.63 - title: 11.22.63 + title: 11 22 63 season: 1 episode: 6 source: HDTV @@ -4062,8 +4062,7 @@ type: episode ? 165.Show Name.s08e014 -: absolute_episode: 165 - title: Show Name +: title: 165 Show Name season: 8 episode: 14 type: episode @@ -4672,7 +4671,7 @@ episode: 1 screen_size: 1080p streaming_service: Viki - source: Web + source: Web release_group: BLUEBERRY container: mp4 mimetype: video/mp4 @@ -4702,7 +4701,6 @@ video_codec: H.264 audio_codec: FLAC container: mkv - mimetype: video/x-matroska type: episode ? "[EveTaku] Kyouso Giga ONA v2 [540p][128BAC43].mkv" @@ -4713,7 +4711,6 @@ screen_size: 540p crc32: 128BAC43 container: mkv - mimetype: video/x-matroska type: episode ? '[Erai-raws] Fumetsu no Anata e - 03 [720p][Multiple Subtitle].mkv' @@ -4723,7 +4720,6 @@ screen_size: 720p subtitle_language: mul container: mkv - mimetype: video/x-matroska type: episode ? Mom.S06E08.Jell-O.Shots.and.the.Truth.About.Santa.1080p.AMZN.WEB-DL.DDP5.1.H.264-NTb.mkv @@ -4739,7 +4735,6 @@ video_codec: H.264 release_group: NTb container: mkv - mimetype: video/x-matroska type: episode ? Archer.2009.S12E05.Shots.720p.HULU.WEB-DL.DDP5.1.H.264-NOGRP @@ -4756,3 +4751,22 @@ video_codec: H.264 release_group: NOGRP type: episode + +? /mydatapool/mydata/Videos/Shows/C/Caprica (2008)/Season 1/Apotheosis_1920x1080.mp4 +: title: Caprica + year: 2008 + season: 1 + episode_title: Apotheosis + screen_size: 1080p + container: mp4 + type: episode + +? 4400.S01E01.1080p.WEB.H264-NOGRP +: title: '4400' + season: 1 + episode: 1 + screen_size: 1080p + source: Web + video_codec: H.264 + release_group: NOGRP + type: episode diff --git a/libs/guessit/test/movies.yml b/libs/guessit/test/movies.yml index ff4232a24..12f0c57f6 100644 --- a/libs/guessit/test/movies.yml +++ b/libs/guessit/test/movies.yml @@ -1314,6 +1314,14 @@ release_group: VISIONPLUSHDR1000 type: movie +? Foo.Bar.2021.DV.2160p.WEB-DL.x265-ASDF +: title: Foo Bar + screen_size: 2160p + other: [Dolby Vision] + video_codec: H.265 + release_group: ASDF + type: movie + ? How To Steal A Dog.2014.BluRay.1080p.12bit.HEVC.OPUS 5.1-Hn1Dr2.mkv : title: How To Steal A Dog year: 2014 @@ -1711,6 +1719,20 @@ release_group: LAZY type: movie +? The.Movie.2016.2160p.UHD.BluRay.REMUX.HDR.HEVC.DTS-X-NOGROUP +: title: The Movie + year: 2016 + screen_size: 2160p + source: Ultra HD Blu-ray + other: + - HDR10 + - Remux + video_codec: H.265 + video_profile: High Efficiency Video Coding + audio_codec: DTS:X + release_group: NOGROUP + type: movie + ? Test (2013) [WEBDL-1080p] [x264 AC3] [ENG+RU+PT] [NTb].mkv : title: Test year: 2013 @@ -1857,5 +1879,4 @@ video_codec: H.264 release_group: DON container: mkv - mimetype: video/x-matroska type: movie diff --git a/libs/guessit/test/rules/audio_codec.yml b/libs/guessit/test/rules/audio_codec.yml index 6c937b831..b215751eb 100644 --- a/libs/guessit/test/rules/audio_codec.yml +++ b/libs/guessit/test/rules/audio_codec.yml @@ -120,6 +120,11 @@ : audio_codec: DTS audio_profile: Extended Surround +? DTS:X +? DTS-X +? DTSX +: audio_codec: DTS:X + ? DD-EX ? DDEX ? -EX diff --git a/libs/guessit/test/rules/edition.yml b/libs/guessit/test/rules/edition.yml index d1d5277f0..c34d60632 100644 --- a/libs/guessit/test/rules/edition.yml +++ b/libs/guessit/test/rules/edition.yml @@ -31,6 +31,22 @@ ? Super Movie Alternative Cut XViD : edition: Alternative Cut +? Remaster +? Remastered +? 4k-Remaster +? 4k-Remastered +? 4k Remaster +? 4k Remastered +: edition: Remastered + +? Restore +? Restored +? 4k-Restore +? 4k-Restored +? 4k Restore +? 4k Restored +: edition: Restored + ? ddc : edition: Director's Definitive Cut diff --git a/libs/guessit/test/rules/episodes.yml b/libs/guessit/test/rules/episodes.yml index 44e06a3bb..7eff1e722 100644 --- a/libs/guessit/test/rules/episodes.yml +++ b/libs/guessit/test/rules/episodes.yml @@ -293,7 +293,7 @@ ? Something 4x05-06 ? Something-4x05-06 : options: -T something - title: something + title: Something season: 4 episode: - 5 @@ -329,3 +329,15 @@ year: 2010 season: 2010 episode: 2 + +? Show Name - S32-Dummy 45-Ep 6478 +: title: Show Name + episode_title: Dummy 45 + season: 32 + episode: 6478 + +? Show Name - S32-Week 45-Ep 6478 +: title: Show Name + season: 32 + week: 45 + episode: 6478 diff --git a/libs/guessit/test/rules/release_group.yml b/libs/guessit/test/rules/release_group.yml index c96383e94..53e5f731f 100644 --- a/libs/guessit/test/rules/release_group.yml +++ b/libs/guessit/test/rules/release_group.yml @@ -69,3 +69,12 @@ : title: Show Name video_codec: H.264 release_group: RiPRG + +? Archer (2009) S13E01 The Big Con (1080p AMZN Webrip x265 10bit EAC3 5.1 - JBENT)[TAoE] +: release_group: JBENT TAoE + +? Dark Phoenix (2019) (1080p BluRay x265 HEVC 10bit AAC 7.1 Tigole) [QxR] +: release_group: Tigole QxR + +? The Peripheral (2022) Season 1 S01 (1080p AMZN WEB-DL x265 HEVC 10bit DDP5.1 D0ct0rLew) [SEV] +: release_group: D0ct0rLew SEV
\ No newline at end of file diff --git a/libs/guessit/test/rules/title.yml b/libs/guessit/test/rules/title.yml index 05c7f2086..82bd91bdd 100644 --- a/libs/guessit/test/rules/title.yml +++ b/libs/guessit/test/rules/title.yml @@ -41,3 +41,8 @@ episode_title: This E.P.T.I.T.L.E has dots type: episode +? /mydatapool/mydata/Videos/Shows/C/Caprica/Season 1/Apotheosis_1920x1080.mp4 +: title: Caprica + episode_title: Apotheosis + season: 1 + type: episode diff --git a/libs/guessit/test/streaming_services.yaml b/libs/guessit/test/streaming_services.yaml index 1573e92a3..bdfb32d75 100644 --- a/libs/guessit/test/streaming_services.yaml +++ b/libs/guessit/test/streaming_services.yaml @@ -54,6 +54,7 @@ # Streaming service: Amazon ? Show.Name.S07E04.Service.1080p.AMZN.WEBRip.DD+5.1.x264 +? Show.Name.S07E04.Service.1080p.AMZN-CBR.WEBRip.DD+5.1.x264 ? Show.Name.S07E04.Service.1080p.AmazonPrime.WEBRip.DD+5.1.x264 : title: Show Name season: 7 |