diff --git a/MIGRATION.rst b/MIGRATION.rst index 93b43e9..6e13dc7 100644 --- a/MIGRATION.rst +++ b/MIGRATION.rst @@ -18,6 +18,13 @@ For episodes, some properties have been renamed - ``series`` is now ``title``. - ``title`` is now ``episodeTitle``. -All other episodes properties have been ported with the same name (*Work in progress ...*) +For movies, some properties have been renamed -For movies, all properties have been ported with the same name (*Work in progress ...*). \ No newline at end of file +- ``filmtitle`` is now ``filmSeries`` + +``type`` ``episode`` value is now ``series``. + +All info type (``seriesinfo``, ``movieinfo``) have been removed in favor of checking the ``extension`` property for +``nfo`` value. + +All other properties have been ported with the same name (*Work in progress ...*) \ No newline at end of file diff --git a/guessit/rules/common/__init__.py b/guessit/rules/common/__init__.py index de50b18..b3ff70c 100644 --- a/guessit/rules/common/__init__.py +++ b/guessit/rules/common/__init__.py @@ -5,7 +5,7 @@ Common module """ import six -seps = six.u(r' [](){}+*|&=§-_~#/\.,;') # list of tags/words separators +seps = six.u(r' [](){}+*|&=§-_~#/\.,;:') # list of tags/words separators title_seps = six.u(r'-+/\|;') # separators for title diff --git a/guessit/rules/processors.py b/guessit/rules/processors.py index 7c2c491..d349292 100644 --- a/guessit/rules/processors.py +++ b/guessit/rules/processors.py @@ -11,6 +11,8 @@ from .common.formatters import strip from .common.comparators import marker_sorted from .common.date import valid_year +from .properties.type import type_processor + import six @@ -161,4 +163,4 @@ def enlarge_group_matches(matches): PROCESSORS = Rebulk().processor(enlarge_group_matches).post_processor(equivalent_holes, remove_ambiguous, - country_in_title, season_year) + country_in_title, season_year, type_processor) diff --git a/guessit/rules/properties/country.py b/guessit/rules/properties/country.py index b2facfa..376a929 100644 --- a/guessit/rules/properties/country.py +++ b/guessit/rules/properties/country.py @@ -93,6 +93,8 @@ def find_countries(string, context=None): COUNTRY.functional(find_countries, - #  Prefer language and any other property over country - conflict_solver=lambda match, other: match) + #  Prefer language and any other property over country if not US or GB. + conflict_solver=lambda match, other: match + if other.name != 'language' or match.value not in [babelfish.Country('US'), babelfish.Country('GB')] + else other) diff --git a/guessit/rules/properties/edition.py b/guessit/rules/properties/edition.py index 186f25e..9afce95 100644 --- a/guessit/rules/properties/edition.py +++ b/guessit/rules/properties/edition.py @@ -13,7 +13,10 @@ EDITION = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]).str EDITION.defaults(name='edition', validator=seps_surround) EDITION.regex('collector', 'collector-edition', 'edition-collector', value='Collector Edition') -EDITION.regex('special-edition', 'edition-special', value='Special Edition') +EDITION.regex('special-edition', 'edition-special', value='Special Edition', + conflict_solver=lambda match, other: other + if other.name == 'episodeDetails' and other.value == 'Special' + else '__default__') EDITION.regex('criterion-edition', 'edition-criterion', value='Criterion Edition') EDITION.regex('deluxe', 'deluxe-edition', 'edition-deluxe', value='Deluxe Edition') EDITION.regex('director\'?s?-cut', 'director\'?s?-cut-edition', 'edition-director\'?s?-cut', value='Director\'s cut') diff --git a/guessit/rules/properties/episode_title.py b/guessit/rules/properties/episode_title.py index 3a3ed48..541be23 100644 --- a/guessit/rules/properties/episode_title.py +++ b/guessit/rules/properties/episode_title.py @@ -57,7 +57,7 @@ class EpisodeTitleFromPosition(TitleBaseRule): lambda previous: any(name in previous.names for name in ['episodeNumber', 'episodeDetails', 'episodeCount', 'season', 'seasonCount', - 'date', 'title']), + 'date', 'title', 'year']), 0) crc32 = matches.named('crc32') @@ -70,15 +70,10 @@ class EpisodeTitleFromPosition(TitleBaseRule): return True return False - def is_ignored(self, match): + def should_remove(self, match, matches, filepart, hole): if match.name == 'episodeDetails': - return True - return super(EpisodeTitleFromPosition, self).is_ignored(match) - - def should_keep(self, match, to_keep, matches, filepart, hole, starting): - if match.name == 'episodeDetails' and not matches.previous(match, lambda match: match.name == 'season'): - return True, False # Keep episodeDetails, but don't crop title. - return super(EpisodeTitleFromPosition, self).should_keep(match, to_keep, matches, filepart, hole, starting) + return False + return super(EpisodeTitleFromPosition, self).should_remove(match, matches, filepart, hole) def __init__(self): super(EpisodeTitleFromPosition, self).__init__('episodeTitle', ['title']) @@ -109,7 +104,7 @@ class AlternativeTitleReplace(Rule): lambda previous: any(name in previous.names for name in ['episodeNumber', 'episodeDetails', 'episodeCount', 'season', 'seasonCount', - 'date', 'title']), + 'date', 'title', 'year']), 0) crc32 = matches.named('crc32') diff --git a/guessit/rules/properties/episodes.py b/guessit/rules/properties/episodes.py index bc1bb9c..5494724 100644 --- a/guessit/rules/properties/episodes.py +++ b/guessit/rules/properties/episodes.py @@ -35,8 +35,7 @@ EPISODES.regex(r'(?P\d+)x(?P\d+)' + # episodeDetails property for episode_detail in ('Special', 'Bonus', 'Omake', 'Ova', 'Oav', 'Pilot', 'Unaired'): - EPISODES.string(episode_detail, value=episode_detail, name='episodeDetails', - conflict_solver=lambda match, other: None) + EPISODES.string(episode_detail, value=episode_detail, name='episodeDetails') EPISODES.regex(r'Extras?', name='episodeDetails', value='Extras') EPISODES.defaults(validate_all=True, validator={'__parent__': seps_surround}, children=True, private_parent=True) diff --git a/guessit/rules/properties/part.py b/guessit/rules/properties/part.py index e3975d7..ef83c14 100644 --- a/guessit/rules/properties/part.py +++ b/guessit/rules/properties/part.py @@ -7,11 +7,12 @@ import regex as re from rebulk import Rebulk from ..common import dash +from ..common.validators import seps_surround from ..common.numeral import numeral, parse_numeral -PART = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]) +PART = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash], validator={'__parent__': seps_surround}) prefixes = ['pt', 'part'] -PART.regex(r'\L-(' + numeral + r')(?:$|[^\d])', prefixes=prefixes, - name='part', private_parent=True, children=True, formatter=parse_numeral) +PART.regex(r'\L-(' + numeral + r')', prefixes=prefixes, + name='part', validate_all=True, private_parent=True, children=True, formatter=parse_numeral) diff --git a/guessit/rules/properties/release_group.py b/guessit/rules/properties/release_group.py index 9e2f19f..e155879 100644 --- a/guessit/rules/properties/release_group.py +++ b/guessit/rules/properties/release_group.py @@ -68,6 +68,9 @@ class SceneReleaseGroup(Rule): not matches.input_string[previous_match.end:last_hole.start].strip(seps)\ and not int_coercable(last_hole.value.strip(seps)): + last_hole.name = 'releaseGroup' + last_hole.tags = ['scene'] + # if hole is insed a group marker with same value, remove [](){} ... group = matches.markers.at_match(last_hole, lambda marker: marker.name == 'group', 0) if group: @@ -75,9 +78,8 @@ class SceneReleaseGroup(Rule): if group.value == last_hole.value: last_hole.start = group.start + 1 last_hole.end = group.end - 1 + last_hole.tags = ['anime'] - last_hole.name = 'releaseGroup' - last_hole.tags = ['scene'] ret.append(last_hole) return ret @@ -94,7 +96,7 @@ class AnimeReleaseGroup(Rule): ret = [] # If a scene releaseGroup is found, ignore this kind of releaseGroup rule. - if matches.named('releaseGroup', lambda match: 'scene' in match.tags): + if matches.named('releaseGroup'): return ret for filepart in marker_sorted(matches.markers.named('path'), matches): diff --git a/guessit/rules/properties/screen_size.py b/guessit/rules/properties/screen_size.py index 7f5f146..62da037 100644 --- a/guessit/rules/properties/screen_size.py +++ b/guessit/rules/properties/screen_size.py @@ -9,29 +9,41 @@ import regex as re from ..common.validators import seps_surround from guessit.rules.common import dash -SCREEN_SIZE = Rebulk().regex_defaults(flags=re.IGNORECASE) -SCREEN_SIZE.defaults(name="screenSize", validator=seps_surround, - conflict_solver=lambda match, other: '__default__' - if other.name in ['screenSize', 'episodeNumber', 'season'] - else other) +def conflict_solver(match, other): + """ + Conflict solver for most screenSize. + """ + if other.name in ['episodeNumber', 'season']: + return '__default__' + if other.name == 'screenSize' and 'resolution' in other.tags: + # The chtouile to solve conflict in "720 x 432" string matching both 720p pattern (but it's not 720p ...) + int_value = _digits_re.findall(match.raw)[-1] + if other.value.startswith(int_value): + return match + return other -SCREEN_SIZE.regex(r"(?:\d{3,}(?:\\|\/|x|\*))?360(?:i|p?x?)", value="360p") -SCREEN_SIZE.regex(r"(?:\d{3,}(?:\\|\/|x|\*))?368(?:i|p?x?)", value="368p") -SCREEN_SIZE.regex(r"(?:\d{3,}(?:\\|\/|x|\*))?480(?:i|p?x?)", value="480p") -SCREEN_SIZE.regex(r"(?:\d{3,}(?:\\|\/|x|\*))?576(?:i|p?x?)", value="576p") -SCREEN_SIZE.regex(r"(?:\d{3,}(?:\\|\/|x|\*))?720(?:i|p?x?)", value="720p") -SCREEN_SIZE.regex(r"(?:\d{3,}(?:\\|\/|x|\*))?900(?:i|p?x?)", value="900p") -SCREEN_SIZE.regex(r"(?:\d{3,}(?:\\|\/|x|\*))?1080i", value="1080i") -SCREEN_SIZE.regex(r"(?:\d{3,}(?:\\|\/|x|\*))?1080p?x?", value="1080p") -SCREEN_SIZE.regex(r"(?:\d{3,4}(?:\\|\/|x|\*))?2160(?:i|p?x?)", value="4K") +SCREEN_SIZE = Rebulk().regex_defaults(flags=re.IGNORECASE) +SCREEN_SIZE.defaults(name="screenSize", validator=seps_surround, conflict_solver=conflict_solver) + +SCREEN_SIZE.regex(r"(?:\d{3,}(?:x|\*))?360(?:i|p?x?)", value="360p") +SCREEN_SIZE.regex(r"(?:\d{3,}(?:x|\*))?368(?:i|p?x?)", value="368p") +SCREEN_SIZE.regex(r"(?:\d{3,}(?:x|\*))?480(?:i|p?x?)", value="480p") +SCREEN_SIZE.regex(r"(?:\d{3,}(?:x|\*))?576(?:i|p?x?)", value="576p") +SCREEN_SIZE.regex(r"(?:\d{3,}(?:x|\*))?720(?:i|p?x?)", value="720p") +SCREEN_SIZE.regex(r"(?:\d{3,}(?:x|\*))?900(?:i|p?x?)", value="900p") +SCREEN_SIZE.regex(r"(?:\d{3,}(?:x|\*))?1080i", value="1080i") +SCREEN_SIZE.regex(r"(?:\d{3,}(?:x|\*))?1080p?x?", value="1080p") +SCREEN_SIZE.regex(r"(?:\d{3,}(?:x|\*))?2160(?:i|p?x?)", value="4K") _digits_re = re.compile(r'\d+') - -SCREEN_SIZE.regex(r'\d{3,4}-?[x\*]-?\d{3,4}', abbreviations=[dash], +SCREEN_SIZE.defaults(name="screenSize", validator=seps_surround) +SCREEN_SIZE.regex(r'\d{3,}-?(?:x|\*)-?\d{3,}', formatter=lambda value: 'x'.join(_digits_re.findall(value)), - conflict_solver=lambda match, other: match if other.name == 'screenSize' else other) + abbreviations=[dash], + tags=['resolution'], + conflict_solver=lambda match, other: '__default__' if other.name == 'screenSize' else other) class ScreenSizeOnlyOne(Rule): diff --git a/guessit/rules/properties/title.py b/guessit/rules/properties/title.py index 4917dfc..0822de1 100644 --- a/guessit/rules/properties/title.py +++ b/guessit/rules/properties/title.py @@ -72,7 +72,7 @@ class TitleBaseRule(Rule): """ Ignore matches when scanning for title (hole) """ - return match.name in ['language', 'country'] + return match.name in ['language', 'country', 'episodeDetails'] def should_keep(self, match, to_keep, matches, filepart, hole, starting): """ @@ -93,22 +93,35 @@ class TitleBaseRule(Rule): :rtype: """ # Keep language if other languages exists in the filepart. - outside_matches = filepart.crop(hole) - other_languages = [] - 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)) + if match.name in ['language', 'country']: + outside_matches = filepart.crop(hole) + other_languages = [] + 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)) - if not other_languages: - return True + if not other_languages: + return True return False + def should_remove(self, match, matches, filepart, hole): + """ + Check if this match should be removed after beeing ignored. + :param match: + :param matches: + :param filepart: + :param hole: + :return: + """ + return True + def check_titles_in_filepart(self, filepart, matches): """ Find title in filepart (ignoring language) """ - # pylint:disable=too-many-locals,too-many-branches + # pylint:disable=too-many-locals,too-many-branches,too-many-statements start, end = filepart.span holes = matches.holes(start, end + 1, formatter=formatters(cleanup, reorder_title), @@ -160,7 +173,9 @@ class TitleBaseRule(Rule): if crop: hole.start = ignored_match.end - to_remove.extend(ignored_matches) + for match in ignored_matches: + if self.should_remove(match, matches, filepart, hole): + to_remove.append(match) for keep_match in to_keep: to_remove.remove(keep_match) @@ -170,8 +185,17 @@ class TitleBaseRule(Rule): if self.alternative_match_name: # Split and keep values that can be a title titles = hole.split(title_seps, lambda match: match.value) - for title in titles[1:]: - title.name = self.alternative_match_name + for title in list(titles[1:]): + previous_title = titles[titles.index(title) - 1] + separator = matches.input_string[previous_title.end:title.start] + if len(separator) == 1 and '-' == separator \ + and previous_title.raw[-1] not in seps \ + and title.raw[0] not in seps: + titles[titles.index(title) - 1].end = title.end + titles.remove(title) + else: + title.name = self.alternative_match_name + else: titles = [hole] return titles, to_remove @@ -238,6 +262,7 @@ class PreferTitleWithYear(Rule): return not context.get('expected_title') def when(self, matches, context): + with_year_in_group = [] with_year = [] titles = matches.named('title') @@ -246,9 +271,15 @@ class PreferTitleWithYear(Rule): if filepart: year_match = matches.range(filepart.start, filepart.end, lambda match: match.name == 'year', 0) if year_match: - with_year.append(title) + group = matches.markers.at_match(year_match, lambda group: group.name == 'group') + if group: + with_year_in_group.append(title) + else: + with_year.append(title) - if with_year: + if with_year_in_group: + title_values = set([title.value for title in with_year_in_group]) + elif with_year: title_values = set([title.value for title in with_year]) else: title_values = set([title.value for title in titles]) diff --git a/guessit/rules/properties/type.py b/guessit/rules/properties/type.py new file mode 100644 index 0000000..ca863ea --- /dev/null +++ b/guessit/rules/properties/type.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +screenSize property +""" +from rebulk.match import Match + + +def _type(matches, value): + """ + Define type match with given value. + :param matches: + :param value: + :return: + """ + matches.append(Match(0, len(matches.input_string), name='type', value=value)) + + +def type_processor(matches): + """ + Post processor to find file type based on all others found matches. + :param matches: + :return: + """ + episode = matches.named('episodeNumber') + season = matches.named('season') + episode_details = matches.named('episodeDetails') + + if episode or season or episode_details: + _type(matches, 'series') + return + + film = matches.named('filmNumber') + if film: + _type(matches, 'movie') + return + + year = matches.named('year') + date = matches.named('date') + + if date and not year: + _type(matches, 'series') + return + + bonus = matches.named('bonusNumber') + if bonus and not year: + _type(matches, 'series') + return + + crc32 = matches.named('crc32') + anime_release_group = matches.named('releaseGroup', lambda match: 'anime' in match.tags) + if crc32 and anime_release_group: + _type(matches, 'series') + return + + _type(matches, 'movie') diff --git a/guessit/test/autodetect.yml b/guessit/test/autodetect.yml new file mode 100644 index 0000000..48b5be3 --- /dev/null +++ b/guessit/test/autodetect.yml @@ -0,0 +1,550 @@ +? Movies/Fear and Loathing in Las Vegas (1998)/Fear.and.Loathing.in.Las.Vegas.720p.HDDVD.DTS.x264-ESiR.mkv +: type: movie + title: Fear and Loathing in Las Vegas + year: 1998 + screenSize: 720p + format: HD-DVD + audioCodec: DTS + videoCodec: h264 + releaseGroup: ESiR + +? Series/Duckman/Duckman - 101 (01) - 20021107 - I, Duckman.avi +: type: series + title: Duckman + season: 1 + episodeNumber: 1 + episodeTitle: I, Duckman + date: 2002-11-07 + +? Series/Neverwhere/Neverwhere.05.Down.Street.[tvu.org.ru].avi +: type: series + title: Neverwhere + episodeNumber: 5 + episodeTitle: Down Street + website: tvu.org.ru + +? Neverwhere.05.Down.Street.[tvu.org.ru].avi +: type: series + title: Neverwhere + episodeNumber: 5 + episodeTitle: Down Street + website: tvu.org.ru + +? Series/Breaking Bad/Minisodes/Breaking.Bad.(Minisodes).01.Good.Cop.Bad.Cop.WEBRip.XviD.avi +: type: series + title: Breaking Bad + episodeFormat: Minisode + episodeNumber: 1 + episodeTitle: Good Cop Bad Cop + format: WEBRip + videoCodec: XviD + +? Series/Kaamelott/Kaamelott - Livre V - Ep 23 - Le Forfait.avi +: type: series + title: Kaamelott + episodeNumber: 23 + episodeTitle: Le Forfait + +? Movies/The Doors (1991)/09.03.08.The.Doors.(1991).BDRip.720p.AC3.X264-HiS@SiLUHD-English.[sharethefiles.com].mkv +: type: movie + title: The Doors + year: 1991 + date: 2008-03-09 + format: BluRay + screenSize: 720p + audioCodec: AC3 + videoCodec: h264 + releaseGroup: HiS@SiLUHD + language: english + website: sharethefiles.com + +? Movies/M.A.S.H. (1970)/MASH.(1970).[Divx.5.02][Dual-Subtitulos][DVDRip].ogm +: type: movie + title: MASH + year: 1970 + videoCodec: DivX + format: DVD + +? the.mentalist.501.hdtv-lol.mp4 +: type: series + title: the mentalist + season: 5 + episodeNumber: 1 + format: HDTV + releaseGroup: lol + +? the.simpsons.2401.hdtv-lol.mp4 +: type: series + title: the simpsons + season: 24 + episodeNumber: 1 + format: HDTV + releaseGroup: lol + +? Homeland.S02E01.HDTV.x264-EVOLVE.mp4 +: type: series + title: Homeland + season: 2 + episodeNumber: 1 + format: HDTV + videoCodec: h264 + releaseGroup: EVOLVE + +? /media/Band_of_Brothers-e01-Currahee.mkv +: type: series + title: Band of Brothers + episodeNumber: 1 + episodeTitle: Currahee + +? /media/Band_of_Brothers-x02-We_Stand_Alone_Together.mkv +: type: series + title: Band of Brothers + bonusNumber: 2 + bonusTitle: We Stand Alone Together + +? /movies/James_Bond-f21-Casino_Royale-x02-Stunts.mkv +: type: movie + title: Casino Royale + filmSeries: James Bond + filmNumber: 21 + bonusNumber: 2 + bonusTitle: Stunts + +? /TV Shows/new.girl.117.hdtv-lol.mp4 +: type: series + title: new girl + season: 1 + episodeNumber: 17 + format: HDTV + releaseGroup: lol + +? The.Office.(US).1x03.Health.Care.HDTV.XviD-LOL.avi +: type: series + title: The Office (US) + country: US + season: 1 + episodeNumber: 3 + episodeTitle: Health Care + format: HDTV + videoCodec: XviD + releaseGroup: LOL + +? The_Insider-(1999)-x02-60_Minutes_Interview-1996.mp4 +: type: movie + title: The Insider + year: 1999 + bonusNumber: 2 + bonusTitle: 60 Minutes Interview-1996 + +? OSS_117--Cairo,_Nest_of_Spies.mkv +: type: movie + title: OSS 117 + alternativeTitle: Cairo, Nest of Spies + +? Rush.._Beyond_The_Lighted_Stage-x09-Between_Sun_and_Moon-2002_Hartford.mkv +: type: movie + title: Rush Beyond The Lighted Stage + bonusNumber: 9 + bonusTitle: Between Sun and Moon + year: 2002 + +? House.Hunters.International.S56E06.720p.hdtv.x264.mp4 +: type: series + title: House Hunters International + season: 56 + episodeNumber: 6 + screenSize: 720p + format: HDTV + videoCodec: h264 + +? White.House.Down.2013.1080p.BluRay.DTS-HD.MA.5.1.x264-PublicHD.mkv +: type: movie + title: White House Down + year: 2013 + screenSize: 1080p + format: BluRay + audioCodec: DTS + audioProfile: HDMA + videoCodec: h264 + releaseGroup: PublicHD + audioChannels: "5.1" + +? White.House.Down.2013.1080p.BluRay.DTSHD.MA.5.1.x264-PublicHD.mkv +: type: movie + title: White House Down + year: 2013 + screenSize: 1080p + format: BluRay + audioCodec: DTS + audioProfile: HDMA + videoCodec: h264 + releaseGroup: PublicHD + audioChannels: "5.1" + +? Hostages.S01E01.Pilot.for.Air.720p.WEB-DL.DD5.1.H.264-NTb.nfo +: type: series + title: Hostages + episodeTitle: Pilot for Air + season: 1 + episodeNumber: 1 + screenSize: 720p + format: WEB-DL + audioChannels: "5.1" + videoCodec: h264 + audioCodec: DolbyDigital + releaseGroup: NTb + +? Despicable.Me.2.2013.1080p.BluRay.x264-VeDeTT.nfo +: type: movie + title: Despicable Me 2 + year: 2013 + screenSize: 1080p + format: BluRay + videoCodec: h264 + releaseGroup: VeDeTT + +? Le Cinquieme Commando 1971 SUBFORCED FRENCH DVDRiP XViD AC3 Bandix.mkv +: type: movie + audioCodec: AC3 + format: DVD + releaseGroup: Bandix + subtitleLanguage: French + title: Le Cinquieme Commando + videoCodec: XviD + year: 1971 + +? Le Seigneur des Anneaux - La Communauté de l'Anneau - Version Longue - BDRip.mkv +: type: movie + format: BluRay + title: Le Seigneur des Anneaux + +? La petite bande (Michel Deville - 1983) VF PAL MP4 x264 AAC.mkv +: type: movie + audioCodec: AAC + language: French + title: La petite bande + videoCodec: h264 + year: 1983 + other: PAL + +? Retour de Flammes (Gregor Schnitzler 2003) FULL DVD.iso +: type: movie + format: DVD + title: Retour de Flammes + type: movie + year: 2003 + +? A.Common.Title.Special.2014.avi +: type: movie + year: 2014 + title: A Common Title Special + +? A.Common.Title.2014.Special.avi +: type: series + year: 2014 + title: A Common Title + episodeTitle: Special + episodeDetails: Special + +? A.Common.Title.2014.Special.Edition.avi +: type: movie + year: 2014 + title: A Common Title + edition: Special Edition + +? Downton.Abbey.2013.Christmas.Special.HDTV.x264-FoV.mp4 +: type: series + year: 2013 + title: Downton Abbey + episodeTitle: Christmas Special + videoCodec: h264 + releaseGroup: FoV + format: HDTV + episodeDetails: Special + +? Doctor_Who_2013_Christmas_Special.The_Time_of_The_Doctor.HD +: type: series + title: Doctor Who + other: HD + episodeDetails: Special + episodeTitle: Christmas Special The Time of The Doctor + year: 2013 + +? Doctor Who 2005 50th Anniversary Special The Day of the Doctor 3.avi +: type: series + title: Doctor Who + episodeDetails: Special + episodeTitle: 50th Anniversary Special The Day of the Doctor 3 + year: 2005 + +? Robot Chicken S06-Born Again Virgin Christmas Special HDTV x264.avi +: type: series + title: Robot Chicken + format: HDTV + season: 6 + episodeTitle: Born Again Virgin Christmas Special + videoCodec: h264 + episodeDetails: Special + +? Wicked.Tuna.S03E00.Head.To.Tail.Special.HDTV.x264-YesTV +: options: -n + type: series + title: Wicked Tuna + episodeTitle: Head To Tail Special + releaseGroup: YesTV + season: 3 + episodeNumber: 0 + videoCodec: h264 + format: HDTV + episodeDetails: Special + +? The.Voice.UK.S03E12.HDTV.x264-C4TV +: options: -n + episodeNumber: 12 + videoCodec: h264 + format: HDTV + title: The Voice (GB) + releaseGroup: C4TV + season: 3 + country: United Kingdom + type: series + +? /tmp/star.trek.9/star.trek.9.mkv +: type: movie + title: star trek 9 + +? star.trek.9.mkv +: type: movie + title: star trek 9 + +? FlexGet.S01E02.TheName.HDTV.xvid +: options: -n + episodeNumber: 2 + format: HDTV + season: 1 + title: FlexGet + episodeTitle: TheName + type: series + videoCodec: XviD + +? FlexGet.S01E02.TheName.HDTV.xvid +: options: -n + episodeNumber: 2 + format: HDTV + season: 1 + title: FlexGet + episodeTitle: TheName + type: series + videoCodec: XviD + +? some.series.S03E14.Title.Here.720p +: options: -n + episodeNumber: 14 + screenSize: 720p + season: 3 + title: some series + episodeTitle: Title Here + type: series + +? '[the.group] Some.Series.S03E15.Title.Two.720p' +: options: -n + episodeNumber: 15 + releaseGroup: the.group + screenSize: 720p + season: 3 + title: Some Series + episodeTitle: Title Two + type: series + +? 'HD 720p: Some series.S03E16.Title.Three' +: options: -n + episodeNumber: 16 + other: HD + screenSize: 720p + season: 3 + title: Some series + episodeTitle: Title Three + type: series + +? Something.Season.2.1of4.Ep.Title.HDTV.torrent +: episodeCount: 4 + episodeNumber: 1 + format: HDTV + season: 2 + title: Something + episodeTitle: Title + type: series + extension: torrent + +? Show-A (US) - Episode Title S02E09 hdtv +: options: -n + country: US + episodeNumber: 9 + format: HDTV + season: 2 + title: Show-A (US) + type: series + +? Jack's.Show.S03E01.blah.1080p +: options: -n + episodeNumber: 1 + screenSize: 1080p + season: 3 + title: Jack's Show + episodeTitle: blah + type: series + +? FlexGet.epic +: options: -n + title: FlexGet epic + type: movie + +? FlexGet.Apt.1 +: options: -n + title: FlexGet Apt 1 + type: movie + +? FlexGet.aptitude +: options: -n + title: FlexGet aptitude + type: movie + +? FlexGet.Step1 +: options: -n + title: FlexGet Step1 + type: movie + +? Movies/El Bosque Animado (1987)/El.Bosque.Animado.[Jose.Luis.Cuerda.1987].[Xvid-Dvdrip-720 * 432].avi +: format: DVD + screenSize: 720x432 + title: El Bosque Animado + videoCodec: XviD + year: 1987 + type: movie + +? Movies/El Bosque Animado (1987)/El.Bosque.Animado.[Jose.Luis.Cuerda.1987].[Xvid-Dvdrip-720x432].avi +: format: DVD + screenSize: 720x432 + title: El Bosque Animado + videoCodec: XviD + year: 1987 + type: movie + +? 2009.shoot.fruit.chan.multi.dvd9.pal +: options: -n + format: DVD + language: mul + other: PAL + title: shoot fruit chan + type: movie + year: 2009 + +? 2009.shoot.fruit.chan.multi.dvd5.pal +: options: -n + format: DVD + language: mul + other: PAL + title: shoot fruit chan + type: movie + year: 2009 + +? The.Flash.2014.S01E01.PREAIR.WEBRip.XviD-EVO.avi +: episodeNumber: 1 + format: WEBRip + other: Preair + releaseGroup: EVO + season: 1 + title: The Flash + type: series + videoCodec: XviD + year: 2014 + +? Ice.Lake.Rebels.S01E06.Ice.Lake.Games.720p.HDTV.x264-DHD +: options: -n + episodeNumber: 6 + format: HDTV + releaseGroup: DHD + screenSize: 720p + season: 1 + title: Ice Lake Rebels + episodeTitle: Ice Lake Games + type: series + videoCodec: h264 + +? The League - S06E10 - Epi Sexy.mkv +: episodeNumber: 10 + season: 6 + title: The League + episodeTitle: Epi Sexy + type: series + +? Stay (2005) [1080p]/Stay.2005.1080p.BluRay.x264.YIFY.mp4 +: format: BluRay + releaseGroup: YIFY + screenSize: 1080p + title: Stay + type: movie + videoCodec: h264 + year: 2005 + +? /media/live/A/Anger.Management.S02E82.720p.HDTV.X264-DIMENSION.mkv +: format: HDTV + releaseGroup: DIMENSION + screenSize: 720p + title: Anger Management + type: series + season: 2 + episodeNumber: 82 + videoCodec: h264 + +? "[Figmentos] Monster 34 - At the End of Darkness [781219F1].mkv" +: type: series + releaseGroup: Figmentos + title: Monster + episodeNumber: 34 + episodeTitle: At the End of Darkness + crc32: 781219F1 + +? Game.of.Thrones.S05E07.720p.HDTV-KILLERS.mkv +: type: series + episodeNumber: 7 + format: HDTV + releaseGroup: KILLERS + screenSize: 720p + season: 5 + title: Game of Thrones + +? Game.of.Thrones.S05E07.HDTV.720p-KILLERS.mkv +: type: series + episodeNumber: 7 + format: HDTV + releaseGroup: KILLERS + screenSize: 720p + season: 5 + title: Game of Thrones + +? Parks and Recreation - [04x12] - Ad Campaign.avi +: type: series + title: Parks and Recreation + season: 4 + episodeNumber: 12 + episodeTitle: Ad Campaign + +? Star Trek Into Darkness (2013)/star.trek.into.darkness.2013.720p.web-dl.h264-publichd.mkv +: type: movie + title: Star Trek Into Darkness + year: 2013 + screenSize: 720p + format: WEB-DL + videoCodec: h264 + releaseGroup: publichd + +? /var/medias/series/The Originals/Season 02/The.Originals.S02E15.720p.HDTV.X264-DIMENSION.mkv +: type: series + title: The Originals + season: 2 + episodeNumber: 15 + screenSize: 720p + format: HDTV + videoCodec: h264 + releaseGroup: DIMENSION diff --git a/guessit/test/movies.yml b/guessit/test/movies.yml index e8eaa5f..a2096ca 100644 --- a/guessit/test/movies.yml +++ b/guessit/test/movies.yml @@ -1,3 +1,6 @@ +? __default__ +: type: movie + ? Movies/Fear and Loathing in Las Vegas (1998)/Fear.and.Loathing.in.Las.Vegas.720p.HDDVD.DTS.x264-ESiR.mkv : title: Fear and Loathing in Las Vegas year: 1998 diff --git a/guessit/test/series.yml b/guessit/test/series.yml index d443e89..5b753fa 100644 --- a/guessit/test/series.yml +++ b/guessit/test/series.yml @@ -1,3 +1,6 @@ +? __default__ +: type: series + ? Series/Californication/Season 2/Californication.2x05.Vaginatown.HDTV.XviD-0TV.avi : title: Californication season: 2 @@ -195,7 +198,7 @@ ? Series/My Name Is Earl/My.Name.Is.Earl.S01Extras.-.Bad.Karma.DVDRip.XviD.avi : title: My Name Is Earl season: 1 - episodeTitle: Bad Karma + episodeTitle: Extras - Bad Karma format: DVD episodeDetails: Extras videoCodec: XviD @@ -553,6 +556,7 @@ : title: Battlestar Galactica season: 0 episodeDetails: Pilot + episodeTitle: Pilot language: French format: DVD videoCodec: XviD @@ -886,6 +890,7 @@ : format: DVB releaseGroup: FlexGet title: FooBar 7 + type: movie ? FooBar.7v3.PDTV-FlexGet : options: --episode-prefer-number @@ -1376,10 +1381,14 @@ format: HDTV videoCodec: XviD releaseGroup: Etc-Group + type: movie + # Fallback to movie type because we can't tell it's a series ... ? Show.Name.Part.1.and.Part.2.Blah-Group : part: [1, 2] title: Show Name + type: movie + # Fallback to movie type because we can't tell it's a series ... ? Show Name - 01 - Ep Name : episodeNumber: 1 diff --git a/guessit/test/test_yml.py b/guessit/test/test_yml.py index e938aa1..3e28b1a 100644 --- a/guessit/test/test_yml.py +++ b/guessit/test/test_yml.py @@ -175,6 +175,13 @@ class TestYml(object): files, ids = files_and_ids(filename_predicate) + @staticmethod + def set_default(expected, default): + if default: + for k, v in default.items(): + if k not in expected: + expected[k] = v + @pytest.mark.parametrize('filename', files, ids=ids) def test(self, filename): with open(os.path.join(__location__, filename), 'r', encoding='utf-8') as infile: @@ -188,7 +195,15 @@ class TestYml(object): else: last_expected = expected + default = None + try: + default = data['__default__'] + del data['__default__'] + except KeyError: + pass + for string, expected in data.items(): + TestYml.set_default(expected, default) if not isinstance(string, six.text_type): string = six.text_type(string) if not string_predicate or string_predicate(string): # pylint: disable=not-callable