summaryrefslogtreecommitdiffhomepage
path: root/libs/guessit
diff options
context:
space:
mode:
Diffstat (limited to 'libs/guessit')
-rw-r--r--libs/guessit/__version__.py2
-rw-r--r--libs/guessit/config/options.json21
-rw-r--r--libs/guessit/options.py20
-rw-r--r--libs/guessit/rules/common/date.py5
-rw-r--r--libs/guessit/rules/common/expected.py8
-rw-r--r--libs/guessit/rules/match_processors.py1
-rw-r--r--libs/guessit/rules/properties/date.py17
-rw-r--r--libs/guessit/rules/properties/episodes.py46
-rw-r--r--libs/guessit/rules/properties/release_group.py6
-rw-r--r--libs/guessit/rules/properties/title.py81
-rw-r--r--libs/guessit/rules/properties/website.py14
-rw-r--r--libs/guessit/test/episodes.yml30
-rw-r--r--libs/guessit/test/movies.yml23
-rw-r--r--libs/guessit/test/rules/audio_codec.yml5
-rw-r--r--libs/guessit/test/rules/edition.yml16
-rw-r--r--libs/guessit/test/rules/episodes.yml14
-rw-r--r--libs/guessit/test/rules/release_group.yml9
-rw-r--r--libs/guessit/test/rules/title.yml5
-rw-r--r--libs/guessit/test/streaming_services.yaml1
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