def test_rich_comparison_and_len(self): """ Test rich comparison and length """ # dicts for testing dict_0 = {} # Empty dictionary dict_1 = {1: 'Elephant'} # Single numeric key dict_2 = {1: 'Elephant', 2: 'Mouse'} # Multiple numeric keys # Construct NumDicts from dicts num_dict = NumDict() num_dict_0 = NumDict(dict_0) num_dict_1 = NumDict(dict_1) num_dict_2 = NumDict(dict_2) # Construct NumDicts from NumDicts num_dict_from_num_dict = NumDict(num_dict) num_dict_from_num_dict_0 = NumDict(num_dict_0) num_dict_from_num_dict_1 = NumDict(num_dict_1) num_dict_from_num_dict_2 = NumDict(num_dict_2) all_dicts = [ dict_0, dict_1, dict_2, num_dict, num_dict_0, num_dict_1, num_dict_2, num_dict_from_num_dict, num_dict_from_num_dict_0, num_dict_from_num_dict_1, num_dict_from_num_dict_2 ] for val_a in all_dicts: for val_b in all_dicts: self.assertEqual(val_a == val_b, len(val_a) == len(val_b))
class Overview(object): # pylint: disable=too-few-public-methods UNAIRED = UNAIRED # 1 SNATCHED = SNATCHED # 2 WANTED = WANTED # 3 GOOD = DOWNLOADED # 4 SKIPPED = SKIPPED # 5 SNATCHED_PROPER = SNATCHED_PROPER # 9 SNATCHED_BEST = SNATCHED_BEST # 1 SNATCHED_FRENCH = SNATCHED_FRENCH # 13 # Should suffice! QUAL = 50 overviewStrings = NumDict({ SKIPPED: "skipped", WANTED: "wanted", QUAL: "qual", GOOD: "good", UNAIRED: "unaired", SNATCHED: "snatched", # we can give these a different class later, otherwise # breaks checkboxes in displayShow for showing different statuses SNATCHED_BEST: "snatched", SNATCHED_PROPER: "snatched", SNATCHED_FRENCH: "snatched" })
def test_rich_comparison_and_len(self): # dicts for testing d0 = {} # Empty dictionary d1 = {1: 'Elephant'} # Single numeric key d2 = {1: 'Elephant', 2: 'Mouse'} # Multiple numeric keys # Construct NumDicts from dicts n = NumDict() n0 = NumDict(d0) n1 = NumDict(d1) n2 = NumDict(d2) # Construct NumDicts from NumDicts nn = NumDict(n) nn0 = NumDict(n0) nn1 = NumDict(n1) nn2 = NumDict(n2) all_dicts = [d0, d1, d2, n, n0, n1, n2, nn, nn0, nn1, nn2] for a in all_dicts: for b in all_dicts: self.assertEqual(a == b, len(a) == len(b))
def test_dict_access_and_mod(self): """ Test num dict access and modification """ # dicts for testing dict_0 = {} # Empty dictionary dict_1 = {1: 'Elephant'} # Single numeric key dict_2 = {1: 'Elephant', 2: 'Mouse'} # Multiple numeric keys # Construct NumDicts from dicts num_dict_0 = NumDict() num_dict_1 = NumDict(dict_1) num_dict_2 = NumDict(dict_2) # test __getitem__ self.assertEqual(num_dict_2[1], 'Elephant') with self.assertRaises(KeyError): _ = num_dict_1['Mouse'] # key is not numeric with self.assertRaises(KeyError): _ = num_dict_1.__getitem__('Mouse') # key is not numeric with self.assertRaises(KeyError): _ = num_dict_1[None] # key does not exist with self.assertRaises(KeyError): _ = num_dict_1.__getitem__(None) # key does not exist # Test __setitem__ num_dict_3 = NumDict(num_dict_2) self.assertEqual(num_dict_2, num_dict_3) num_dict_3[2] = 'Frog' self.assertNotEqual(num_dict_2, num_dict_3) # Check None keys and numeric key conversion num_dict_3['3'] = 'Armadillo' num_dict_3[None] = 'Cockroach' # Check long ints num_dict_3[12390809518259081208909880312] = 'Squid' num_dict_3['12390809518259081208909880312'] = 'Octopus' self.assertEqual(num_dict_3[12390809518259081208909880312], 'Octopus') with self.assertRaises(TypeError): num_dict_3.__setitem__('Gorilla', 1) # key is not numeric with self.assertRaises(TypeError): num_dict_3['Chimpanzee'] = 1 # key is not numeric with self.assertRaises(TypeError): num_dict_3[(4, 1)] = 1 # key is not numeric with self.assertRaises(TypeError): num_dict_3[[1, 3, 4]] = 1 # key is not numeric and is not hashable # Test __delitem__ del num_dict_3[3] del num_dict_3[None] with self.assertRaises(KeyError): del num_dict_3[3] # already deleted with self.assertRaises(KeyError): num_dict_3.__delitem__(3) # already deleted with self.assertRaises(KeyError): del num_dict_3[ 'Mouse'] # key would not exist, since it is not numeric # Test clear num_dict_3.clear() self.assertEqual(num_dict_3, {}) # Test copy() num_dict_2a = dict_2.copy() self.assertEqual(num_dict_2, num_dict_2a) num_dict_2b = num_dict_2.copy() self.assertEqual(num_dict_2b, num_dict_2) num_dict_2c = UserDict({1: 'Elephant', 2: 'Mouse'}) num_dict_2d = num_dict_2c.copy( ) # making a copy of a UserDict is special cased self.assertEqual(num_dict_2c, num_dict_2d) class MyNumDict(NumDict): """ subclass Numdict for testing """ def display(self): """ add a method to subclass to differentiate from superclass """ print('MyNumDict:', self) my_num_dict = MyNumDict(num_dict_2) my_num_dict_a = my_num_dict.copy() self.assertEqual(my_num_dict_a, my_num_dict) my_num_dict[1] = 'Frog' self.assertNotEqual(my_num_dict_a, my_num_dict) # Test keys, items, values self.assertEqual(sorted(six.iterkeys(num_dict_2)), sorted(six.iterkeys(dict_2))) self.assertEqual(sorted(six.iteritems(num_dict_2)), sorted(six.iteritems(dict_2))) self.assertEqual(sorted(six.itervalues(num_dict_2)), sorted(six.itervalues(dict_2))) # Test "in". for i in num_dict_2: self.assertIn(i, num_dict_2) self.assertEqual(i in num_dict_1, i in dict_1) self.assertEqual(i in num_dict_0, i in dict_0) self.assertFalse(None in num_dict_2) self.assertEqual(None in num_dict_2, None in dict_2) dict_2[None] = 'Cow' num_dict_2[None] = dict_2[None] self.assertTrue(None in num_dict_2) self.assertEqual(None in num_dict_2, None in dict_2) self.assertFalse('Penguin' in num_dict_2) # Test update test = NumDict() test.update(dict_2) self.assertEqual(test, num_dict_2) # Test get for i in num_dict_2: self.assertEqual(num_dict_2.get(i), num_dict_2[i]) self.assertEqual(num_dict_1.get(i), dict_1.get(i)) self.assertEqual(num_dict_0.get(i), dict_0.get(i)) for i in ['purple', None, 12312301924091284, 23]: self.assertEqual(num_dict_2.get(i), dict_2.get(i), i) with self.assertRaises(AssertionError): i = '1' self.assertEqual( num_dict_2.get(i), dict_2.get(i), i) # dict_2 expects string key which does not exist # Test "in" iteration. num_dict_2b = num_dict_2 for i in range(20): num_dict_2[i] = six.text_type(i) num_dict_2b[six.text_type(i)] = six.text_type(i) self.assertEqual(num_dict_2, num_dict_2b) ikeys = [] for k in num_dict_2: ikeys.append(k) self.assertEqual(set(ikeys), set(num_dict_2.keys())) # Test setdefault val = 1 test = NumDict() self.assertEqual(test.setdefault(val, 42), 42) self.assertEqual(test.setdefault(val, '42'), 42) self.assertNotEqual(test.setdefault(val, 42), '42') self.assertNotEqual(test.setdefault(val, '42'), '42') self.assertIn(val, test) self.assertEqual(test.setdefault(val, 23), 42) self.assertEqual(test.setdefault(val, '23'), 42) self.assertNotEqual(test.setdefault(val, 23), '42') self.assertNotEqual(test.setdefault(val, '23'), '42') self.assertIn(val, test) # Test pop val = 1 test = NumDict({val: 42}) self.assertEqual(test.pop(val), 42) self.assertRaises(KeyError, test.pop, val) self.assertEqual(test.pop(val, 1), 1) test[val] = 42 self.assertEqual(test.pop(val, 1), 42) # Test popitem val = 1 test = NumDict({val: 42}) self.assertEqual(test.popitem(), (val, 42)) self.assertRaises(KeyError, test.popitem)
def test_constructors(self): """ Test NumDict constructors """ # dicts for testing dict_0 = {} # Empty dictionary dict_1 = {1: 'Elephant'} # Single numeric key dict_2 = {1: 'Elephant', 2: 'Mouse'} # Multiple numeric keys dict_3 = {'3': 'Aardvark'} # Numeric string key dict_4 = {'3': 'Aardvark', '4': 'Ant'} # Multiple numeric string keys dict_5 = { 5: 'Cat', '6': 'Dog' } # Mixed numeric and numeric string keys dict_6 = {1: None, '2': None} # None as values dict_7 = {None: 'Empty'} # None as key # Construct NumDicts from dicts num_dict = NumDict() num_dict_0 = NumDict(dict_0) num_dict_1 = NumDict(dict_1) num_dict_2 = NumDict(dict_2) num_dict_3 = NumDict(dict_3) num_dict_4 = NumDict(dict_4) num_dict_5 = NumDict(dict_5) num_dict_6 = NumDict(dict_6) num_dict_7 = NumDict(dict_7) # Most NumDicts from dicts should compare equal... self.assertEqual(num_dict, {}) self.assertEqual(num_dict_0, dict_0) self.assertEqual(num_dict_1, dict_1) self.assertEqual(num_dict_2, dict_2) # ...however, numeric keys are not equal to numeric string keys... self.assertNotEqual(num_dict_3, dict_3) self.assertNotEqual(num_dict_4, dict_4) self.assertNotEqual(num_dict_5, dict_5) self.assertNotEqual(num_dict_6, dict_6) # ...but None keys work just fine self.assertEqual(num_dict_7, dict_7) # Construct dicts from NumDicts dict_from_num_dict = dict(num_dict) dict_from_num_dict_1 = dict(num_dict_1) dict_from_num_dict_2 = dict(num_dict_2) dict_from_num_dict_3 = dict(num_dict_3) dict_from_num_dict_4 = dict(num_dict_4) dict_from_num_dict_5 = dict(num_dict_5) dict_from_num_dict_6 = dict(num_dict_6) dict_from_num_dict_7 = dict(num_dict_7) # All dicts from NumDicts should compare equal self.assertEqual(num_dict, dict_from_num_dict) self.assertEqual(num_dict_1, dict_from_num_dict_1) self.assertEqual(num_dict_2, dict_from_num_dict_2) self.assertEqual(num_dict_3, dict_from_num_dict_3) self.assertEqual(num_dict_4, dict_from_num_dict_4) self.assertEqual(num_dict_5, dict_from_num_dict_5) self.assertEqual(num_dict_6, dict_from_num_dict_6) self.assertEqual(num_dict_7, dict_from_num_dict_7) # Construct NumDicts from NumDicts num_dict_from_num_dict = NumDict(num_dict) num_dict_from_num_dict_0 = NumDict(num_dict_0) num_dict_from_num_dict_1 = NumDict(num_dict_1) num_dict_from_num_dict_2 = NumDict(num_dict_2) num_dict_from_num_dict_3 = NumDict(num_dict_3) num_dict_from_num_dict_4 = NumDict(num_dict_4) num_dict_from_num_dict_5 = NumDict(num_dict_5) num_dict_from_num_dict_6 = NumDict(num_dict_6) num_dict_from_num_dict_7 = NumDict(num_dict_7) # All NumDicts from NumDicts should compare equal self.assertEqual(num_dict, num_dict_from_num_dict) self.assertEqual(num_dict_0, num_dict_from_num_dict_0) self.assertEqual(num_dict_1, num_dict_from_num_dict_1) self.assertEqual(num_dict_2, num_dict_from_num_dict_2) self.assertEqual(num_dict_3, num_dict_from_num_dict_3) self.assertEqual(num_dict_4, num_dict_from_num_dict_4) self.assertEqual(num_dict_5, num_dict_from_num_dict_5) self.assertEqual(num_dict_6, num_dict_from_num_dict_6) self.assertEqual(num_dict_7, num_dict_from_num_dict_7) # keyword arg constructor should fail with self.assertRaises(TypeError): NumDict( one=1, two=2) # Raise TypeError since we can't have numeric keywords # item sequence constructors work fine... self.assertEqual(NumDict([(1, 'Elephant'), (2, 'Mouse')]), dict_from_num_dict_2) self.assertEqual(NumDict(dict=[(1, 'Elephant'), (2, 'Mouse')]), dict_from_num_dict_2) self.assertEqual(NumDict([(1, 'Elephant'), ('2', 'Mouse')]), dict_from_num_dict_2) self.assertEqual(NumDict(dict=[('1', 'Elephant'), (2, 'Mouse')]), dict_from_num_dict_2) # ...unless you have a non-numeric key with self.assertRaises(TypeError): NumDict([('Rat', 11), ('Snake', 12)]) with self.assertRaises(TypeError): NumDict(dict=[('Rat', 11), ('Snake', 12)]) # combining item sequence constructors with keyword args does not work with self.assertRaises( TypeError ): # Raise TypeError since we can't have numeric keywords NumDict([(1, 'one'), (2, 'two')], two=3, five=4) # alternate constructors dict_8 = {1: 'Echo', 2: 'Echo'} self.assertEqual(NumDict.fromkeys('1 2'.split()), dict_from_num_dict_6) self.assertEqual(NumDict().fromkeys('1 2'.split()), dict_from_num_dict_6) self.assertEqual(NumDict.fromkeys('1 2'.split(), 'Echo'), dict_8) self.assertEqual(NumDict().fromkeys('1 2'.split(), 'Echo'), dict_8) self.assertTrue(num_dict_1.fromkeys('1 2'.split()) is not num_dict_1) self.assertIsInstance(num_dict_1.fromkeys('1 2'.split()), NumDict) self.assertIsInstance(num_dict_2.fromkeys('1 2'.split()), NumDict) self.assertIsInstance(num_dict_3.fromkeys('1 2'.split()), NumDict) self.assertIsInstance(num_dict_4.fromkeys('1 2'.split()), NumDict)
def test_repr(self): """ Test representation of NumDicts """ # dicts for testing dict_0 = {} # Empty dictionary dict_1 = {1: 'Elephant'} # Single numeric key dict_2 = {1: 'Elephant', 2: 'Mouse'} # Multiple numeric keys dict_3 = {'3': 'Aardvark'} # Numeric string key dict_4 = {'3': 'Aardvark', '4': 'Ant'} # Multiple numeric string keys dict_5 = { 5: 'Cat', '6': 'Dog' } # Mixed numeric and numeric string keys dict_6 = {1: None, '2': None} # None as values dict_7 = {None: 'Empty'} # None as key # Construct NumDicts from dicts num_dict = NumDict() num_dict_0 = NumDict(dict_0) num_dict_1 = NumDict(dict_1) num_dict_2 = NumDict(dict_2) num_dict_3 = NumDict(dict_3) num_dict_4 = NumDict(dict_4) num_dict_5 = NumDict(dict_5) num_dict_6 = NumDict(dict_6) num_dict_7 = NumDict(dict_7) reps = ( "{}", "{1: u'Elephant'}", "{1: u'Elephant', 2: u'Mouse'}", "'3': u'Aardvark'", "{'3': u'Aardvark', '4': u'Ant'}", "{5: u'Cat', '6': u'Dog'}", "{1: None, '2': None}", "{None: u'Empty'}", ) # Most representations of NumDicts should compare equal to dicts... self.assertEqual(six.text_type(num_dict), six.text_type({})) self.assertEqual(repr(num_dict), repr({})) self.assertIn(repr(num_dict), reps) self.assertEqual(six.text_type(num_dict_0), six.text_type(dict_0)) self.assertEqual(repr(num_dict_0), repr(dict_0)) self.assertIn(repr(num_dict_0), reps) self.assertEqual(six.text_type(num_dict_1), six.text_type(dict_1)) self.assertEqual(repr(num_dict_1), repr(dict_1)) self.assertIn(repr(num_dict_1), reps) self.assertEqual(six.text_type(num_dict_2), six.text_type(dict_2)) self.assertEqual(repr(num_dict_2), repr(dict_2)) self.assertIn(repr(num_dict_2), reps) # ...however, numeric keys are not equal to numeric string keys... # ...so the string representations for those are different... self.assertNotEqual(six.text_type(num_dict_3), six.text_type(dict_3)) self.assertNotEqual(repr(num_dict_3), repr(dict_3)) self.assertNotIn(repr(num_dict_3), reps) self.assertNotEqual(six.text_type(num_dict_4), six.text_type(dict_4)) self.assertNotEqual(repr(num_dict_4), repr(dict_4)) self.assertNotIn(repr(num_dict_4), reps) self.assertNotEqual(six.text_type(num_dict_5), six.text_type(dict_5)) self.assertNotEqual(repr(num_dict_5), repr(dict_5)) self.assertNotIn(repr(num_dict_5), reps) self.assertNotEqual(six.text_type(num_dict_6), six.text_type(dict_6)) self.assertNotEqual(repr(num_dict_6), repr(dict_6)) self.assertNotIn(repr(num_dict_6), reps) # ...but None keys work just fine self.assertEqual(six.text_type(num_dict_7), six.text_type(dict_7)) self.assertEqual(repr(num_dict_7), repr(dict_7)) self.assertIn(repr(num_dict_7), reps)
class Quality(object): """ Determine quality and set status codes """ NONE = 0 # 0 SDTV = 1 # 1 SDDVD = 1 << 1 # 2 HDTV = 1 << 2 # 4 RAWHDTV = 1 << 3 # 8 -- 720p/1080i mpeg2 (trollhd releases) FULLHDTV = 1 << 4 # 16 -- 1080p HDTV (QCF releases) HDWEBDL = 1 << 5 # 32 FULLHDWEBDL = 1 << 6 # 64 -- 1080p web-dl HDBLURAY = 1 << 7 # 128 FULLHDBLURAY = 1 << 8 # 256 UHD_4K_TV = 1 << 9 # 512 -- 2160p aka 4K UHD aka UHD-1 UHD_4K_WEBDL = 1 << 10 # 1024 UHD_4K_BLURAY = 1 << 11 # 2048 UHD_8K_TV = 1 << 12 # 4096 -- 4320p aka 8K UHD aka UHD-2 UHD_8K_WEBDL = 1 << 13 # 8192 UHD_8K_BLURAY = 1 << 14 # 16384 ANYHDTV = HDTV | FULLHDTV # 20 ANYWEBDL = HDWEBDL | FULLHDWEBDL # 96 ANYBLURAY = HDBLURAY | FULLHDBLURAY # 384 # put these bits at the other end of the spectrum, far enough out that they shouldn't interfere UNKNOWN = 1 << 15 # 32768 qualityStrings = NumDict({ None: "None", NONE: "N/A", UNKNOWN: "Unknown", SDTV: "SDTV", SDDVD: "SD DVD", HDTV: "720p HDTV", RAWHDTV: "RawHD", FULLHDTV: "1080p HDTV", HDWEBDL: "720p WEB-DL", FULLHDWEBDL: "1080p WEB-DL", HDBLURAY: "720p BluRay", FULLHDBLURAY: "1080p BluRay", UHD_4K_TV: "4K UHD TV", UHD_8K_TV: "8K UHD TV", UHD_4K_WEBDL: "4K UHD WEB-DL", UHD_8K_WEBDL: "8K UHD WEB-DL", UHD_4K_BLURAY: "4K UHD BluRay", UHD_8K_BLURAY: "8K UHD BluRay", }) sceneQualityStrings = NumDict({ None: "None", NONE: "N/A", UNKNOWN: "Unknown", SDTV: "HDTV", SDDVD: "", HDTV: "720p HDTV", RAWHDTV: "1080i HDTV", FULLHDTV: "1080p HDTV", HDWEBDL: "720p WEB-DL", FULLHDWEBDL: "1080p WEB-DL", HDBLURAY: "720p BluRay", FULLHDBLURAY: "1080p BluRay", UHD_4K_TV: "4K UHD TV", UHD_8K_TV: "8K UHD TV", UHD_4K_WEBDL: "4K UHD WEB-DL", UHD_8K_WEBDL: "8K UHD WEB-DL", UHD_4K_BLURAY: "4K UHD BluRay", UHD_8K_BLURAY: "8K UHD BluRay", }) combinedQualityStrings = NumDict({ ANYHDTV: "HDTV", ANYWEBDL: "WEB-DL", ANYBLURAY: "BluRay" }) cssClassStrings = NumDict({ None: "None", NONE: "N/A", UNKNOWN: "Unknown", SDTV: "SDTV", SDDVD: "SDDVD", HDTV: "HD720p", RAWHDTV: "RawHD", FULLHDTV: "HD1080p", HDWEBDL: "HD720p", FULLHDWEBDL: "HD1080p", HDBLURAY: "HD720p", FULLHDBLURAY: "HD1080p", UHD_4K_TV: "UHD-4K", UHD_8K_TV: "UHD-8K", UHD_4K_WEBDL: "UHD-4K", UHD_8K_WEBDL: "UHD-8K", UHD_4K_BLURAY: "UHD-4K", UHD_8K_BLURAY: "UHD-8K", ANYHDTV: "any-hd", ANYWEBDL: "any-hd", ANYBLURAY: "any-hd" }) statusPrefixes = NumDict({ DOWNLOADED: _("Downloaded"), SNATCHED: _("Snatched"), SNATCHED_PROPER: _("Snatched (Proper)"), FAILED: _("Failed"), SNATCHED_BEST: _("Snatched (Best)"), ARCHIVED: _("Archived") }) @staticmethod def _getStatusStrings(status): """ Returns string values associated with Status prefix :param status: Status prefix to resolve :return: Human readable status value """ to_return = {} for quality in Quality.qualityStrings: if quality is not None: stat = Quality.statusPrefixes[status] qual = Quality.qualityStrings[quality] comp = Quality.compositeStatus(status, quality) to_return[comp] = '{0} ({1})'.format(stat, qual) return to_return @staticmethod def combineQualities(allowed_qualities, preferred_qualities): any_quality = 0 best_quality = 0 if allowed_qualities: any_quality = reduce(operator.or_, allowed_qualities) if preferred_qualities: best_quality = reduce(operator.or_, preferred_qualities) return any_quality | (best_quality << 16) @staticmethod def splitQuality(quality): if quality is None: quality = Quality.NONE allowed_qualities = [] preferred_qualities = [] for cur_qual in Quality.qualityStrings: if cur_qual is None: cur_qual = Quality.NONE if cur_qual & quality: allowed_qualities.append(cur_qual) if cur_qual << 16 & quality: preferred_qualities.append(cur_qual) return sorted(allowed_qualities), sorted(preferred_qualities) @staticmethod def nameQuality(name, anime=False): """ Return The quality from an episode File renamed by SickRage If no quality is achieved it will try scene_quality regex :param name: to parse :param anime: Boolean to indicate if the show we're resolving is Anime :return: Quality prefix """ # Try Scene names first quality = Quality.scene_quality(name, anime) if quality != Quality.UNKNOWN: return quality quality = Quality.qualityFromFileMeta(name) if quality != Quality.UNKNOWN: return quality if name.lower().endswith(".ts"): return Quality.RAWHDTV else: return Quality.UNKNOWN @staticmethod def scene_quality(name, anime=False): # pylint: disable=too-many-branches, too-many-statements """ Return The quality from the scene episode File :param name: Episode filename to analyse :param anime: Boolean to indicate if the show we're resolving is Anime :return: Quality """ if not name: return Quality.UNKNOWN name = ek(path.basename, name) result = None ep = EpisodeTags(name) if anime: sd_options = tags.anime_sd.search(name) hd_options = tags.anime_hd.search(name) full_hd = tags.anime_fullhd.search(name) ep.rex[b'bluray'] = tags.anime_bluray # BluRay if ep.bluray and (full_hd or hd_options): result = Quality.FULLHDBLURAY if full_hd else Quality.HDBLURAY # HD TV elif not ep.bluray and (full_hd or hd_options): result = Quality.FULLHDTV if full_hd else Quality.HDTV # SD DVD elif ep.dvd: result = Quality.SDDVD # SD TV elif sd_options: result = Quality.SDTV return Quality.UNKNOWN if result is None else result # Is it UHD? if ep.vres in [2160, 4320] and ep.scan == 'p': # BluRay full_res = (ep.vres == 4320) if ep.avc and ep.bluray: result = Quality.UHD_4K_BLURAY if not full_res else Quality.UHD_8K_BLURAY # WEB-DL elif (ep.avc and ep.itunes) or ep.web: result = Quality.UHD_4K_WEBDL if not full_res else Quality.UHD_8K_WEBDL # HDTV elif ep.avc and ep.tv == 'hd': result = Quality.UHD_4K_TV if not full_res else Quality.UHD_8K_TV # Is it HD? elif ep.vres in [1080, 720]: if ep.scan == 'p': # BluRay full_res = (ep.vres == 1080) if ep.avc and (ep.bluray or ep.hddvd): result = Quality.FULLHDBLURAY if full_res else Quality.HDBLURAY # WEB-DL elif (ep.avc and ep.itunes) or ep.web: result = Quality.FULLHDWEBDL if full_res else Quality.HDWEBDL # HDTV elif ep.avc and ep.tv == 'hd': result = Quality.FULLHDTV if full_res else Quality.HDTV #1080 HDTV h264 # MPEG2 encoded elif all([ep.vres == 1080, ep.tv == 'hd', ep.mpeg]): result = Quality.RAWHDTV elif all([ep.vres == 720, ep.tv == 'hd', ep.mpeg]): result = Quality.RAWHDTV elif (ep.res == '1080i') and ep.tv == 'hd' and ( ep.mpeg or (ep.raw and ep.avc_non_free)): result = Quality.RAWHDTV elif ep.hrws: result = Quality.HDTV # Is it SD? elif ep.xvid or ep.avc: # SD DVD if ep.dvd or ep.bluray: result = Quality.SDDVD # SDTV elif ep.res == '480p' or any([ep.tv, ep.sat, ep.web]): result = Quality.SDTV elif ep.dvd: # SD DVD result = Quality.SDDVD elif ep.tv: # SD TV/HD TV result = Quality.SDTV return Quality.UNKNOWN if result is None else result @staticmethod def qualityFromFileMeta(filename): # pylint: disable=too-many-branches """ Get quality file file metadata :param filename: Filename to analyse :return: Quality prefix """ height = video_screen_size(filename)[1] if not height: return Quality.UNKNOWN base_filename = ek(path.basename, filename) bluray = re.search(r"blue?-?ray|hddvd|b[rd](rip|mux)", base_filename, re.I) is not None webdl = re.search(r"web.?dl|web(rip|mux|hd)", base_filename, re.I) is not None ret = Quality.UNKNOWN if 3240 < height: ret = ((Quality.UHD_8K_TV, Quality.UHD_8K_BLURAY)[bluray], Quality.UHD_8K_WEBDL)[webdl] if 1620 < height <= 3240: ret = ((Quality.UHD_4K_TV, Quality.UHD_4K_BLURAY)[bluray], Quality.UHD_4K_WEBDL)[webdl] elif 800 < height <= 1620: ret = ((Quality.FULLHDTV, Quality.FULLHDBLURAY)[bluray], Quality.FULLHDWEBDL)[webdl] elif 680 < height <= 800: ret = ((Quality.HDTV, Quality.HDBLURAY)[bluray], Quality.HDWEBDL)[webdl] elif height <= 680: ret = (Quality.SDTV, Quality.SDDVD)[re.search( r'dvd|b[rd]rip|blue?-?ray', base_filename, re.I) is not None] return ret @staticmethod def compositeStatus(status, quality): if quality is None: quality = Quality.NONE return status + 100 * quality @staticmethod def qualityDownloaded(status): return (status - DOWNLOADED) / 100 @staticmethod def splitCompositeStatus(status): """ Split a composite status code into a status and quality. :param status: to split :returns: a tuple containing (status, quality) """ status = long(status) if status == UNKNOWN: return UNKNOWN, Quality.UNKNOWN for q in sorted(Quality.qualityStrings.keys(), reverse=True): if status > q * 100: return status - q * 100, q return status, Quality.NONE @staticmethod def sceneQualityFromName(name, quality): # pylint: disable=too-many-branches """ Get scene naming parameters from filename and quality :param name: Filename to check :param quality: int of quality to make sure we get the right rip type :return: encoder type for scene quality naming """ codec_list = ['xvid', 'divx'] x264_list = ['x264', 'x 264', 'x.264'] h264_list = ['h264', 'h 264', 'h.264', 'avc'] x265_list = ['x265', 'x 265', 'x.265'] h265_list = ['h265', 'h 265', 'h.265', 'hevc'] codec_list += x264_list + h264_list + x265_list + h265_list found_codecs = {} found_codec = None for codec in codec_list: if codec in name.lower(): found_codecs[name.lower().rfind(codec)] = codec if found_codecs: sorted_codecs = sorted(found_codecs, reverse=True) found_codec = found_codecs[list(sorted_codecs)[0]] # 2 corresponds to SDDVD quality if quality == 2: if re.search(r"b(r|d|rd)?(-| |\.)?(rip|mux)", name.lower()): rip_type = " BDRip" elif re.search(r"(dvd)(-| |\.)?(rip|mux)?", name.lower()): rip_type = " DVDRip" else: rip_type = "" if found_codec: if codec_list[0] in found_codec: found_codec = 'XviD' elif codec_list[1] in found_codec: found_codec = 'DivX' elif found_codec in x264_list: found_codec = x264_list[0] elif found_codec in h264_list: found_codec = h264_list[0] elif found_codec in x265_list: found_codec = x265_list[0] elif found_codec in h265_list: found_codec = h265_list[0] if quality == 2: return rip_type + " " + found_codec else: return " " + found_codec elif quality == 2: return rip_type else: return "" @staticmethod def statusFromName(name, anime=False): """ Get a status object from filename :param name: Filename to check :param anime: boolean to enable anime parsing :return: Composite status/quality object """ return Quality.compositeStatus(DOWNLOADED, Quality.nameQuality(name, anime)) DOWNLOADED = None SNATCHED = None SNATCHED_PROPER = None FAILED = None SNATCHED_BEST = None ARCHIVED = None
NOTIFY_DOWNLOAD = 2 NOTIFY_SUBTITLE_DOWNLOAD = 3 NOTIFY_GIT_UPDATE = 4 NOTIFY_GIT_UPDATE_TEXT = 5 NOTIFY_LOGIN = 6 NOTIFY_LOGIN_TEXT = 7 notifyStrings = NumDict({ # pylint: disable=undefined-variable NOTIFY_SNATCH: _("Started Download"), NOTIFY_DOWNLOAD: _("Download Finished"), NOTIFY_SUBTITLE_DOWNLOAD: _("Subtitle Download Finished"), NOTIFY_GIT_UPDATE: _("SickRage Updated"), NOTIFY_GIT_UPDATE_TEXT: _("SickRage Updated To Commit#: "), NOTIFY_LOGIN: _("SickRage new login"), NOTIFY_LOGIN_TEXT: _("New login from IP: {0}. http://geomaplookup.net/?ip={0}") }) # Episode statuses UNKNOWN = -1 # should never happen UNAIRED = 1 # episodes that haven't aired yet SNATCHED = 2 # qualified with quality WANTED = 3 # episodes we don't have but want to get DOWNLOADED = 4 # qualified with quality
class Quality(object): """ Determine quality and set status codes """ NONE = 0 # 0 SDTV = 1 # 1 SDDVD = 1 << 1 # 2 HDTV = 1 << 2 # 4 RAWHDTV = 1 << 3 # 8 -- 720p/1080i mpeg2 (trollhd releases) FULLHDTV = 1 << 4 # 16 -- 1080p HDTV (QCF releases) HDWEBDL = 1 << 5 # 32 FULLHDWEBDL = 1 << 6 # 64 -- 1080p web-dl HDBLURAY = 1 << 7 # 128 FULLHDBLURAY = 1 << 8 # 256 UHD_4K_TV = 1 << 9 # 512 -- 2160p aka 4K UHD aka UHD-1 UHD_4K_WEBDL = 1 << 10 # 1024 UHD_4K_BLURAY = 1 << 11 # 2048 UHD_8K_TV = 1 << 12 # 4096 -- 4320p aka 8K UHD aka UHD-2 UHD_8K_WEBDL = 1 << 13 # 8192 UHD_8K_BLURAY = 1 << 14 # 16384 ANYHDTV = HDTV | FULLHDTV # 20 ANYWEBDL = HDWEBDL | FULLHDWEBDL # 96 ANYBLURAY = HDBLURAY | FULLHDBLURAY # 384 # put these bits at the other end of the spectrum, far enough out that they shouldn't interfere UNKNOWN = 1 << 15 # 32768 qualityStrings = NumDict({ None: "None", NONE: "N/A", UNKNOWN: "Unknown", SDTV: "SDTV", SDDVD: "SD DVD", HDTV: "720p HDTV", RAWHDTV: "RawHD", FULLHDTV: "1080p HDTV", HDWEBDL: "720p WEB-DL", FULLHDWEBDL: "1080p WEB-DL", HDBLURAY: "720p BluRay", FULLHDBLURAY: "1080p BluRay", UHD_4K_TV: "4K UHD TV", UHD_8K_TV: "8K UHD TV", UHD_4K_WEBDL: "4K UHD WEB-DL", UHD_8K_WEBDL: "8K UHD WEB-DL", UHD_4K_BLURAY: "4K UHD BluRay", UHD_8K_BLURAY: "8K UHD BluRay", }) sceneQualityStrings = NumDict({ None: "None", NONE: "N/A", UNKNOWN: "Unknown", SDTV: "HDTV", SDDVD: "", HDTV: "720p HDTV", RAWHDTV: "1080i HDTV", FULLHDTV: "1080p HDTV", HDWEBDL: "720p WEB-DL", FULLHDWEBDL: "1080p WEB-DL", HDBLURAY: "720p BluRay", FULLHDBLURAY: "1080p BluRay", UHD_4K_TV: "4K UHD TV", UHD_8K_TV: "8K UHD TV", UHD_4K_WEBDL: "4K UHD WEB-DL", UHD_8K_WEBDL: "8K UHD WEB-DL", UHD_4K_BLURAY: "4K UHD BluRay", UHD_8K_BLURAY: "8K UHD BluRay", }) combinedQualityStrings = NumDict({ ANYHDTV: "HDTV", ANYWEBDL: "WEB-DL", ANYBLURAY: "BluRay" }) cssClassStrings = NumDict({ None: "None", NONE: "N/A", UNKNOWN: "Unknown", SDTV: "SDTV", SDDVD: "SDDVD", HDTV: "HD720p", RAWHDTV: "RawHD", FULLHDTV: "HD1080p", HDWEBDL: "HD720p", FULLHDWEBDL: "HD1080p", HDBLURAY: "HD720p", FULLHDBLURAY: "HD1080p", UHD_4K_TV: "UHD-4K", UHD_8K_TV: "UHD-8K", UHD_4K_WEBDL: "UHD-4K", UHD_8K_WEBDL: "UHD-8K", UHD_4K_BLURAY: "UHD-4K", UHD_8K_BLURAY: "UHD-8K", ANYHDTV: "any-hd", ANYWEBDL: "any-hd", ANYBLURAY: "any-hd" }) statusPrefixes = NumDict({ DOWNLOADED: "Downloaded", SNATCHED: "Snatched", SNATCHED_PROPER: "Snatched (Proper)", FAILED: "Failed", SNATCHED_BEST: "Snatched (Best)", ARCHIVED: "Archived" }) @staticmethod def _getStatusStrings(status): """ Returns string values associated with Status prefix :param status: Status prefix to resolve :return: Human readable status value """ to_return = {} for quality in Quality.qualityStrings: if quality is not None: stat = Quality.statusPrefixes[status] qual = Quality.qualityStrings[quality] comp = Quality.compositeStatus(status, quality) to_return[comp] = '%s (%s)' % (stat, qual) return to_return @staticmethod def combineQualities(allowed_qualities, preferred_qualities): any_quality = 0 best_quality = 0 if allowed_qualities: any_quality = reduce(operator.or_, allowed_qualities) if preferred_qualities: best_quality = reduce(operator.or_, preferred_qualities) return any_quality | (best_quality << 16) @staticmethod def splitQuality(quality): if quality is None: quality = Quality.NONE allowed_qualities = [] preferred_qualities = [] for cur_qual in Quality.qualityStrings: if cur_qual is None: cur_qual = Quality.NONE if cur_qual & quality: allowed_qualities.append(cur_qual) if cur_qual << 16 & quality: preferred_qualities.append(cur_qual) return sorted(allowed_qualities), sorted(preferred_qualities) @staticmethod def nameQuality(name, anime=False): """ Return The quality from an episode File renamed by SickRage If no quality is achieved it will try sceneQuality regex :param name: to parse :param anime: Boolean to indicate if the show we're resolving is Anime :return: Quality prefix """ # Try Scene names first quality = Quality.sceneQuality(name, anime) if quality != Quality.UNKNOWN: return quality quality = Quality.assumeQuality(name) if quality != Quality.UNKNOWN: return quality return Quality.UNKNOWN @staticmethod # TODO: Remove this method and sceneQuality after the new scene_quality has been validated. def old_scene_quality(name, anime=False): # pylint: disable=too-many-branches """ Return The quality from the scene episode File :param name: Episode filename to analyse :param anime: Boolean to indicate if the show we're resolving is Anime :return: Quality prefix """ ret = Quality.UNKNOWN if not name: return ret name = ek(path.basename, name) check_name = lambda regex_list, func: func( [re.search(regex, name, re.I) for regex in regex_list]) if anime: dvd_options = check_name([r"dvd", r"dvdrip"], any) bluray_options = check_name([r"BD", r"blue?-?ray"], any) sd_options = check_name([r"360p", r"480p", r"848x480", r"XviD"], any) hd_options = check_name([r"720p", r"1280x720", r"960x720"], any) full_hd = check_name([r"1080p", r"1920x1080"], any) if sd_options and not bluray_options and not dvd_options: ret = Quality.SDTV elif dvd_options: ret = Quality.SDDVD elif hd_options and not bluray_options and not full_hd: ret = Quality.HDTV elif full_hd and not bluray_options and not hd_options: ret = Quality.FULLHDTV elif hd_options and not bluray_options and not full_hd: ret = Quality.HDWEBDL elif bluray_options and hd_options and not full_hd: ret = Quality.HDBLURAY elif bluray_options and full_hd and not hd_options: ret = Quality.FULLHDBLURAY return ret if check_name([ r"480p|web.?dl|web(rip|mux|hd)|[sph]d.?tv|dsr|tv(rip|mux)|satrip", r"xvid|divx|[xh].?26[45]" ], all) and not check_name( [r"(720|1080)[pi]"], all) and not check_name([ r"hr.ws.pdtv.[xh].?26[45]", r"dvd(rip|mux)|b[rd](rip|mux)|blue?-?ray" ], any): ret = Quality.SDTV elif check_name([ r"dvd(rip|mux)|b[rd](rip|mux)|blue?-?ray", r"xvid|divx|[xh].?26[45]" ], all) and not check_name([r"(720|1080)[pi]"], all) and not check_name( [r"hr.ws.pdtv.[xh].?26[45]"], any): ret = Quality.SDDVD elif check_name( [r"720p", r"hd.?tv", r"[xh].?26[45]"], all) or check_name([r"hr.ws.pdtv.[xh].?26[45]"], any) and not check_name([r"1080[pi]"], all): ret = Quality.HDTV elif check_name([r"720p|1080i", r"hd.?tv", r"mpeg-?2"], all) or check_name([r"1080[pi].hdtv", r"h.?26[45]"], all): ret = Quality.RAWHDTV elif check_name([r"1080p", r"hd.?tv", r"[xh].?26[45]"], all): ret = Quality.FULLHDTV elif check_name([r"720p", r"web.?dl|web(rip|mux|hd)"], all) or check_name( [r"720p", r"itunes", r"[xh].?26[45]"], all): ret = Quality.HDWEBDL elif check_name([r"1080p", r"web.?dl|web(rip|mux|hd)"], all) or check_name( [r"1080p", r"itunes", r"[xh].?26[45]"], all): ret = Quality.FULLHDWEBDL elif check_name( [r"720p", r"blue?-?ray|hddvd|b[rd](rip|mux)", r"[xh].?26[45]"], all): ret = Quality.HDBLURAY elif check_name( [r"1080p", r"blue?-?ray|hddvd|b[rd](rip|mux)", r"[xh].?26[45]"], all): ret = Quality.FULLHDBLURAY return ret @staticmethod def scene_quality(name, anime=False): # pylint: disable=too-many-branches """ Return The quality from the scene episode File :param name: Episode filename to analyse :param anime: Boolean to indicate if the show we're resolving is Anime :return: Quality """ if not name: return Quality.UNKNOWN else: name = ek(path.basename, name) result = None ep = EpisodeTags(name) if anime: sd_options = tags.anime_sd.search(name) hd_options = tags.anime_hd.search(name) full_hd = tags.anime_fullhd.search(name) ep.rex[u'bluray'] = tags.anime_bluray # BluRay if ep.bluray and (full_hd or hd_options): result = Quality.FULLHDBLURAY if full_hd else Quality.HDBLURAY # HD TV elif not ep.bluray and (full_hd or hd_options): result = Quality.FULLHDTV if full_hd else Quality.HDTV # SD DVD elif ep.dvd: result = Quality.SDDVD # SD TV elif sd_options: result = Quality.SDTV return Quality.UNKNOWN if result is None else result # Is it UHD? if ep.vres in [2160, 4320] and ep.scan == u'p': # BluRay full_res = (ep.vres == 4320) if ep.avc and ep.bluray: result = Quality.UHD_4K_BLURAY if not full_res else Quality.UHD_8K_BLURAY # WEB-DL elif (ep.avc and ep.itunes) or ep.web: result = Quality.UHD_4K_WEBDL if not full_res else Quality.UHD_8K_WEBDL # HDTV elif ep.avc and ep.tv == u'hd': result = Quality.UHD_4K_TV if not full_res else Quality.UHD_8K_TV # Is it HD? elif ep.vres in [1080, 720]: if ep.scan == u'p': # BluRay full_res = (ep.vres == 1080) if ep.avc and (ep.bluray or ep.hddvd): result = Quality.FULLHDBLURAY if full_res else Quality.HDBLURAY # WEB-DL elif (ep.avc and ep.itunes) or ep.web: result = Quality.FULLHDWEBDL if full_res else Quality.HDWEBDL # HDTV elif ep.avc and ep.tv == u'hd': if not all([ep.vres == 1080, ep.raw, ep.avc_non_free]): result = Quality.FULLHDTV if full_res else Quality.HDTV else: result = Quality.RAWHDTV elif all([ep.vres == 720, ep.tv == u'hd', ep.mpeg]): result = Quality.RAWHDTV elif (ep.res == u'1080i') and ep.tv == u'hd': if ep.mpeg or (ep.raw and ep.avc_non_free): result = Quality.RAWHDTV elif ep.hrws: result = Quality.HDTV # Is it SD? elif ep.xvid or ep.avc: # SD DVD if ep.dvd or ep.bluray: result = Quality.SDDVD # SDTV elif ep.res == u'480p' or any([ep.tv, ep.sat, ep.web]): result = Quality.SDTV return Quality.UNKNOWN if result is None else result def scene_french(name, anime=False): # pylint: disable=too-many-branches """ Return The quality from the scene episode File :param name: Episode filename to analyse :param anime: Boolean to indicate if the show we're resolving is Anime :return: Quality """ if not name: return "" else: name = ek(path.basename, name) result = None ep = Episode_fr(name) return ep @staticmethod # TODO: Remove this method and old_scene_quality after the new scene_quality has been validated. def sceneQuality(name, anime=False): """ Validation for new scene_quality. :param name: Episode filename to analyse :param anime: Boolean to indicate if the show we're resolving is Anime :return: Quality """ # use the new scene_quality to determine quality result = Quality.scene_quality(name, anime) # if its a quality known by old_scene_quality assert they match if result <= Quality.FULLHDBLURAY or result == Quality.UNKNOWN: old = Quality.old_scene_quality(name, anime) assert old == result, 'Old quality does not match new: %s != %s : %s' % ( Quality.qualityStrings[old], Quality.qualityStrings[result], name) return result @staticmethod def assumeQuality(name): """ Assume a quality from file extension if we cannot resolve it otherwise :param name: File name of episode to analyse :return: Quality prefix """ quality = Quality.qualityFromFileMeta(name) if quality != Quality.UNKNOWN: return quality if name.lower().endswith(".ts"): return Quality.RAWHDTV else: return Quality.UNKNOWN @staticmethod def qualityFromFileMeta(filename): # pylint: disable=too-many-branches """ Get quality file file metadata :param filename: Filename to analyse :return: Quality prefix """ log.use_print = False try: parser = createParser(filename) except Exception: # pylint: disable=broad-except parser = None if not parser: return Quality.UNKNOWN try: metadata = extractMetadata(parser) except Exception: # pylint: disable=broad-except metadata = None try: parser.stream._input.close() # pylint: disable=protected-access except Exception: # pylint: disable=broad-except pass if not metadata: return Quality.UNKNOWN height = 0 if metadata.has('height'): height = int(metadata.get('height') or 0) else: test = getattr(metadata, "iterGroups", None) if callable(test): for metagroup in metadata.iterGroups(): if metagroup.has('height'): height = int(metagroup.get('height') or 0) if not height: return Quality.UNKNOWN base_filename = ek(path.basename, filename) bluray = re.search(r"blue?-?ray|hddvd|b[rd](rip|mux)", base_filename, re.I) is not None webdl = re.search(r"web.?dl|web(rip|mux|hd)", base_filename, re.I) is not None ret = Quality.UNKNOWN if 3240 < height: ret = ((Quality.UHD_8K_TV, Quality.UHD_8K_BLURAY)[bluray], Quality.UHD_8K_WEBDL)[webdl] if 1620 < height <= 3240: ret = ((Quality.UHD_4K_TV, Quality.UHD_4K_BLURAY)[bluray], Quality.UHD_4K_WEBDL)[webdl] elif 800 < height <= 1620: ret = ((Quality.FULLHDTV, Quality.FULLHDBLURAY)[bluray], Quality.FULLHDWEBDL)[webdl] elif 680 < height <= 800: ret = ((Quality.HDTV, Quality.HDBLURAY)[bluray], Quality.HDWEBDL)[webdl] elif height <= 680: ret = (Quality.SDTV, Quality.SDDVD)[re.search( r'dvd|b[rd]rip|blue?-?ray', base_filename, re.I) is not None] return ret @staticmethod def compositeStatus(status, quality): if quality is None: quality = Quality.NONE return status + 100 * quality @staticmethod def qualityDownloaded(status): return (status - DOWNLOADED) / 100 @staticmethod def splitCompositeStatus(status): """ Split a composite status code into a status and quality. :param status: to split :returns: a tuple containing (status, quality) """ status = long(status) if status == UNKNOWN: return UNKNOWN, Quality.UNKNOWN for q in sorted(Quality.qualityStrings.keys(), reverse=True): if status > q * 100: return status - q * 100, q return status, Quality.NONE @staticmethod def sceneQualityFromName(name, quality): # pylint: disable=too-many-branches """ Get scene naming parameters from filename and quality :param name: Filename to check :param quality: int of quality to make sure we get the right rip type :return: encoder type for scene quality naming """ codec_list = ['xvid', 'divx'] x264_list = ['x264', 'x 264', 'x.264'] h264_list = ['h264', 'h 264', 'h.264', 'avc'] x265_list = ['x265', 'x 265', 'x.265'] h265_list = ['h265', 'h 265', 'h.265', 'hevc'] codec_list += x264_list + h264_list + x265_list + h265_list found_codecs = {} found_codec = None for codec in codec_list: if codec in name.lower(): found_codecs[name.lower().rfind(codec)] = codec if found_codecs: sorted_codecs = sorted(found_codecs, reverse=True) found_codec = found_codecs[list(sorted_codecs)[0]] # 2 corresponds to SDDVD quality if quality == 2: if re.search(r"b(r|d|rd)?(-| |\.)?(rip|mux)", name.lower()): rip_type = " BDRip" elif re.search(r"(dvd)(-| |\.)?(rip|mux)?", name.lower()): rip_type = " DVDRip" else: rip_type = "" if found_codec: if codec_list[0] in found_codec: found_codec = 'XviD' elif codec_list[1] in found_codec: found_codec = 'DivX' elif found_codec in x264_list: found_codec = x264_list[0] elif found_codec in h264_list: found_codec = h264_list[0] elif found_codec in x265_list: found_codec = x265_list[0] elif found_codec in h265_list: found_codec = h265_list[0] if quality == 2: return rip_type + " " + found_codec else: return " " + found_codec elif quality == 2: return rip_type else: return "" @staticmethod def statusFromName(name, assume=True, anime=False): """ Get a status object from filename :param name: Filename to check :param assume: boolean to assume quality by extension if we can't figure it out :param anime: boolean to enable anime parsing :return: Composite status/quality object """ quality = Quality.nameQuality(name, anime) if assume and quality == Quality.UNKNOWN: quality = Quality.assumeQuality(name) return Quality.compositeStatus(DOWNLOADED, quality) DOWNLOADED = None SNATCHED = None SNATCHED_PROPER = None FAILED = None SNATCHED_BEST = None ARCHIVED = None SNATCHED_FRENCH = None
NOTIFY_SNATCH = 1 NOTIFY_DOWNLOAD = 2 NOTIFY_SUBTITLE_DOWNLOAD = 3 NOTIFY_GIT_UPDATE = 4 NOTIFY_GIT_UPDATE_TEXT = 5 NOTIFY_LOGIN = 6 NOTIFY_LOGIN_TEXT = 7 notifyStrings = NumDict({ NOTIFY_SNATCH: "Started Download", NOTIFY_DOWNLOAD: "Download Finished", NOTIFY_SUBTITLE_DOWNLOAD: "Subtitle Download Finished", NOTIFY_GIT_UPDATE: "SickRage Updated", NOTIFY_GIT_UPDATE_TEXT: "SickRage Updated To Commit#: ", NOTIFY_LOGIN: "******", NOTIFY_LOGIN_TEXT: "New login from IP: {0}. http://geomaplookup.net/?ip={0}" }) # Episode statuses UNKNOWN = -1 # should never happen UNAIRED = 1 # episodes that haven't aired yet SNATCHED = 2 # qualified with quality WANTED = 3 # episodes we don't have but want to get DOWNLOADED = 4 # qualified with quality SKIPPED = 5 # episodes we don't want
def test_constructors(self): """ Test NumDict constructors """ # dicts for testing d0 = {} # Empty dictionary d1 = {1: 'Elephant'} # Single numeric key d2 = {1: 'Elephant', 2: 'Mouse'} # Multiple numeric keys d3 = {'3': 'Aardvark'} # Numeric string key d4 = {'3': 'Aardvark', '4': 'Ant'} # Multiple numeric string keys d5 = {5: 'Cat', '6': 'Dog'} # Mixed numeric and numeric string keys d6 = {1: None, '2': None} # None as values d7 = {None: 'Empty'} # None as key # Construct NumDicts from dicts n = NumDict() n0 = NumDict(d0) n1 = NumDict(d1) n2 = NumDict(d2) n3 = NumDict(d3) n4 = NumDict(d4) n5 = NumDict(d5) n6 = NumDict(d6) n7 = NumDict(d7) # Most NumDicts from dicts should compare equal... self.assertEqual(n, {}) self.assertEqual(n0, d0) self.assertEqual(n1, d1) self.assertEqual(n2, d2) # ...however, numeric keys are not equal to numeric string keys... self.assertNotEqual(n3, d3) self.assertNotEqual(n4, d4) self.assertNotEqual(n5, d5) self.assertNotEqual(n6, d6) # ...but None keys work just fine self.assertEqual(n7, d7) # Construct dicts from NumDicts dn = dict(n) dn1 = dict(n1) dn2 = dict(n2) dn3 = dict(n3) dn4 = dict(n4) dn5 = dict(n5) dn6 = dict(n6) dn7 = dict(n7) # All dicts from NumDicts should compare equal self.assertEqual(n, dn) self.assertEqual(n1, dn1) self.assertEqual(n2, dn2) self.assertEqual(n3, dn3) self.assertEqual(n4, dn4) self.assertEqual(n5, dn5) self.assertEqual(n6, dn6) self.assertEqual(n7, dn7) # Construct NumDicts from NumDicts nn = NumDict(n) nn0 = NumDict(n0) nn1 = NumDict(n1) nn2 = NumDict(n2) nn3 = NumDict(n3) nn4 = NumDict(n4) nn5 = NumDict(n5) nn6 = NumDict(n6) nn7 = NumDict(n7) # All NumDicts from NumDicts should compare equal self.assertEqual(n, nn) self.assertEqual(n0, nn0) self.assertEqual(n1, nn1) self.assertEqual(n2, nn2) self.assertEqual(n3, nn3) self.assertEqual(n4, nn4) self.assertEqual(n5, nn5) self.assertEqual(n6, nn6) self.assertEqual(n7, nn7) # keyword arg constructor should fail with self.assertRaises(TypeError): NumDict( one=1, two=2) # Raise TypeError since we can't have numeric keywords # item sequence constructors work fine... self.assertEqual(NumDict([(1, 'Elephant'), (2, 'Mouse')]), dn2) self.assertEqual(NumDict(dict=[(1, 'Elephant'), (2, 'Mouse')]), dn2) self.assertEqual(NumDict([(1, 'Elephant'), ('2', 'Mouse')]), dn2) self.assertEqual(NumDict(dict=[('1', 'Elephant'), (2, 'Mouse')]), dn2) # ...unless you have a non-numeric key with self.assertRaises(TypeError): NumDict([('Rat', 11), ('Snake', 12)]) with self.assertRaises(TypeError): NumDict(dict=[('Rat', 11), ('Snake', 12)]) # combining item sequence constructors with keyword args does not work with self.assertRaises( TypeError ): # Raise TypeError since we can't have numeric keywords NumDict([(1, 'one'), (2, 'two')], two=3, five=4) # alternate constructors d8 = {1: 'Echo', 2: 'Echo'} self.assertEqual(NumDict.fromkeys('1 2'.split()), dn6) self.assertEqual(NumDict().fromkeys('1 2'.split()), dn6) self.assertEqual(NumDict.fromkeys('1 2'.split(), 'Echo'), d8) self.assertEqual(NumDict().fromkeys('1 2'.split(), 'Echo'), d8) self.assertTrue(n1.fromkeys('1 2'.split()) is not n1) self.assertIsInstance(n1.fromkeys('1 2'.split()), NumDict) self.assertIsInstance(n2.fromkeys('1 2'.split()), NumDict) self.assertIsInstance(n3.fromkeys('1 2'.split()), NumDict) self.assertIsInstance(n4.fromkeys('1 2'.split()), NumDict)
def test_dict_access_and_modification(self): # dicts for testing d0 = {} # Empty dictionary d1 = {1: 'Elephant'} # Single numeric key d2 = {1: 'Elephant', 2: 'Mouse'} # Multiple numeric keys # Construct NumDicts from dicts n0 = NumDict() n1 = NumDict(d1) n2 = NumDict(d2) # test __getitem__ self.assertEqual(n2[1], 'Elephant') with self.assertRaises(KeyError): n1['Mouse'] # key is not numeric with self.assertRaises(KeyError): n1.__getitem__('Mouse') # key is not numeric with self.assertRaises(KeyError): n1[None] # key does not exist with self.assertRaises(KeyError): n1.__getitem__(None) # key does not exist # Test __setitem__ n3 = NumDict(n2) self.assertEqual(n2, n3) n3[2] = 'Frog' self.assertNotEqual(n2, n3) # Check None keys and numeric key conversion n3['3'] = 'Armadillo' n3[None] = 'Cockroach' # Check long ints n3[12390809518259081208909880312] = 'Squid' n3['12390809518259081208909880312'] = 'Octopus' self.assertEqual(n3[12390809518259081208909880312], 'Octopus') with self.assertRaises(TypeError): n3.__setitem__('Gorilla', 1) # key is not numeric with self.assertRaises(TypeError): n3['Chimpanzee'] = 1 # key is not numeric with self.assertRaises(TypeError): n3[(4, 1)] = 1 # key is not numeric with self.assertRaises(TypeError): n3[[1, 3, 4]] = 1 # key is not numeric and is not hashable # Test __delitem__ del n3[3] del n3[None] with self.assertRaises(KeyError): del n3[3] # already deleted with self.assertRaises(KeyError): n3.__delitem__(3) # already deleted with self.assertRaises(KeyError): del n3['Mouse'] # key would not exist, since it is not numeric # Test clear n3.clear() self.assertEqual(n3, {}) # Test copy() n2a = d2.copy() self.assertEqual(n2, n2a) n2b = n2.copy() self.assertEqual(n2b, n2) n2c = UserDict({1: 'Elephant', 2: 'Mouse'}) n2d = n2c.copy() # making a copy of a UserDict is special cased self.assertEqual(n2c, n2d) class MyNumDict(NumDict): """ subclass Numdict for testing """ def display(self): """ add a method to subclass to differentiate from superclass """ print('MyNumDict:', self) m2 = MyNumDict(n2) m2a = m2.copy() self.assertEqual(m2a, m2) m2[1] = 'Frog' self.assertNotEqual(m2a, m2) # Test keys, items, values self.assertEqual(sorted(n2.keys()), sorted(d2.keys())) self.assertEqual(sorted(n2.items()), sorted(d2.items())) self.assertEqual(sorted(n2.values()), sorted(d2.values())) # Test "in". for i in n2: self.assertIn(i, n2) self.assertEqual(i in n1, i in d1) self.assertEqual(i in n0, i in d0) self.assertFalse(None in n2) self.assertEqual(None in n2, None in d2) d2[None] = 'Cow' n2[None] = d2[None] self.assertTrue(None in n2) self.assertEqual(None in n2, None in d2) self.assertEqual(n2.has_key(None), None in d2) if not PY3: self.assertEqual(n2.has_key(None), d2.has_key(None)) self.assertFalse('Penguin' in n2) # Test update t = NumDict() t.update(d2) self.assertEqual(t, n2) # Test get for i in n2: self.assertEqual(n2.get(i), n2[i]) self.assertEqual(n1.get(i), d1.get(i)) self.assertEqual(n0.get(i), d0.get(i)) for i in ['purple', None, 12312301924091284, 23]: self.assertEqual(n2.get(i), d2.get(i), i) with self.assertRaises(AssertionError): i = '1' self.assertEqual(n2.get(i), d2.get(i), i) # d2 expects string key which does not exist # Test "in" iteration. n2b = n2 for i in range(20): n2[i] = str(i) n2b[str(i)] = str(i) self.assertEqual(n2, n2b) ikeys = [] for k in n2: ikeys.append(k) self.assertEqual(set(ikeys), set(n2.keys())) # Test setdefault x = 1 t = NumDict() self.assertEqual(t.setdefault(x, 42), 42) self.assertEqual(t.setdefault(x, '42'), 42) self.assertNotEqual(t.setdefault(x, 42), '42') self.assertNotEqual(t.setdefault(x, '42'), '42') self.assertIn(x, t) self.assertEqual(t.setdefault(x, 23), 42) self.assertEqual(t.setdefault(x, '23'), 42) self.assertNotEqual(t.setdefault(x, 23), '42') self.assertNotEqual(t.setdefault(x, '23'), '42') self.assertIn(x, t) # Test pop x = 1 t = NumDict({x: 42}) self.assertEqual(t.pop(x), 42) self.assertRaises(KeyError, t.pop, x) self.assertEqual(t.pop(x, 1), 1) t[x] = 42 self.assertEqual(t.pop(x, 1), 42) # Test popitem x = 1 t = NumDict({x: 42}) self.assertEqual(t.popitem(), (x, 42)) self.assertRaises(KeyError, t.popitem)
def test_repr(self): # dicts for testing d0 = {} # Empty dictionary d1 = {1: 'Elephant'} # Single numeric key d2 = {1: 'Elephant', 2: 'Mouse'} # Multiple numeric keys d3 = {'3': 'Aardvark'} # Numeric string key d4 = {'3': 'Aardvark', '4': 'Ant'} # Multiple numeric string keys d5 = {5: 'Cat', '6': 'Dog'} # Mixed numeric and numeric string keys d6 = {1: None, '2': None} # None as values d7 = {None: 'Empty'} # None as key # Construct NumDicts from dicts n = NumDict() n0 = NumDict(d0) n1 = NumDict(d1) n2 = NumDict(d2) n3 = NumDict(d3) n4 = NumDict(d4) n5 = NumDict(d5) n6 = NumDict(d6) n7 = NumDict(d7) reps = ( "{}", "{1: 'Elephant'}", "{1: 'Elephant', 2: 'Mouse'}", "'3': 'Aardvark'", "{'3': 'Aardvark', '4': 'Ant'}", "{5: 'Cat', '6': 'Dog'}", "{1: None, '2': None}", "{None: 'Empty'}", ) # Most representations of NumDicts should compare equal to dicts... self.assertEqual(str(n), str({})) self.assertEqual(repr(n), repr({})) self.assertIn(repr(n), reps) self.assertEqual(str(n0), str(d0)) self.assertEqual(repr(n0), repr(d0)) self.assertIn(repr(n0), reps) self.assertEqual(str(n1), str(d1)) self.assertEqual(repr(n1), repr(d1)) self.assertIn(repr(n1), reps) self.assertEqual(str(n2), str(d2)) self.assertEqual(repr(n2), repr(d2)) self.assertIn(repr(n2), reps) # ...however, numeric keys are not equal to numeric string keys... # ...so the string representations for those are different... self.assertNotEqual(str(n3), str(d3)) self.assertNotEqual(repr(n3), repr(d3)) self.assertNotIn(repr(n3), reps) self.assertNotEqual(str(n4), str(d4)) self.assertNotEqual(repr(n4), repr(d4)) self.assertNotIn(repr(n4), reps) self.assertNotEqual(str(n5), str(d5)) self.assertNotEqual(repr(n5), repr(d5)) self.assertNotIn(repr(n5), reps) self.assertNotEqual(str(n6), str(d6)) self.assertNotEqual(repr(n6), repr(d6)) self.assertNotIn(repr(n6), reps) # ...but None keys work just fine self.assertEqual(str(n7), str(d7)) self.assertEqual(repr(n7), repr(d7)) self.assertIn(repr(n7), reps)
class Quality(object): """ Determine quality and set status codes """ NONE = 0 # 0 SDTV = 1 # 1 SDDVD = 1 << 1 # 2 HDTV = 1 << 2 # 4 RAWHDTV = 1 << 3 # 8 -- 720p/1080i mpeg2 (trollhd releases) FULLHDTV = 1 << 4 # 16 -- 1080p HDTV (QCF releases) HDWEBDL = 1 << 5 # 32 FULLHDWEBDL = 1 << 6 # 64 -- 1080p web-dl HDBLURAY = 1 << 7 # 128 FULLHDBLURAY = 1 << 8 # 256 ANYHDTV = HDTV | FULLHDTV # 20 ANYWEBDL = HDWEBDL | FULLHDWEBDL # 96 ANYBLURAY = HDBLURAY | FULLHDBLURAY # 384 # put these bits at the other end of the spectrum, far enough out that they shouldn't interfere UNKNOWN = 1 << 15 # 32768 qualityStrings = NumDict({ NONE: "N/A", UNKNOWN: "Unknown", SDTV: "SDTV", SDDVD: "SD DVD", HDTV: "720p HDTV", RAWHDTV: "RawHD", FULLHDTV: "1080p HDTV", HDWEBDL: "720p WEB-DL", FULLHDWEBDL: "1080p WEB-DL", HDBLURAY: "720p BluRay", FULLHDBLURAY: "1080p BluRay" }) sceneQualityStrings = NumDict({ NONE: "N/A", UNKNOWN: "Unknown", SDTV: "HDTV", SDDVD: "", HDTV: "720p HDTV", RAWHDTV: "1080i HDTV", FULLHDTV: "1080p HDTV", HDWEBDL: "720p WEB-DL", FULLHDWEBDL: "1080p WEB-DL", HDBLURAY: "720p BluRay", FULLHDBLURAY: "1080p BluRay" }) combinedQualityStrings = NumDict({ ANYHDTV: "HDTV", ANYWEBDL: "WEB-DL", ANYBLURAY: "BluRay" }) cssClassStrings = NumDict({ NONE: "N/A", UNKNOWN: "Unknown", SDTV: "SDTV", SDDVD: "SDDVD", HDTV: "HD720p", RAWHDTV: "RawHD", FULLHDTV: "HD1080p", HDWEBDL: "HD720p", FULLHDWEBDL: "HD1080p", HDBLURAY: "HD720p", FULLHDBLURAY: "HD1080p", ANYHDTV: "any-hd", ANYWEBDL: "any-hd", ANYBLURAY: "any-hd" }) statusPrefixes = NumDict({ DOWNLOADED: "Downloaded", SNATCHED: "Snatched", SNATCHED_PROPER: "Snatched (Proper)", FAILED: "Failed", SNATCHED_BEST: "Snatched (Best)", ARCHIVED: "Archived" }) @staticmethod def _getStatusStrings(status): """ Returns string values associated with Status prefix :param status: Status prefix to resolve :return: Human readable status value """ to_return = {} for quality in Quality.qualityStrings: to_return[Quality.compositeStatus(status, quality)] = Quality.statusPrefixes[status] + " (" + \ Quality.qualityStrings[quality] + ")" return to_return @staticmethod def combineQualities(any_qualities, best_qualities): any_quality = 0 best_quality = 0 if any_qualities: any_quality = reduce(operator.or_, any_qualities) if best_qualities: best_quality = reduce(operator.or_, best_qualities) return any_quality | (best_quality << 16) @staticmethod def splitQuality(quality): any_qualities = [] best_qualities = [] for cur_qual in Quality.qualityStrings: if cur_qual & quality: any_qualities.append(cur_qual) if cur_qual << 16 & quality: best_qualities.append(cur_qual) return sorted(any_qualities), sorted(best_qualities) @staticmethod def nameQuality(name, anime=False): """ Return The quality from an episode File renamed by SickRage If no quality is achieved it will try sceneQuality regex :param name: to parse :param anime: Boolean to indicate if the show we're resolving is Anime :return: Quality prefix """ # Try Scene names first quality = Quality.sceneQuality(name, anime) if quality != Quality.UNKNOWN: return quality quality = Quality.assumeQuality(name) if quality != Quality.UNKNOWN: return quality return Quality.UNKNOWN @staticmethod def sceneQuality(name, anime=False): # pylint: disable=too-many-branches """ Return The quality from the scene episode File :param name: Episode filename to analyse :param anime: Boolean to indicate if the show we're resolving is Anime :return: Quality prefix """ ret = Quality.UNKNOWN if not name: return ret name = ek(path.basename, name) check_name = lambda regex_list, func: func( [re.search(regex, name, re.I) for regex in regex_list]) if anime: dvd_options = check_name([r"dvd", r"dvdrip"], any) bluray_options = check_name([r"BD", r"blue?-?ray"], any) sd_options = check_name([r"360p", r"480p", r"848x480", r"XviD"], any) hd_options = check_name([r"720p", r"1280x720", r"960x720"], any) full_hd = check_name([r"1080p", r"1920x1080"], any) if sd_options and not bluray_options and not dvd_options: ret = Quality.SDTV elif dvd_options: ret = Quality.SDDVD elif hd_options and not bluray_options and not full_hd: ret = Quality.HDTV elif full_hd and not bluray_options and not hd_options: ret = Quality.FULLHDTV elif hd_options and not bluray_options and not full_hd: ret = Quality.HDWEBDL elif bluray_options and hd_options and not full_hd: ret = Quality.HDBLURAY elif bluray_options and full_hd and not hd_options: ret = Quality.FULLHDBLURAY return ret if check_name([ r"480p|web.?dl|web(rip|mux|hd)|[sph]d.?tv|dsr|tv(rip|mux)|satrip", r"xvid|divx|[xh].?26[45]" ], all) and not check_name([r"(720|1080)[pi]"], all) and not check_name( [r"hr.ws.pdtv.[xh].?26[45]"], any): ret = Quality.SDTV elif check_name([ r"dvd(rip|mux)|b[rd](rip|mux)|blue?-?ray", r"xvid|divx|[xh].?26[45]" ], all) and not check_name([r"(720|1080)[pi]"], all) and not check_name( [r"hr.ws.pdtv.[xh].?26[45]"], any): ret = Quality.SDDVD elif check_name( [r"720p", r"hd.?tv", r"[xh].?26[45]"], all) or check_name([r"hr.ws.pdtv.[xh].?26[45]"], any) and not check_name([r"1080[pi]"], all): ret = Quality.HDTV elif check_name([r"720p|1080i", r"hd.?tv", r"mpeg-?2"], all) or check_name([r"1080[pi].hdtv", r"h.?26[45]"], all): ret = Quality.RAWHDTV elif check_name([r"1080p", r"hd.?tv", r"[xh].?26[45]"], all): ret = Quality.FULLHDTV elif check_name([r"720p", r"web.?dl|web(rip|mux|hd)"], all) or check_name( [r"720p", r"itunes", r"[xh].?26[45]"], all): ret = Quality.HDWEBDL elif check_name([r"1080p", r"web.?dl|web(rip|mux|hd)"], all) or check_name( [r"1080p", r"itunes", r"[xh].?26[45]"], all): ret = Quality.FULLHDWEBDL elif check_name( [r"720p", r"blue?-?ray|hddvd|b[rd](rip|mux)", r"[xh].?26[45]"], all): ret = Quality.HDBLURAY elif check_name( [r"1080p", r"blue?-?ray|hddvd|b[rd](rip|mux)", r"[xh].?26[45]"], all): ret = Quality.FULLHDBLURAY return ret @staticmethod def assumeQuality(name): """ Assume a quality from file extension if we cannot resolve it otherwise :param name: File name of episode to analyse :return: Quality prefix """ quality = Quality.qualityFromFileMeta(name) if quality != Quality.UNKNOWN: return quality if name.lower().endswith(".ts"): return Quality.RAWHDTV else: return Quality.UNKNOWN @staticmethod def qualityFromFileMeta(filename): # pylint: disable=too-many-branches """ Get quality file file metadata :param filename: Filename to analyse :return: Quality prefix """ log.use_print = False try: parser = createParser(filename) except Exception: # pylint: disable=broad-except parser = None if not parser: return Quality.UNKNOWN try: metadata = extractMetadata(parser) except Exception: # pylint: disable=broad-except metadata = None try: parser.stream._input.close() # pylint: disable=protected-access except Exception: # pylint: disable=broad-except pass if not metadata: return Quality.UNKNOWN height = 0 if metadata.has('height'): height = int(metadata.get('height') or 0) else: test = getattr(metadata, "iterGroups", None) if callable(test): for metagroup in metadata.iterGroups(): if metagroup.has('height'): height = int(metagroup.get('height') or 0) if not height: return Quality.UNKNOWN base_filename = ek(path.basename, filename) bluray = re.search(r"blue?-?ray|hddvd|b[rd](rip|mux)", base_filename, re.I) is not None webdl = re.search(r"web.?dl|web(rip|mux|hd)", base_filename, re.I) is not None ret = Quality.UNKNOWN if height > 1000: ret = ((Quality.FULLHDTV, Quality.FULLHDBLURAY)[bluray], Quality.FULLHDWEBDL)[webdl] elif 680 < height < 800: ret = ((Quality.HDTV, Quality.HDBLURAY)[bluray], Quality.HDWEBDL)[webdl] elif height < 680: ret = (Quality.SDTV, Quality.SDDVD)[re.search( r'dvd|b[rd]rip|blue?-?ray', base_filename, re.I) is not None] return ret @staticmethod def compositeStatus(status, quality): return status + 100 * quality @staticmethod def qualityDownloaded(status): return (status - DOWNLOADED) / 100 @staticmethod def splitCompositeStatus(status): """ Split a composite status code into a status and quality. :param status: to split :returns: a tuple containing (status, quality) """ if status == UNKNOWN: return UNKNOWN, Quality.UNKNOWN for q in sorted(Quality.qualityStrings.keys(), reverse=True): if status > q * 100: return status - q * 100, q return status, Quality.NONE @staticmethod def sceneQualityFromName(name, quality): # pylint: disable=too-many-branches """ Get scene naming parameters from filename and quality :param name: Filename to check :param quality: int of quality to make sure we get the right rip type :return: encoder type for scene quality naming """ codec_list = ['xvid', 'divx'] x264_list = ['x264', 'x 264', 'x.264'] h264_list = ['h264', 'h 264', 'h.264', 'avc'] x265_list = ['x265', 'x 265', 'x.265'] h265_list = ['h265', 'h 265', 'h.265', 'hevc'] codec_list += x264_list + h264_list + x265_list + h265_list found_codecs = {} found_codec = None for codec in codec_list: if codec in name.lower(): found_codecs[name.lower().rfind(codec)] = codec if found_codecs: sorted_codecs = sorted(found_codecs, reverse=True) found_codec = found_codecs[list(sorted_codecs)[0]] # 2 corresponds to SDDVD quality if quality == 2: if re.search(r"b(r|d|rd)?(-| |\.)?(rip|mux)", name.lower()): rip_type = " BDRip" elif re.search(r"(dvd)(-| |\.)?(rip|mux)?", name.lower()): rip_type = " DVDRip" else: rip_type = "" if found_codec: if codec_list[0] in found_codec: found_codec = 'XviD' elif codec_list[1] in found_codec: found_codec = 'DivX' elif found_codec in x264_list: found_codec = x264_list[0] elif found_codec in h264_list: found_codec = h264_list[0] elif found_codec in x265_list: found_codec = x265_list[0] elif found_codec in h265_list: found_codec = h265_list[0] if quality == 2: return rip_type + " " + found_codec else: return " " + found_codec elif quality == 2: return rip_type else: return "" @staticmethod def statusFromName(name, assume=True, anime=False): """ Get a status object from filename :param name: Filename to check :param assume: boolean to assume quality by extension if we can't figure it out :param anime: boolean to enable anime parsing :return: Composite status/quality object """ quality = Quality.nameQuality(name, anime) if assume and quality == Quality.UNKNOWN: quality = Quality.assumeQuality(name) return Quality.compositeStatus(DOWNLOADED, quality) DOWNLOADED = None SNATCHED = None SNATCHED_PROPER = None FAILED = None SNATCHED_BEST = None ARCHIVED = None