Merge remote-tracking branch 'origin/matchtree'
[smewt:guessit.git] / guessit / patterns.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 # GuessIt - A library for guessing information from filenames
5 # Copyright (c) 2011 Nicolas Wack <wackou@gmail.com>
6 # Copyright (c) 2011 Ricard Marxer <ricardmp@gmail.com>
7 #
8 # GuessIt is free software; you can redistribute it and/or modify it under
9 # the terms of the Lesser GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # GuessIt is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # Lesser GNU General Public License for more details.
17 #
18 # You should have received a copy of the Lesser GNU General Public License
19 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21
22
23 subtitle_exts = [ 'srt', 'idx', 'sub', 'ssa', 'txt' ]
24
25 video_exts = [ 'avi', 'mkv', 'mpg', 'mp4', 'm4v', 'mov', 'ogg', 'ogm', 'ogv', 'wmv', 'divx' ]
26
27 group_delimiters = [ '()', '[]', '{}' ]
28
29 # separator character regexp
30 sep = r'[][)(}{+ \._-]' # regexp art, hehe :D
31
32 # character used to represent a deleted char (when matching groups)
33 deleted = '_'
34
35 # format: [ (regexp, confidence, span_adjust) ]
36 episode_rexps = [ # ... Season 2 ...
37                   (r'season (?P<season>[0-9]+)', 1.0, (0, 0)),
38                   (r'saison (?P<season>[0-9]+)', 1.0, (0, 0)),
39
40                   # ... s02-x01 ...
41                   (r's(?P<season>[0-9]{1,2})-x(?P<bonusNumber>[0-9]{1,2})[^0-9]', 1.0, (0, -1)),
42
43                   # ... s02e13 ...
44                   (r'[Ss](?P<season>[0-9]{1,2}).{,3}[EeXx](?P<episodeNumber>[0-9]{1,2})[^0-9]', 1.0, (0, -1)),
45
46                   # ... 2x13 ...
47                   (r'[^0-9](?P<season>[0-9]{1,2})[x\.](?P<episodeNumber>[0-9]{2})[^0-9]', 0.8, (1, -1)),
48
49                   # ... s02 ...
50                   (sep + r's(?P<season>[0-9]{1,2})' + sep, 0.6, (1, -1)),
51                   (sep + r's(?P<season>[0-9]{1,2})', 0.6, (1, 0)),
52
53                   # v2 or v3 for some mangas which have multiples rips
54                   (sep + r'(?P<episodeNumber>[0-9]{1,3})v[23]' + sep, 0.6, (0, 0)),
55                   ]
56
57
58 weak_episode_rexps = [ # ... 213 or 0106 ...
59                        (sep + r'(?P<episodeNumber>[0-9]{1,4})' + sep, (1, -1)),
60                        ]
61
62 non_episode_title = [ 'extras' ]
63
64
65 video_rexps = [ # cd number
66                 (r'cd ?(?P<cdNumber>[0-9])( ?of ?(?P<cdNumberTotal>[0-9]))?', 1.0, (0, 0)),
67                 (r'(?P<cdNumberTotal>[1-9]) cds?', 0.9, (0, 0)),
68
69                 # special editions
70                 (r'edition' + sep + r'(?P<edition>collector)', 1.0, (0, 0)),
71                 (r'(?P<edition>collector)' + sep + 'edition', 1.0, (0, 0)),
72                 (r'(?P<edition>special)' + sep + 'edition', 1.0, (0, 0)),
73                 (r'(?P<edition>criterion)' + sep + 'edition', 1.0, (0, 0)),
74
75                 # director's cut
76                 (r"(?P<edition>director'?s?" + sep + "cut)", 1.0, (0, 0)),
77
78                 # video size
79                 (r'(?P<width>[0-9]{3,4})x(?P<height>[0-9]{3,4})', 0.9, (0, 0)),
80
81                 # website
82                 (r'(?P<website>www(\.[a-zA-Z0-9]+){2,3})', 0.8, (0, 0))
83                 ]
84
85 websites = [ 'tvu.org.ru', 'emule-island.com', 'UsaBit.com', 'www.divx-overnet.com', 'sharethefiles.com' ]
86
87 unlikely_series = ['series']
88
89 properties = { 'format': [ 'DVDRip', 'HD-DVD', 'HDDVD', 'HDDVDRip', 'BluRay', 'Blu-ray', 'BDRip', 'BRRip',
90                            'HDRip', 'DVD', 'DVDivX', 'HDTV', 'DVB', 'DVBRip', 'PDTV', 'WEBRip',
91                            'DVDSCR', 'Screener', 'VHS', 'VIDEO_TS' ],
92
93                'screenSize': [ '720p', '720' ],
94
95                'videoCodec': [ 'XviD', 'DivX', 'x264', 'h264', 'Rv10' ],
96
97                'audioCodec': [ 'AC3', 'DTS', 'He-AAC', 'AAC-He', 'AAC' ],
98
99                'audioChannels': [ '5.1' ],
100
101                'releaseGroup': [ 'ESiR', 'WAF', 'SEPTiC', '[XCT]', 'iNT', 'PUKKA',
102                                  'CHD', 'ViTE', 'TLF', 'DEiTY', 'FLAiTE',
103                                  'MDX', 'GM4F', 'DVL', 'SVD', 'iLUMiNADOS', ' FiNaLe',
104                                  'UnSeeN', 'aXXo', 'KLAXXON', 'NoTV', 'ZeaL', 'LOL' ],
105
106                'episodeFormat': [ 'Minisode', 'Minisodes' ],
107
108                'other': [ '5ch', 'PROPER', 'REPACK', 'LIMITED', 'DualAudio', 'iNTERNAL', 'Audiofixed', 'R5',
109                           'complete', 'classic', # not so sure about these ones, could appear in a title
110                           'ws', # widescreen
111                           #'SE', # special edition
112                           # TODO: director's cut
113                           ],
114                }
115
116 def find_properties(filename):
117     result = []
118     clow = filename.lower()
119     for prop, values in properties.items():
120         for value in values:
121             pos = clow.find(value.lower())
122             if pos != -1:
123                 end = pos + len(value)
124                 # make sure our word is always surrounded by separators
125                 if ((pos > 0 and clow[pos-1] not in sep) or
126                     (end < len(clow) and clow[end] not in sep)):
127                     # note: sep is a regexp, but in this case using it as
128                     #       a sequence achieves the same goal
129                     continue
130
131                 result.append((prop, value, pos, end))
132     return result
133
134
135 property_synonyms = { 'DVD': [ 'DVDRip', 'VIDEO_TS' ],
136                       'HD-DVD': [ 'HDDVD', 'HDDVDRip' ],
137                       'BluRay': [ 'BDRip', 'BRRip', 'Blu-ray' ],
138                       'DVB': [ 'DVBRip', 'PDTV' ],
139                       'Screener': [ 'DVDSCR' ],
140                       'DivX': [ 'DVDivX' ],
141                       'h264': [ 'x264' ],
142                       '720p': [ '720' ],
143                       'AAC': [ 'He-AAC', 'AAC-He' ],
144                       'Special Edition': [ 'Special' ],
145                       'Collector Edition': [ 'Collector' ],
146                       'Criterion Edition': [ 'Criterion' ],
147                       'Minisode': [ 'Minisodes' ]
148                       }
149
150
151 reverse_synonyms = {}
152 for prop, values in properties.items():
153     for value in values:
154         reverse_synonyms[value.lower()] = value
155
156 for canonical, synonyms in property_synonyms.items():
157     for synonym in synonyms:
158         reverse_synonyms[synonym.lower()] = canonical
159
160 def canonical_form(string):
161     return reverse_synonyms.get(string.lower(), string)