summaryrefslogtreecommitdiffhomepage
path: root/libs/subliminal_patch/providers/betaseries.py
blob: a11179406e61e3db153b17c69d0d265276cdb983 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import logging
import os
import io
import rarfile
import zipfile

from guessit import guessit
from requests import Session

from subliminal import Episode
from subliminal.exceptions import AuthenticationError, ConfigurationError
from subliminal_patch.subtitle import Subtitle, guess_matches
from subliminal.subtitle import fix_line_ending, SUBTITLE_EXTENSIONS
from subliminal_patch.providers import Provider
from subzero.language import Language

logger = logging.getLogger(__name__)

server_url = 'https://api.betaseries.com/'


class BetaSeriesSubtitle(Subtitle):
    provider_name = 'betaseries'

    def __init__(self, subtitle_id, language, video_name, url, matches, source, video_release_group):
        super(BetaSeriesSubtitle, self).__init__(language, page_link=url)
        self.subtitle_id = subtitle_id
        self.video_name = video_name
        self.download_url = url
        self.matches = matches
        self.source = source
        self.video_release_group = video_release_group
        self.release_info = video_name

    @property
    def id(self):
        return self.subtitle_id

    @property
    def download_link(self):
        return self.download_url

    def get_matches(self, video):
        matches = self.matches

        if isinstance(video, Episode):
            matches |= guess_matches(video, guessit(
                self.video_name, {'type': 'episode'}), partial=True)

        return matches


class BetaSeriesProvider(Provider):
    """BetaSeries Provider"""
    languages = {Language(l) for l in ['fra', 'eng']}
    video_types = (Episode,)

    def __init__(self, token=None):
        if not token:
            raise ConfigurationError('Token must be specified')
        self.token = token
        self.video = None

    def initialize(self):
        self.session = Session()
        self.session.headers = {
            'User-Agent': os.environ.get("SZ_USER_AGENT", "Sub-Zero/2")}

    def terminate(self):
        self.session.close()

    def query(self, languages, video):
        # query the server
        self.video = video
        matches = set()
        if video.tvdb_id:
            params = {'key': self.token,
                      'thetvdb_id': video.tvdb_id,
                      'v': 3.0,
                      'subtitles': 1}
            logger.debug('Searching subtitles %r', params)
            res = self.session.get(
                server_url + 'episodes/display', params=params, timeout=10)
            res.raise_for_status()
            result = res.json()
            matches.add('tvdb_id')
        elif video.series_tvdb_id:
            params = {'key': self.token,
                      'thetvdb_id': video.series_tvdb_id,
                      'season': video.season,
                      'episode': video.episode,
                      'subtitles': 1,
                      'v': 3.0}
            logger.debug('Searching subtitles %r', params)
            res = self.session.get(
                server_url + 'shows/episodes', params=params, timeout=10)
            if res.status_code == 400:
                raise AuthenticationError("Invalid token provided")
            res.raise_for_status()
            result = res.json()
            matches.add('series_tvdb_id')
        else:
            logger.debug(
                'The show has no tvdb_id and series_tvdb_id: the search can\'t be done')
            return []

        if result['errors'] != []:
            logger.debug('Status error: %r', result['errors'])
            return []

        # parse the subtitles
        subtitles = []
        if 'episode' in result and 'subtitles' in result['episode']:
            subs = result['episode']['subtitles']
        elif 'episodes' in result and len(result['episodes'] and 'subtitles' in result['episodes'][0]):
            subs = result['episodes'][0]['subtitles']
        else:
            return []

        for sub in subs:
            language = _translateLanguageCodeToLanguage(sub['language'])
            if language in languages:
                # Filter seriessub source because it shut down so the links are all 404
                if str(sub['source']) != 'seriessub':
                    subtitles.append(BetaSeriesSubtitle(
                        sub['id'], language, sub['file'], sub['url'], matches, str(sub['source']),
                        self.video.release_group))

        return subtitles

    def list_subtitles(self, video, languages):
        return self.query(languages, video)

    def download_subtitle(self, subtitle):
        logger.info('Downloading subtitle %r', subtitle)
        r = self.session.get(subtitle.download_link, timeout=10)
        if r.status_code == 404:
            logger.error('Error 404 downloading %r', subtitle)
            return
        else:
            r.raise_for_status()

        archive = _get_archive(r.content)
        if archive:
            subtitle_names = _get_subtitle_names_from_archive(archive)
            subtitle_to_download = _choose_subtitle_with_release_group(subtitle_names, subtitle.video_release_group)
            logger.debug('Subtitle to download: ' + subtitle_to_download)
            subtitle_content = archive.read(subtitle_to_download)
        else:
            subtitle_content = r.content

        if subtitle_content:
            subtitle.content = fix_line_ending(subtitle_content)
        else:
            logger.error('Could not extract subtitle from %r', archive)


def _get_archive(content):
    # open the archive
    archive_stream = io.BytesIO(content)
    archive = None
    if rarfile.is_rarfile(archive_stream):
        logger.debug('Identified rar archive')
        archive = rarfile.RarFile(archive_stream)
    elif zipfile.is_zipfile(archive_stream):
        logger.debug('Identified zip archive')
        archive = zipfile.ZipFile(archive_stream)

    return archive


def _get_subtitle_names_from_archive(archive):
    subtitlesToConsider = []
    for name in archive.namelist():
        # discard hidden files
        if os.path.split(name)[-1].startswith('.'):
            continue

        # discard non-subtitle files
        if not name.lower().endswith(SUBTITLE_EXTENSIONS):
            continue

        subtitlesToConsider.append(name)

    if len(subtitlesToConsider)>0:
        logger.debug('Subtitles in archive: ' + ' '.join(subtitlesToConsider))
        return subtitlesToConsider
    else:
        return None


def _translateLanguageCodeToLanguage(languageCode):
    if languageCode.lower() == 'vo':
        return Language.fromalpha2('en')
    elif languageCode.lower() == 'vf':
        return Language.fromalpha2('fr')


def _choose_subtitle_with_release_group(subtitle_names, release_group):
    if release_group:
        for subtitle in subtitle_names:
            if release_group in subtitle:
                return subtitle
    return subtitle_names[0]