|
@ -16,105 +16,127 @@ |
|
|
# You should have received a copy of the GNU Lesser General Public License |
|
|
# You should have received a copy of the GNU Lesser General Public License |
|
|
# along with subliminal. If not, see <http://www.gnu.org/licenses/>. |
|
|
# along with subliminal. If not, see <http://www.gnu.org/licenses/>. |
|
|
from . import ServiceBase |
|
|
from . import ServiceBase |
|
|
from ..exceptions import DownloadFailedError, ServiceError |
|
|
from ..exceptions import ServiceError |
|
|
from ..language import language_set |
|
|
from ..language import language_set |
|
|
from ..subtitles import get_subtitle_path, ResultSubtitle |
|
|
from ..subtitles import get_subtitle_path, ResultSubtitle |
|
|
from ..videos import Episode, Movie |
|
|
from ..videos import Episode, Movie |
|
|
from ..utils import to_unicode, get_keywords |
|
|
from ..utils import to_unicode |
|
|
from bs4 import BeautifulSoup |
|
|
|
|
|
import bisect |
|
|
import bisect |
|
|
import json |
|
|
|
|
|
import logging |
|
|
import logging |
|
|
|
|
|
|
|
|
|
|
|
from urllib import urlencode |
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__) |
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Subscenter(ServiceBase): |
|
|
class Subscenter(ServiceBase): |
|
|
server = 'http://www.subscenter.info/he/' |
|
|
server = 'http://www.cinemast.org/he/cinemast/api/' |
|
|
api_based = False |
|
|
api_based = True |
|
|
languages = language_set(['he']) |
|
|
languages = language_set(['he']) |
|
|
videos = [Episode, Movie] |
|
|
videos = [Episode, Movie] |
|
|
require_video = False |
|
|
require_video = False |
|
|
|
|
|
|
|
|
def _search_url_title(self, title, kind): |
|
|
default_username = 'subliminal@gmail.com' |
|
|
"""Search the URL title for the given `title`. |
|
|
default_password = 'subliminal' |
|
|
|
|
|
|
|
|
:param str title: title to search for. |
|
|
|
|
|
:param str kind: kind of the title, ``movie`` or ``series``. |
|
|
|
|
|
:return: the URL version of the title. |
|
|
|
|
|
:rtype: str or None |
|
|
|
|
|
""" |
|
|
|
|
|
# make the search |
|
|
|
|
|
logger.info('Searching title name for %r', title) |
|
|
|
|
|
r = self.session.get(self.server + 'subtitle/search/', params={'q': title}, allow_redirects=False, timeout=10) |
|
|
|
|
|
r.raise_for_status() |
|
|
|
|
|
|
|
|
|
|
|
# if redirected, get the url title from the Location header |
|
|
def __init__(self, config=None): |
|
|
if r.is_redirect: |
|
|
super(Subscenter, self).__init__(config) |
|
|
parts = r.headers['Location'].split('/') |
|
|
self.token = None |
|
|
|
|
|
self.user_id = None |
|
|
|
|
|
|
|
|
# check kind |
|
|
def init(self): |
|
|
if parts[-3] == kind: |
|
|
super(Subscenter, self).init() |
|
|
return parts[-2] |
|
|
logger.debug('Logging in') |
|
|
|
|
|
url = self.server_url + 'login/' |
|
|
|
|
|
|
|
|
return None |
|
|
# actual login |
|
|
|
|
|
data = {'username': self.default_username, 'password': self.default_password} |
|
|
|
|
|
r = self.session.post(url, data=urlencode(data), allow_redirects=False, timeout=10) |
|
|
|
|
|
|
|
|
# otherwise, get the first valid suggestion |
|
|
if r.status_code != 200: |
|
|
soup = BeautifulSoup(r.content, ['lxml', 'html.parser']) |
|
|
raise ServiceError('Login failed') |
|
|
suggestions = soup.select('#processes div.generalWindowTop a') |
|
|
|
|
|
logger.debug('Found %d suggestions', len(suggestions)) |
|
|
try: |
|
|
for suggestion in suggestions: |
|
|
result = r.json() |
|
|
parts = suggestion.attrs['href'].split('/') |
|
|
if 'token' not in result: |
|
|
|
|
|
raise ServiceError('Login failed') |
|
|
|
|
|
|
|
|
# check kind |
|
|
logger.info('Logged in') |
|
|
if parts[-3] == kind: |
|
|
self.user_id = r.json().get('user') |
|
|
return parts[-2] |
|
|
self.token = r.json().get('token') |
|
|
|
|
|
except ValueError: |
|
|
|
|
|
raise ServiceError('Login failed') |
|
|
|
|
|
|
|
|
|
|
|
def terminate(self): |
|
|
|
|
|
super(Subscenter, self).terminate() |
|
|
|
|
|
if self.token or self.user_id: |
|
|
|
|
|
logger.info('Logged out') |
|
|
|
|
|
self.token = None |
|
|
|
|
|
self.user_id = None |
|
|
|
|
|
|
|
|
def list_checked(self, video, languages): |
|
|
def list_checked(self, video, languages): |
|
|
series = None |
|
|
series = None |
|
|
season = None |
|
|
season = None |
|
|
episode = None |
|
|
episode = None |
|
|
title = video.title |
|
|
title = video.title |
|
|
|
|
|
year = video.year |
|
|
if isinstance(video, Episode): |
|
|
if isinstance(video, Episode): |
|
|
series = video.series |
|
|
series = video.series |
|
|
season = video.season |
|
|
season = video.season |
|
|
episode = video.episode |
|
|
episode = video.episode |
|
|
return self.query(video.path or video.release, languages, get_keywords(video.guess), series, season, |
|
|
return self.query(video.path or video.release, languages, series, season, episode, title, year) |
|
|
episode, title) |
|
|
|
|
|
|
|
|
|
|
|
def query(self, filepath, languages=None, keywords=None, series=None, season=None, episode=None, title=None): |
|
|
def query(self, filepath, languages=None, series=None, season=None, episode=None, title=None, year=None): |
|
|
logger.debug(u'Getting subtitles for {0} season {1} episode {2} with languages {3}'.format( |
|
|
logger.debug(u'Getting subtitles for {0} season {1} episode {2} with languages {3}'.format( |
|
|
series, season, episode, languages)) |
|
|
series, season, episode, languages)) |
|
|
# Set the correct parameters depending on the kind. |
|
|
|
|
|
if series and season and episode: |
|
|
query = { |
|
|
url_series = self._search_url_title(series, 'series') |
|
|
'user': self.user_id, |
|
|
url = self.server + 'cst/data/series/sb/{}/{}/{}/'.format(url_series, season, episode) |
|
|
'token': self.token |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
# episode |
|
|
|
|
|
if season and episode: |
|
|
|
|
|
query['q'] = series |
|
|
|
|
|
query['type'] = 'series' |
|
|
|
|
|
query['season'] = season |
|
|
|
|
|
query['episode'] = episode |
|
|
elif title: |
|
|
elif title: |
|
|
url_title = self._search_url_title(title, 'movie') |
|
|
query['q'] = title |
|
|
url = self.server + 'cst/data/movie/sb/{}/'.format(url_title) |
|
|
query['type'] = 'movies' |
|
|
|
|
|
if year: |
|
|
|
|
|
query['year_start'] = year - 1 |
|
|
|
|
|
query['year_end'] = year |
|
|
else: |
|
|
else: |
|
|
raise ServiceError('One or more parameters are missing') |
|
|
raise ServiceError('One or more parameters are missing') |
|
|
logger.debug('Searching subtitles for title {0}, season {1}, episode {2}'.format(title, season, episode)) |
|
|
|
|
|
response = self.session.get(url) |
|
|
# get the list of subtitles |
|
|
if response.status_code != 200: |
|
|
logger.debug('Getting the list of subtitles') |
|
|
raise ServiceError('Request failed with status code {0}'.format(response.status_code)) |
|
|
url = self.server_url + 'search/' |
|
|
# Loop over results. |
|
|
r = self.session.post(url, data=urlencode(query)) |
|
|
subtitles = dict() |
|
|
r.raise_for_status() |
|
|
response_json = json.loads(response.content) |
|
|
|
|
|
for language_code, language_data in response_json.items(): |
|
|
try: |
|
|
|
|
|
results = r.json() |
|
|
|
|
|
except ValueError: |
|
|
|
|
|
return {} |
|
|
|
|
|
|
|
|
|
|
|
# loop over results |
|
|
|
|
|
subtitles = {} |
|
|
|
|
|
for group_data in results.get('data', []): |
|
|
|
|
|
for language_code, subtitles_data in group_data.get('subtitles', {}).items(): |
|
|
language_object = self.get_language(language_code) |
|
|
language_object = self.get_language(language_code) |
|
|
if language_object in self.languages and language_object in languages: |
|
|
|
|
|
for quality_data in language_data.values(): |
|
|
for subtitle_item in subtitles_data: |
|
|
for quality, subtitles_data in quality_data.items(): |
|
|
# read the item |
|
|
for subtitle_item in subtitles_data.values(): |
|
|
|
|
|
# Read the item. |
|
|
|
|
|
subtitle_id = subtitle_item['id'] |
|
|
subtitle_id = subtitle_item['id'] |
|
|
subtitle_key = subtitle_item['key'] |
|
|
subtitle_key = subtitle_item['key'] |
|
|
subtitle_version = subtitle_item['h_version'] |
|
|
release = subtitle_item['version'] |
|
|
release = subtitle_item['subtitle_version'] |
|
|
|
|
|
subtitle_path = get_subtitle_path(filepath, language_object, self.config.multi) |
|
|
subtitle_path = get_subtitle_path(filepath, language_object, self.config.multi) |
|
|
download_link = self.server_url + 'subtitle/download/{0}/{1}/?v={2}&key={3}'.format( |
|
|
download_link = self.server_url + 'subtitle/download/{0}/?v={1}&key={2}&sub_id={3}'.format( |
|
|
language_code, subtitle_id, subtitle_version, subtitle_key) |
|
|
language_code, release, subtitle_key, subtitle_id) |
|
|
# Add the release and increment downloaded count if we already have the subtitle. |
|
|
# Add the release and increment downloaded count if we already have the subtitle. |
|
|
if subtitle_id in subtitles: |
|
|
if subtitle_id in subtitles: |
|
|
logger.debug('Found additional release {0} for subtitle {1}'.format( |
|
|
logger.debug('Found additional release {0} for subtitle {1}'.format( |
|
@ -126,14 +148,15 @@ class Subscenter(ServiceBase): |
|
|
download_link, release=to_unicode(release)) |
|
|
download_link, release=to_unicode(release)) |
|
|
logger.debug('Found subtitle %r', subtitle) |
|
|
logger.debug('Found subtitle %r', subtitle) |
|
|
subtitles[subtitle_id] = subtitle |
|
|
subtitles[subtitle_id] = subtitle |
|
|
|
|
|
|
|
|
return subtitles.values() |
|
|
return subtitles.values() |
|
|
|
|
|
|
|
|
def download(self, subtitle): |
|
|
def download(self, subtitle): |
|
|
try: |
|
|
data = { |
|
|
self.download_zip_file(subtitle.link, subtitle.path) |
|
|
'user': self.user_id, |
|
|
except DownloadFailedError: |
|
|
'token': self.token |
|
|
# If no zip file was retrieved, daily downloads limit has exceeded. |
|
|
} |
|
|
raise ServiceError('Daily limit exceeded') |
|
|
self.download_zip_file(subtitle.link, subtitle.path, data=urlencode(data)) |
|
|
return subtitle |
|
|
return subtitle |
|
|
|
|
|
|
|
|
|
|
|
|
|
|