1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
10
import chardet as charade
12
from . import Provider
13
from .. import __version__
14
from ..exceptions import ProviderError, ProviderNotAvailable, InvalidSubtitle
15
from ..subtitle import Subtitle, is_valid_subtitle, compute_guess_matches
16
from ..video import Episode, Movie
19
logger = logging.getLogger(__name__)
22
class OpenSubtitlesSubtitle(Subtitle):
23
provider_name = 'opensubtitles'
24
series_re = re.compile('^"(?P<series_name>.*)" (?P<series_title>.*)$')
26
def __init__(self, language, hearing_impaired, id, matched_by, movie_kind, hash, movie_name, movie_release_name, # @ReservedAssignment
27
movie_year, movie_imdb_id, series_season, series_episode):
28
super(OpenSubtitlesSubtitle, self).__init__(language, hearing_impaired)
30
self.matched_by = matched_by
31
self.movie_kind = movie_kind
33
self.movie_name = movie_name
34
self.movie_release_name = movie_release_name
35
self.movie_year = movie_year
36
self.movie_imdb_id = movie_imdb_id
37
self.series_season = series_season
38
self.series_episode = series_episode
41
def series_name(self):
42
return self.series_re.match(self.movie_name).group('series_name')
45
def series_title(self):
46
return self.series_re.match(self.movie_name).group('series_title')
48
def compute_matches(self, video):
51
if isinstance(video, Episode) and self.movie_kind == 'episode':
53
if video.series and self.series_name.lower() == video.series.lower():
56
if video.season and self.series_season == video.season:
59
if video.episode and self.series_episode == video.episode:
60
matches.add('episode')
62
matches |= compute_guess_matches(video, guessit.guess_episode_info(self.movie_release_name + '.mkv'))
64
elif isinstance(video, Movie) and self.movie_kind == 'movie':
66
if video.year and self.movie_year == video.year:
69
matches |= compute_guess_matches(video, guessit.guess_movie_info(self.movie_release_name + '.mkv'))
71
logger.info('%r is not a valid movie_kind for %r', self.movie_kind, video)
74
if 'opensubtitles' in video.hashes and self.hash == video.hashes['opensubtitles']:
77
if video.imdb_id and self.movie_imdb_id == video.imdb_id:
78
matches.add('imdb_id')
80
if video.title and self.movie_name.lower() == video.title.lower():
85
class OpenSubtitlesProvider(Provider):
86
languages = {babelfish.Language.fromopensubtitles(l) for l in babelfish.get_language_converter('opensubtitles').codes}
89
self.server = xmlrpclib.ServerProxy('http://api.opensubtitles.org/xml-rpc')
94
response = self.server.LogIn('', '', 'eng', 'subliminal v%s' % __version__)
95
except xmlrpclib.ProtocolError:
96
raise ProviderNotAvailable
97
if response['status'] != '200 OK':
98
raise ProviderError('Login failed with status %r' % response['status'])
99
self.token = response['token']
103
response = self.server.LogOut(self.token)
104
except xmlrpclib.ProtocolError:
105
raise ProviderNotAvailable
106
if response['status'] != '200 OK':
107
raise ProviderError('Logout failed with status %r' % response['status'])
109
def query(self, languages, hash=None, size=None, imdb_id=None, query=None): # @ReservedAssignment
112
searches.append({'moviehash': hash, 'moviebytesize': str(size)})
114
searches.append({'imdbid': imdb_id})
116
searches.append({'query': query})
118
raise ValueError('One or more parameter missing')
119
for search in searches:
120
search['sublanguageid'] = ','.join(l.opensubtitles for l in languages)
121
logger.debug('Searching subtitles %r', searches)
123
response = self.server.SearchSubtitles(self.token, searches)
124
except xmlrpclib.ProtocolError:
125
raise ProviderNotAvailable
126
if response['status'] != '200 OK':
127
raise ProviderError('Search failed with status %r' % response['status'])
128
if not response['data']:
129
logger.debug('No subtitle found')
131
return [OpenSubtitlesSubtitle(babelfish.Language.fromopensubtitles(r['SubLanguageID']),
132
bool(int(r['SubHearingImpaired'])), r['IDSubtitleFile'], r['MatchedBy'],
133
r['MovieKind'], r['MovieHash'], r['MovieName'], r['MovieReleaseName'],
134
int(r['MovieYear']) if r['MovieYear'] else None, int(r['IDMovieImdb']),
135
int(r['SeriesSeason']) if r['SeriesSeason'] else None,
136
int(r['SeriesEpisode']) if r['SeriesEpisode'] else None)
137
for r in response['data']]
139
def list_subtitles(self, video, languages):
141
if ('opensubtitles' not in video.hashes or not video.size) and not video.imdb_id:
142
query = video.name.split(os.sep)[-1]
143
return self.query(languages, hash=video.hashes.get('opensubtitles'), size=video.size, imdb_id=video.imdb_id,
146
def download_subtitle(self, subtitle):
148
response = self.server.DownloadSubtitles(self.token, [subtitle.id])
149
except xmlrpclib.ProtocolError:
150
raise ProviderNotAvailable
151
if response['status'] != '200 OK':
152
raise ProviderError('Download failed with status %r' % response['status'])
153
if not response['data']:
154
raise ProviderError('Nothing to download')
155
subtitle_bytes = zlib.decompress(base64.b64decode(response['data'][0]['data']), 47)
156
subtitle_text = subtitle_bytes.decode(charade.detect(subtitle_bytes)['encoding'], 'replace')
157
if not is_valid_subtitle(subtitle_text):
158
raise InvalidSubtitle