1# -*- coding: utf-8 -*- 2import pytest 3 4from flexget.components.parsing.parsers.parser_guessit import ParserGuessit 5from flexget.components.parsing.parsers.parser_internal import ParserInternal 6 7 8class TestSeriesParser: 9 @pytest.fixture( 10 scope='class', params=(ParserInternal, ParserGuessit), ids=['internal', 'guessit'] 11 ) 12 def parse(self, request): 13 p = request.param() 14 15 def parse(data, name=None, **kwargs): 16 return p.parse_series(data, name=name, **kwargs) 17 18 return parse 19 20 @pytest.fixture(scope='class') 21 def parse_invalid(self, parse): 22 def parse_invalid(name, data, **kwargs): 23 """Makes sure either ParseWarning is raised, or return is invalid.""" 24 r = parse(name, data, **kwargs) 25 assert not r.valid, '{data} should not be valid'.format(data=data) 26 27 return parse_invalid 28 29 def test_proper(self, parse): 30 """SeriesParser: proper""" 31 s = parse(name='Something Interesting', data='Something.Interesting.S01E02.Proper-FlexGet') 32 assert s.season == 1 33 assert s.episode == 2 34 assert s.quality.name == 'unknown' 35 assert s.proper, 'did not detect proper from %s' % s.data 36 s = parse(name='foobar', data='foobar 720p proper s01e01') 37 assert s.proper, 'did not detect proper from %s' % s.data 38 39 def test_non_proper(self, parse): 40 """SeriesParser: non-proper""" 41 s = parse(name='Something Interesting', data='Something.Interesting.S01E02-FlexGet') 42 assert s.season == 1 43 assert s.episode == 2 44 assert s.quality.name == 'unknown' 45 assert not s.proper, 'detected proper' 46 47 def test_anime_proper(self, parse): 48 """SeriesParser: anime fansub style proper (13v2)""" 49 s = parse(name='Anime', data='[aoeu] Anime 19v2 [23BA98]') 50 assert s.identifier == 19 51 assert s.proper_count == 1 52 s = parse(name='Anime', data='Anime_-_19v3') 53 assert s.identifier == 19 54 assert s.proper_count == 2 55 56 def test_basic(self, parse, parse_invalid): 57 """SeriesParser: basic parsing""" 58 parse_invalid( 59 name='Something Interesting', data='The.Something.Interesting.S01E02-FlexGet' 60 ) 61 62 s = parse(name='25', data='25.And.More.S01E02-FlexGet') 63 assert s.valid, 'Fix the implementation, should be valid' 64 assert s.identifier == 'S01E02', 'identifier broken' 65 66 def test_confusing_date(self, parse): 67 """SeriesParser: confusing (invalid) numbering scheme""" 68 s = parse(name='Something', data='Something.2008x12.13-FlexGet') 69 assert not s.episode, 'Should not have episode' 70 assert s.id_type == 'date' 71 assert s.identifier == '2008-12-13', 'invalid id' 72 assert s.valid, 'should be valid' 73 74 def test_unwanted_disc(self, parse_invalid): 75 """SeriesParser: unwanted disc releases""" 76 parse_invalid(name='Something', data='Something.S01D2.DVDR-FlexGet') 77 78 def test_season_x_ep(self, parse): 79 """SeriesParser: 01x02""" 80 s = parse(name='Something', data='Something.01x02-FlexGet') 81 assert s.season == 1 and s.episode == 2, 'failed to parse 01x02' 82 83 s = parse(name='Something', data='Something 1 x 2-FlexGet') 84 assert s.season == 1 and s.episode == 2, 'failed to parse 1 x 2' 85 86 # Ticket #732 87 s = parse(name='Something', data='Something - This is the Subtitle 14x9 [Group-Name]') 88 assert s.season == 14 and s.episode == 9, 'failed to parse %s' % s.data 89 90 @pytest.mark.skip(reason='FIX: #402 .. a bit hard to do') 91 def test_ep_in_square_brackets(self, parse): 92 """SeriesParser: [S01] [E02] NOT IMPLEMENTED""" 93 # FIX: #402 .. a bit hard to do 94 s = parse(name='Something', data='Something [S01] [E02]') 95 assert s.season == 1 and s.episode == 2, 'failed to parse %s' % s 96 97 def test_ep_in_parenthesis(self, parse): 98 """SeriesParser: test ep in parenthesis""" 99 s = parse(name='Something', data='Something (S01E02)') 100 assert s.season == 1 and s.episode == 2, 'failed to parse %s' % s 101 102 def test_season_episode(self, parse): 103 """SeriesParser: season X, episode Y""" 104 s = parse(name='Something', data='Something - Season 3, Episode 2') 105 assert s.season == 3 and s.episode == 2, 'failed to parse %s' % s 106 107 s = parse(name='Something', data='Something - Season2, Episode2') 108 assert s.season == 2 and s.episode == 2, 'failed to parse %s' % s 109 110 s = parse(name='Something', data='Something - Season2 Episode2') 111 assert s.season == 2 and s.episode == 2, 'failed to parse %s' % s 112 113 @pytest.mark.xfail(reason='Not supported in guessit, works for internal parser') 114 def test_series_episode(self, parse): 115 """SeriesParser: series X, episode Y""" 116 s = parse(name='Something', data='Something - Series 2, Episode 2') 117 assert s.season == 2 and s.episode == 2, 'failed to parse %s' % s 118 119 s = parse(name='Something', data='Something - Series3, Episode2') 120 assert s.season == 3 and s.episode == 2, 'failed to parse %s' % s 121 122 s = parse(name='Something', data='Something - Series4 Episode2') 123 assert s.season == 4 and s.episode == 2, 'failed to parse %s' % s 124 125 def test_episode(self, parse): 126 """SeriesParser: episode X (assume season 1)""" 127 s = parse(name='Something', data='Something - Episode2') 128 assert s.season == 1 and s.episode == 2, 'failed to parse %s' % s 129 130 s = parse(name='Something', data='Something - Episode 2') 131 assert s.season == 1 and s.episode == 2, 'failed to parse %s' % s 132 133 s = parse(name='Something', data='Something - Episode VIII') 134 assert s.season == 1 and s.episode == 8, 'failed to parse %s' % s 135 136 def test_ep(self, parse): 137 """SeriesParser: ep X (assume season 1)""" 138 s = parse(name='Something', data='Something - Ep2') 139 assert s.season == 1 and s.episode == 2, 'failed to parse %s' % s 140 141 s = parse(name='Something', data='Something - Ep 2') 142 assert s.season == 1 and s.episode == 2, 'failed to parse %s' % s 143 144 s = parse(name='Something', data='Something - Ep VIII') 145 assert s.season == 1 and s.episode == 8, 'failed to parse %s' % s 146 147 s = parse(name='Something', data='Something - E01') 148 assert s.season == 1 and s.episode == 1, 'failed to parse %s' % s 149 150 def test_season_episode_of_total(self, parse): 151 """SeriesParser: season X YofZ""" 152 s = parse(name='Something', data='Something Season 2 2of12') 153 assert s.season == 2 and s.episode == 2, 'failed to parse %s' % s 154 155 s = parse(name='Something', data='Something Season 2, 2 of 12') 156 assert s.season == 2 and s.episode == 2, 'failed to parse %s' % s 157 158 def test_episode_of_total(self, parse): 159 """SeriesParser: YofZ (assume season 1)""" 160 s = parse(name='Something', data='Something 2of12') 161 assert s.season == 1 and s.episode == 2, 'failed to parse %s' % s 162 163 s = parse(name='Something', data='Something 2 of 12') 164 assert s.season == 1 and s.episode == 2, 'failed to parse %s' % s 165 166 def test_part(self, parse): 167 """SeriesParser: test parsing part numeral (assume season 1)""" 168 s = parse(name='Test', data='Test.Pt.I.720p-FlexGet') 169 assert s.season == 1 and s.episode == 1, 'failed to parse %s' % s 170 s = parse(name='Test', data='Test.Pt.VI.720p-FlexGet') 171 assert s.season == 1 and s.episode == 6, 'failed to parse %s' % s 172 s = parse(name='Test', data='Test.Part.2.720p-FlexGet') 173 assert s.season == 1 and s.episode == 2, 'failed to parse %s' % s 174 assert s.identifier == 'S01E02' 175 s = parse(name='Test', data='Test.Part3.720p-FlexGet') 176 assert s.season == 1 and s.episode == 3, 'failed to parse %s' % s 177 s = parse(name='Test', data='Test.Season.3.Part.IV') 178 assert s.season == 3 and s.episode == 4, 'failed to parse %s' % s 179 s = parse(name='Test', data='Test.Part.One') 180 assert s.season == 1 and s.episode == 1, 'failed to parse %s' % s 181 182 def test_digits(self, parse): 183 """SeriesParser: digits (UID)""" 184 s = parse(name='Something', data='Something 01 FlexGet') 185 assert s.id == 1, 'failed to parse %s' % s.data 186 assert s.id_type == 'sequence' 187 188 s = parse(name='Something', data='Something-121.H264.FlexGet') 189 assert s.id == 121, 'failed to parse %s' % s.data 190 assert s.id_type == 'sequence' 191 192 s = parse(name='Something', data='Something 1 AC3') 193 assert s.id == 1, 'failed to parse %s' % s.data 194 assert s.id_type == 'sequence' 195 196 s = parse(name='Something', data='[TheGroup] Something - 12 1280x720 x264-Hi10P') 197 assert s.id == 12, 'failed to parse %s' % s.data 198 assert s.id_type == 'sequence' 199 200 def test_quality(self, parse): 201 """SeriesParser: quality""" 202 s = parse(name='Foo Bar', data='Foo.Bar.S01E01.720p.HDTV.x264-FlexGet') 203 assert s.season == 1 and s.episode == 1, 'failed to parse episodes from %s' % s.data 204 assert s.quality.name == '720p hdtv h264', 'failed to parse quality from %s' % s.data 205 206 s = parse(name='Test', data='Test.S01E01.720p-FlexGet') 207 assert s.quality.name == '720p', 'failed to parse quality from %s' % s.data 208 209 s = parse(name='30 Suck', data='30 Suck 4x4 [HDTV - FlexGet]') 210 assert s.quality.name == 'hdtv', 'failed to parse quality %s' % s.data 211 212 s = parse(name='ShowB', data='ShowB.S04E19.Name of Ep.720p.WEB-DL.DD5.1.H.264') 213 assert s.quality.name == '720p webdl h264 dd5.1', 'failed to parse quality %s' % s.data 214 215 def test_quality_parenthesis(self, parse): 216 """SeriesParser: quality in parenthesis""" 217 s = parse(name='Foo Bar', data='Foo.Bar.S01E01.[720p].HDTV.x264-FlexGet') 218 assert s.season == 1 and s.episode == 1, 'failed to parse episodes from %s' % s.data 219 assert s.quality.name == '720p hdtv h264', 'failed to parse quality from %s' % s.data 220 221 s = parse(name='Foo Bar', data='Foo.Bar.S01E01.(720p).HDTV.x264-FlexGet') 222 assert s.season == 1 and s.episode == 1, 'failed to parse episodes from %s' % s.data 223 assert s.quality.name == '720p hdtv h264', 'failed to parse quality from %s' % s.data 224 225 s = parse(name='Foo Bar', data='[720p]Foo.Bar.S01E01.HDTV.x264-FlexGet') 226 assert s.season == 1 and s.episode == 1, 'failed to parse episodes from %s' % s.data 227 assert s.quality.name == '720p hdtv h264', 'failed to parse quality from %s' % s.data 228 229 def test_numeric_names(self, parse): 230 """SeriesParser: numeric names (24)""" 231 s = parse(name='24', data='24.1x2-FlexGet') 232 assert s.season == 1 and s.episode == 2, 'failed to parse %s' % s.data 233 234 s = parse(name='90120', data='90120.1x2-FlexGet') 235 assert s.season == 1 and s.episode == 2, 'failed to parse %s' % s.data 236 237 def test_group_prefix(self, parse): 238 """SeriesParser: [group] before name""" 239 s = parse(name='Foo Bar', data='[l.u.l.z] Foo Bar - 11 (H.264) [5235532D].mkv') 240 assert s.id == 11, 'failed to parse %s' % s.data 241 242 s = parse(name='Foo Bar', data='[7.1.7.5] Foo Bar - 11 (H.264) [5235532D].mkv') 243 assert s.id == 11, 'failed to parse %s' % s.data 244 245 def test_hd_prefix(self, parse): 246 """SeriesParser: HD 720p before name""" 247 s = parse(name='Foo Bar', data='HD 720p: Foo Bar - 11 (H.264) [5235532D].mkv') 248 assert s.id == 11, 'failed to parse %s' % s.data 249 assert s.quality.name == '720p h264', 'failed to pick up quality' 250 251 def test_partially_numeric(self, parse): 252 """SeriesParser: partially numeric names""" 253 s = parse(name='Foo 2009', data='Foo.2009.S02E04.HDTV.XviD-2HD[FlexGet]') 254 assert s.season == 2 and s.episode == 4, 'failed to parse %s' % s.data 255 assert s.quality.name == 'hdtv xvid', 'failed to parse quality from %s' % s.data 256 257 def test_ignore_seasonpacks(self, parse, parse_invalid): 258 """SeriesParser: ignoring season packs""" 259 # parse_invalid(name='The Foo', data='The.Foo.S04.1080p.FlexGet.5.1') 260 parse_invalid(name='The Foo', data='The Foo S05 720p BluRay DTS x264-FlexGet') 261 parse_invalid(name='The Foo', data='The Foo S05 720p BluRay DTS x264-FlexGet') 262 parse_invalid(name='Something', data='Something S02 Pack 720p WEB-DL-FlexGet') 263 parse_invalid(name='Something', data='Something S06 AC3-CRAPL3SS') 264 parse_invalid( 265 name='Something', 266 data='Something SEASON 1 2010 540p BluRay QEBS AAC ANDROID IPAD MP4 FASM', 267 ) 268 parse_invalid(name='Something', data='Something.1x0.Complete.Season-FlexGet') 269 parse_invalid(name='Something', data='Something.1xAll.Season.Complete-FlexGet') 270 parse_invalid(name='Something', data='Something Seasons 1 & 2 - Complete') 271 parse_invalid(name='Something', data='Something Seasons 4 Complete') 272 parse_invalid(name='Something', data='Something Seasons 1 2 3 4') 273 parse_invalid(name='Something', data='Something S6 E1-4') 274 parse_invalid(name='Something', data='Something_Season_1_Full_Season_2_EP_1-7_HD') 275 parse_invalid(name='Something', data='Something - Season 10 - FlexGet') 276 parse_invalid(name='Something', data='Something_ DISC_1_OF_2 MANofKENT INVICTA RG') 277 # Make sure no false positives 278 assert parse(name='Something', data='Something S01E03 Full Throttle').valid 279 280 def test_similar(self, parse): 281 s = parse( 282 name='Foo Bar', data='Foo.Bar:Doppelganger.S02E04.HDTV.FlexGet', strict_name=True 283 ) 284 assert not s.valid, 'should not have parser Foo.Bar:Doppelganger' 285 s = parse( 286 name='Foo Bar', data='Foo.Bar.Doppelganger.S02E04.HDTV.FlexGet', strict_name=True 287 ) 288 assert not s.valid, 'should not have parser Foo.Bar.Doppelganger' 289 290 def test_idiotic_numbering(self, parse): 291 """SeriesParser: idiotic 101, 102, 103, .. numbering""" 292 s = parse('Test.706.720p-FlexGet', name='test', identified_by='ep') 293 assert s.season == 7, 'didn\'t pick up season' 294 assert s.episode == 6, 'didn\'t pick up episode' 295 296 def test_idiotic_numbering_with_zero(self, parse): 297 """SeriesParser: idiotic 0101, 0102, 0103, .. numbering""" 298 s = parse('Test.0706.720p-FlexGet', name='test', identified_by='ep') 299 assert s.season == 7, 'season missing' 300 assert s.episode == 6, 'episode missing' 301 assert s.identifier == 'S07E06', 'identifier broken' 302 303 def test_idiotic_invalid(self, parse): 304 """SeriesParser: idiotic confused by invalid""" 305 s = parse( 306 'Test.Revealed.WS.PDTV.XviD-aAF.5190458.TPB.torrent', name='test', identified_by='ep' 307 ) 308 # assert_raises(ParseWarning, s.parse) 309 assert not s.season == 5, 'confused, got season' 310 assert not s.season == 4, 'confused, got season' 311 assert not s.episode == 19, 'confused, got episode' 312 assert not s.episode == 58, 'confused, got episode' 313 314 def test_zeroes(self, parse): 315 """SeriesParser: test zeroes as a season, episode""" 316 317 for data in ['Test.S00E00-FlexGet', 'Test.S00E01-FlexGet', 'Test.S01E00-FlexGet']: 318 s = parse(name='Test', data=data) 319 id = s.identifier 320 assert s.valid, 'parser not a valid for %s' % data 321 assert isinstance(id, str), 'id is not a string for %s' % data 322 assert isinstance(s.season, int), 'season is not a int for %s' % data 323 assert isinstance(s.episode, int), 'season is not a int for %s' % data 324 325 def test_exact_name(self, parse): 326 """SeriesParser: test exact/strict name parsing""" 327 328 s = parse('Test.Foobar.S01E02.720p-FlexGet', name='test') 329 assert s.valid, 'normal failed' 330 331 s = parse('Test.A.S01E02.720p-FlexGet', name='test', strict_name=True) 332 assert not s.valid, 'strict A failed' 333 334 s = parse('Test.AB.S01E02.720p-FlexGet', name='Test AB', strict_name=True) 335 assert s.valid, 'strict AB failed' 336 337 s = parse('Red Tomato (US) S01E02 720p-FlexGet', name='Red Tomato', strict_name=True) 338 assert not s.valid, 'Red Tomato (US) should not match Red Tomato in exact mode' 339 340 def test_name_word_boundries(self, parse): 341 name = 'test' 342 s = parse('Test.S01E02.720p-FlexGet', name=name) 343 assert s.valid, 'normal failed' 344 # In non-exact mode these should match 345 s = parse('Test.crap.S01E02.720p-FlexGet', name=name) 346 assert s.valid, 'normal failed' 347 s = parse('Test_crap.S01E02.720p-FlexGet', name=name) 348 assert s.valid, 'underscore failed' 349 # However if the title ends mid-word, it should not match 350 s = parse('Testing.S01E02.720p-FlexGet', name=name) 351 assert not s.valid, 'word border failed' 352 353 def test_quality_as_ep(self, parse): 354 """SeriesParser: test that qualities are not picked as ep""" 355 from flexget.utils import qualities 356 357 for quality in qualities.all_components(): 358 parse('FooBar %s XviD-FlexGet' % quality.name, name='FooBar') 359 360 def test_sound_as_ep(self, parse): 361 """SeriesParser: test that sound infos are not picked as ep""" 362 sounds = ['AC3', 'DD5.1', 'DTS'] 363 for sound in sounds: 364 parse(data='FooBar %s XViD-FlexGet' % sound, name='FooBar') 365 366 @pytest.mark.xfail(reason='Bug in guessit, works for internal parser') 367 def test_ep_as_quality(self, parse): 368 """SeriesParser: test that eps are not picked as qualities""" 369 from flexget.utils import qualities 370 371 for quality1 in qualities.all_components(): 372 # Attempt to create an episode number out of quality 373 mock_ep1 = ''.join(list(filter(str.isdigit, quality1.name))) 374 if not mock_ep1: 375 continue 376 377 for quality2 in qualities.all_components(): 378 mock_ep2 = ''.join(list(filter(str.isdigit, quality2.name))) 379 if not mock_ep2: 380 continue 381 382 # 720i, 1080i, etc. are failing because 383 # e.g the 720 in 720i can always be taken to mean 720p, 384 # which is a higher priority quality. 385 # Moreover, 1080 as an ep number is always failing because 386 # sequence regexps support at most 3 digits at the moment. 387 # Luckily, all of these cases are discarded by the following, 388 # which also discards the failing cases when episode number 389 # (e.g. 720) is greater or equal than quality number (e.g. 480p). 390 # There's nothing that can be done with those failing cases with the 391 # current 392 # "grab leftmost occurrence of highest quality-like thing" algorithm. 393 if int(mock_ep1) >= int(mock_ep2) or int(mock_ep2) > 999: 394 continue 395 396 s = parse('FooBar - %s %s-FlexGet' % (mock_ep1, quality2.name), name='FooBar') 397 assert s.episode == int(mock_ep1), "confused episode %s with quality %s" % ( 398 mock_ep1, 399 quality2.name, 400 ) 401 402 # Also test with reversed relative order of episode and quality 403 s = parse('[%s] FooBar - %s [FlexGet]' % (quality2.name, mock_ep1), name='FooBar') 404 assert s.episode == int(mock_ep1), "confused episode %s with quality %s" % ( 405 mock_ep1, 406 quality2.name, 407 ) 408 409 def test_name_with_number(self, parse): 410 """SeriesParser: test number in a name""" 411 parse('Storage 13 no ep number', name='Storage 13') 412 413 def test_name_uncorrupted(self, parse): 414 """SeriesParser: test name doesn't get corrupted when cleaned""" 415 s = parse( 416 name='The New Adventures of Old Christine', 417 data='The.New.Adventures.of.Old.Christine.S05E16.HDTV.XviD-FlexGet', 418 ) 419 assert s.name == 'The New Adventures of Old Christine' 420 assert s.season == 5 421 assert s.episode == 16 422 assert s.quality.name == 'hdtv xvid' 423 424 def test_from_groups(self, parse): 425 """SeriesParser: test from groups""" 426 s = parse('Test.S01E01-Group', name='Test', allow_groups=['xxxx', 'group']) 427 assert s.group.lower() == 'group', 'did not get group' 428 429 def test_group_dashes(self, parse): 430 """SeriesParser: group name around extra dashes""" 431 s = parse('Test.S01E01-FooBar-Group', name='Test', allow_groups=['xxxx', 'group']) 432 assert s.group.lower() == 'group', 'did not get group with extra dashes' 433 434 def test_id_and_hash(self, parse): 435 """SeriesParser: Series with confusing hash""" 436 s = parse(name='Something', data='Something 63 [560D3414]') 437 assert s.id == 63, 'failed to parse %s' % s.data 438 439 s = parse(name='Something', data='Something 62 [293A8395]') 440 assert s.id == 62, 'failed to parse %s' % s.data 441 442 def test_ticket_700(self, parse): 443 """SeriesParser: confusing name (#700)""" 444 s = parse(name='Something', data='Something 9x02 - Episode 2') 445 assert s.season == 9, 'failed to parse season' 446 assert s.episode == 2, 'failed to parse episode' 447 448 def test_date_id(self, parse): 449 """SeriesParser: Series with dates""" 450 s = parse(name='Something', data='Something.2010.10.25') 451 assert s.identifier == '2010-10-25', 'failed to parse %s' % s.data 452 assert s.id_type == 'date' 453 454 s = parse(name='Something', data='Something 2010-10-25') 455 assert s.identifier == '2010-10-25', 'failed to parse %s' % s.data 456 assert s.id_type == 'date' 457 458 s = parse(name='Something', data='Something 10/25/2010') 459 assert s.identifier == '2010-10-25', 'failed to parse %s' % s.data 460 assert s.id_type == 'date' 461 462 s = parse(name='Something', data='Something 25.10.2010') 463 assert s.identifier == '2010-10-25', 'failed to parse %s' % s.data 464 assert s.id_type == 'date' 465 466 # February 1 is picked rather than January 2 because it is closer to now 467 s = parse(name='Something', data='Something 1.2.11') 468 assert s.identifier == '2011-02-01', 'failed to parse %s' % s.data 469 assert s.id_type == 'date' 470 471 # Future dates should not be considered dates 472 s = parse(name='Something', data='Something 01.02.32') 473 assert s.id_type != 'date' 474 475 # Dates with parts used to be parsed as episodes. 476 s = parse(name='Something', data='Something.2010.10.25, Part 2') 477 assert s.identifier == '2010-10-25', 'failed to parse %s' % s.data 478 assert s.id_type == 'date' 479 480 # Text based dates 481 s = parse(name='Something', data='Something (18th july 2013)') 482 assert s.identifier == '2013-07-18', 'failed to parse %s' % s.data 483 assert s.id_type == 'date' 484 485 s = parse(name='Something', data='Something 2 mar 2013)') 486 assert s.identifier == '2013-03-02', 'failed to parse %s' % s.data 487 assert s.id_type == 'date' 488 489 s = parse(name='Something', data='Something 1st february 1993)') 490 assert s.identifier == '1993-02-01', 'failed to parse %s' % s.data 491 assert s.id_type == 'date' 492 493 def test_date_options(self, parse): 494 # By default we should pick the latest interpretation 495 s = parse(name='Something', data='Something 01-02-03') 496 assert s.identifier == '2003-02-01', 'failed to parse %s' % s.data 497 498 # Test it still works with both options specified 499 s = parse( 500 name='Something', data='Something 01-02-03', date_yearfirst=False, date_dayfirst=True 501 ) 502 assert s.identifier == '2003-02-01', 'failed to parse %s' % s.data 503 504 # If we specify yearfirst yes it should force another interpretation 505 s = parse(name='Something', data='Something 01-02-03', date_yearfirst=True) 506 assert s.identifier == '2001-03-02', 'failed to parse %s' % s.data 507 508 # If we specify dayfirst no it should force the third interpretation 509 s = parse(name='Something', data='Something 01-02-03', date_dayfirst=False) 510 assert s.identifier == '2003-01-02', 'failed to parse %s' % s.data 511 512 def test_season_title_episode(self, parse): 513 """SeriesParser: Series with title between season and episode""" 514 s = parse(name='Something', data='Something.S5.Drunk.Santa.Part1') 515 assert s.season == 5, 'failed to parse season' 516 assert s.episode == 1, 'failed to parse episode' 517 518 def test_specials(self, parse): 519 """SeriesParser: Special episodes with no id""" 520 s = parse( 521 name='The Show', data='The Show 2005 A Christmas Carol 2010 Special 720p HDTV x264' 522 ) 523 assert s.valid, 'Special episode should be valid' 524 525 def test_double_episodes(self, parse): 526 s = parse(name='Something', data='Something.S04E05-06') 527 assert s.season == 4, 'failed to parse season' 528 assert s.episode == 5, 'failed to parse episode' 529 assert s.episodes == 2, 'failed to parse episode range' 530 s = parse(name='Something', data='Something.S04E05-E06') 531 assert s.season == 4, 'failed to parse season' 532 assert s.episode == 5, 'failed to parse episode' 533 assert s.episodes == 2, 'failed to parse episode range' 534 s = parse(name='Something', data='Something.S04E05E06') 535 assert s.season == 4, 'failed to parse season' 536 assert s.episode == 5, 'failed to parse episode' 537 assert s.episodes == 2, 'failed to parse episode range' 538 s = parse(name='Something', data='Something.4x05-06') 539 assert s.season == 4, 'failed to parse season' 540 assert s.episode == 5, 'failed to parse episode' 541 assert s.episodes == 2, 'failed to parse episode range' 542 # Test that too large a range is not accepted 543 s = parse(name='Something', data='Something.S04E05-09') 544 assert not s.valid, 'large episode range should not be valid' 545 # Make sure regular identifier doesn't have end_episode 546 s = parse(name='Something', data='Something.S04E05') 547 assert s.episodes == 1, 'should not have detected end_episode' 548 549 def test_and_replacement(self, parse): 550 titles = [ 551 'Alpha.&.Beta.S01E02.hdtv', 552 'alpha.and.beta.S01E02.hdtv', 553 'alpha&beta.S01E02.hdtv', 554 ] 555 for title in titles: 556 s = parse(name='Alpha & Beta', data=title) 557 assert s.valid 558 s = parse(name='Alpha and Beta', data=title) 559 assert s.valid 560 # Test 'and' isn't replaced within a word 561 s = parse(name='Sandy Dunes', data='S&y Dunes.S01E01.hdtv') 562 assert not s.valid 563 564 def test_unicode(self, parse): 565 s = parse(name='abc äää abc', data='abc.äää.abc.s01e02') 566 assert s.season == 1 567 assert s.episode == 2 568 569 def test_parentheticals(self, parse): 570 s = parse('The Show (US)', name="The Show (US)") 571 # Make sure US is ok outside of parentheses 572 s = parse('The.Show.US.S01E01', name="The Show (US)") 573 assert s.valid 574 # Make sure US is ok inside parentheses 575 s = parse('The Show (US) S01E01', name="The Show (US)") 576 assert s.valid 577 # Make sure it works without US 578 s = parse('The.Show.S01E01', name="The Show (US)") 579 assert s.valid 580 # Make sure it doesn't work with a different country 581 s = parse('The Show (UK) S01E01', name="The Show (US)") 582 assert not s.valid 583 584 def test_id_regexps(self, parse): 585 id_regexps = ['(dog)?e(cat)?'] 586 s = parse('The Show dogecat', name='The Show', id_regexps=id_regexps) 587 assert s.valid 588 assert s.id == 'dog-cat' 589 s = parse('The Show doge', name='The Show', id_regexps=id_regexps) 590 assert s.valid 591 assert s.id == 'dog' 592 s = parse('The Show ecat', name='The Show', id_regexps=id_regexps) 593 assert s.valid 594 assert s.id == 'cat' 595 # assert_raises(ParseWarning, s.parse, 'The Show e') 596 597 def test_apostrophe(self, parse): 598 s = parse(name=u"FlexGet's show", data=u"FlexGet's show s01e01") 599 assert s.valid 600 s = parse(name=u"FlexGet's show", data=u"FlexGets show s01e01") 601 assert s.valid 602 s = parse(name=u"FlexGet's show", data=u"FlexGet s show s01e01") 603 assert s.valid 604 s = parse(name=u"FlexGet's show", data=u"FlexGet show s01e01") 605 assert not s.valid 606 # bad data with leftover escaping 607 s = parse(name=u"FlexGet's show", data=u"FlexGet\\'s show s01e01") 608 assert s.valid 609 610 def test_alternate_names(self, parse): 611 name = 'The Show' 612 alternate_names = ['Show', 'Completely Different'] 613 s = parse('The Show S01E01', name=name, alternate_names=alternate_names) 614 assert s.valid 615 s = parse('Show S01E01', name=name, alternate_names=alternate_names) 616 assert s.valid 617 s = parse('Completely.Different.S01E01', name=name, alternate_names=alternate_names) 618 assert s.valid 619 s = parse('Not The Show S01E01', name=name, alternate_names=alternate_names) 620 assert not s.valid 621 622 def test_long_season(self, parse): 623 """SeriesParser: long season ID Ticket #2197""" 624 s = parse( 625 name='FlexGet', data='FlexGet.US.S2013E14.Title.Here.720p.HDTV.AAC5.1.x264-NOGRP' 626 ) 627 assert s.season == 2013 628 assert s.episode == 14 629 assert s.quality.name == '720p hdtv h264 aac' 630 assert not s.proper, 'detected proper' 631 632 s = parse( 633 name='FlexGet', 634 data='FlexGet.Series.2013.14.of.21.Title.Here.720p.HDTV.AAC5.1.x264-NOGRP', 635 ) 636 assert s.season == 2013 637 assert s.episode == 14 638 assert s.quality.name == '720p hdtv h264 aac' 639 assert not s.proper, 'detected proper' 640 641 def test_episode_with_season_pack_match_is_not_season_pack(self, parse): 642 r"""SeriesParser: Github issue #1986, s\d{1} parses as invalid season""" 643 s = parse(name='The Show', data='The.Show.S01E01.eps3.0.some.title.720p.x264-NOGRP') 644 assert s.valid 645 assert not s.season_pack 646 assert s.season == 1 647 assert s.episode == 1 648