Skip to content

Commit

Permalink
make subscenter based on a PR from ofir123 - Diaoul/subliminal#817
Browse files Browse the repository at this point in the history
  • Loading branch information
natizlp committed Jan 25, 2018
1 parent 9df2805 commit f783758
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 120 deletions.
8 changes: 6 additions & 2 deletions Contents/Code/support/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ def get_providers(self):
'legendastv': cast_bool(Prefs['provider.legendastv.enabled']),
'napiprojekt': cast_bool(Prefs['provider.napiprojekt.enabled']),
'shooter': cast_bool(Prefs['provider.shooter.enabled']),
'subscenter': False,
'subscenter': cast_bool(Prefs['provider.subscenter.enabled']),
'wizdom': cast_bool(Prefs['provider.wizdom.enabled']),
}

Expand Down Expand Up @@ -515,7 +515,11 @@ def get_provider_settings(self):
'legendastv': {'username': Prefs['provider.legendastv.username'],
'password': Prefs['provider.legendastv.password'],
},
}
},
'subscenter': {'username': Prefs['provider.subscenter.username'],
'password': Prefs['provider.subscenter.password'],
},
}

return provider_settings

Expand Down
20 changes: 20 additions & 0 deletions Contents/DefaultPrefs.json
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,26 @@
"type": "bool",
"default": "true"
},
{
"id": "provider.subscenter.enabled",
"label": "Provider: Enable Subscenter",
"type": "bool",
"default": "true"
},
{
"id": "provider.subscenter.username",
"label": "Subscenter Username",
"type": "text",
"default": ""
},
{
"id": "provider.subscenter.password",
"label": "Subscenter Password",
"type": "text",
"option": "hidden",
"default": "",
"secure": "true"
},
{
"id": "provider.wizdom.enabled",
"label": "Provider: Enable Wizdom",
Expand Down
213 changes: 96 additions & 117 deletions Contents/Libraries/Shared/subliminal/providers/subscenter.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
# -*- coding: utf-8 -*-
import bisect
from collections import defaultdict
import io
import json
import logging
import zipfile

from babelfish import Language
from guessit import guessit
from requests import Session

from . import ParserBeautifulSoup, Provider

from .. import __short_version__
from ..cache import SHOW_EXPIRATION_TIME, region
from ..exceptions import AuthenticationError, ConfigurationError, ProviderError
from ..subtitle import Subtitle, fix_line_ending, guess_matches
from ..utils import sanitize
Expand Down Expand Up @@ -75,169 +71,152 @@ def get_matches(self, video):
class SubsCenterProvider(Provider):
"""SubsCenter Provider."""
languages = {Language.fromalpha2(l) for l in ['he']}
server_url = 'http://www.subscenter.org/he/'
server_url = 'http://www.cinemast.org/he/cinemast/api/'
subtitle_class = SubsCenterSubtitle

def __init__(self, username=None, password=None):
if username is not None and password is None or username is None and password is not None:
if not (username and password):
raise ConfigurationError('Username and password must be specified')

self.session = None
self.username = username
self.password = password
self.logged_in = False
self.user_id = None
self.token = None
self.session = None

def initialize(self):
self.session = Session()
self.session.headers['User-Agent'] = 'Subliminal/{}'.format(__short_version__)

# login
if self.username is not None and self.password is not None:
logger.debug('Logging in')
url = self.server_url + 'subscenter/accounts/login/'
logger.debug('Logging in')
url = self.server_url + 'login/'

# retrieve CSRF token
self.session.get(url)
csrf_token = self.session.cookies['csrftoken']
# actual login
data = {'username': self.username, 'password': self.password}
r = self.session.post(url, data=data, allow_redirects=False, timeout=10)

# actual login
data = {'username': self.username, 'password': self.password, 'csrfmiddlewaretoken': csrf_token}
r = self.session.post(url, data, allow_redirects=False, timeout=10)
if r.status_code != 200:
raise AuthenticationError(self.username)

if r.status_code != 302:
try:
result = r.json()
if 'token' not in result:
raise AuthenticationError(self.username)

logger.info('Logged in')
self.logged_in = True
self.user_id = r.json().get('user')
self.token = r.json().get('token')
except ValueError:
raise AuthenticationError(self.username)

@staticmethod
def _slugify_title(title):
return title.lower().replace(' ', '-').replace('\'', '').replace('"', '').replace('.', '').replace(';', '')

def terminate(self):
# logout
if self.logged_in:
logger.info('Logging out')
r = self.session.get(self.server_url + 'subscenter/accounts/logout/', timeout=10)
r.raise_for_status()
if self.token or self.user_id:
logger.info('Logged out')
self.logged_in = False
self.token = None
self.user_id = None

self.session.close()

@region.cache_on_arguments(expiration_time=SHOW_EXPIRATION_TIME)
def _search_url_titles(self, title):
"""Search the URL titles by kind for the given `title`.
:param str title: title to search for.
:return: the URL titles by kind.
:rtype: collections.defaultdict
"""
# make the search
logger.info('Searching title name for %r', title)
r = self.session.get(self.server_url + 'subtitle/search/', params={'q': title}, timeout=10)
r.raise_for_status()

# check for redirections
if r.history and all([h.status_code == 302 for h in r.history]):
logger.debug('Redirected to the subtitles page')
links = [r.url]
else:
# get the suggestions (if needed)
soup = ParserBeautifulSoup(r.content, ['lxml', 'html.parser'])
links = [link.attrs['href'] for link in soup.select('#processes div.generalWindowTop a')]
logger.debug('Found %d suggestions', len(links))

url_titles = defaultdict(list)
for link in links:
parts = link.split('/')
url_titles[parts[-3]].append(parts[-2])

return url_titles

def query(self, title, season=None, episode=None):
# search for the url title
url_titles = self._search_url_titles(title)
def query(self, title, season=None, episode=None, year=None):
query = {
'q': title,
'user': self.user_id,
'token': self.token
}

# episode
if season and episode:
if 'series' not in url_titles:
logger.error('No URL title found for series %r', title)
return []
url_title = url_titles['series'][0]
logger.debug('Using series title %r', url_title)
url = self.server_url + 'cst/data/series/sb/{}/{}/{}/'.format(url_title, season, episode)
page_link = self.server_url + 'subtitle/series/{}/{}/{}/'.format(url_title, season, episode)
query['type'] = 'series'
query['season'] = season
query['episode'] = episode
else:
if 'movie' not in url_titles:
logger.error('No URL title found for movie %r', title)
return []
url_title = url_titles['movie'][0]
logger.debug('Using movie title %r', url_title)
url = self.server_url + 'cst/data/movie/sb/{}/'.format(url_title)
page_link = self.server_url + 'subtitle/movie/{}/'.format(url_title)
query['type'] = 'movies'
if year:
query['year_start'] = year - 1
query['year_end'] = year

# get the list of subtitles
logger.debug('Getting the list of subtitles')
r = self.session.get(url)
url = self.server_url + 'search/'
r = self.session.post(url, data=query)
r.raise_for_status()
results = json.loads(r.text)

try:
results = r.json()
except ValueError:
return {}

# loop over results
subtitles = {}
for language_code, language_data in results.items():
for quality_data in language_data.values():
for quality, subtitles_data in quality_data.items():
for subtitle_item in subtitles_data.values():
# read the item
language = Language.fromalpha2(language_code)
hearing_impaired = bool(subtitle_item['hearing_impaired'])
subtitle_id = subtitle_item['id']
subtitle_key = subtitle_item['key']
subtitle_version = subtitle_item['h_version']
downloaded = subtitle_item['downloaded']
release = subtitle_item['subtitle_version']

# add the release and increment downloaded count if we already have the subtitle
if subtitle_id in subtitles:
logger.debug('Found additional release %r for subtitle %d', release, subtitle_id)
bisect.insort_left(subtitles[subtitle_id].releases, release) # deterministic order
subtitles[subtitle_id].downloaded += downloaded
continue

# otherwise create it
subtitle = self.subtitle_class(language, hearing_impaired, page_link, title, season, episode,
title, subtitle_id, subtitle_key, subtitle_version, downloaded,
[release])
logger.debug('Found subtitle %r', subtitle)
subtitles[subtitle_id] = subtitle
for group_data in results.get('data', []):
# create page link
slug_name = self._slugify_title(group_data.get('name_en'))
if query['type'] == 'series':
page_link = self.server_url + 'subtitle/series/{}/{}/{}/'.format(slug_name, season, episode)
else:
page_link = self.server_url + 'subtitle/movie/{}/'.format(slug_name)

# go over each language
for language_code, subtitles_data in group_data.get('subtitles', {}).items():
for subtitle_item in subtitles_data:
# read the item
language = Language.fromalpha2(language_code)
subtitle_id = subtitle_item['id']
subtitle_key = subtitle_item['key']
release = subtitle_item['version']

# otherwise create it
subtitle = self.subtitle_class(language, False, page_link, title, season, episode, title, subtitle_id,
subtitle_key, None, None, [release])
logger.debug('Found subtitle %r', subtitle)
subtitles[subtitle_id] = subtitle

return subtitles.values()

def list_subtitles(self, video, languages):
season = episode = None
title = video.title

if isinstance(video, Episode):
title = video.series
titles = [video.series]
season = video.season
episode = video.episode
else:
titles = [video.title]

for title in titles:
subtitles = [s for s in self.query(title, season, episode) if s.language in languages]
if subtitles:
return subtitles

return [s for s in self.query(title, season, episode) if s.language in languages]
return []

def download_subtitle(self, subtitle):
# download
url = self.server_url + 'subtitle/download/{}/{}/'.format(subtitle.language.alpha2, subtitle.subtitle_id)
params = {'v': subtitle.subtitle_version, 'key': subtitle.subtitle_key}
r = self.session.get(url, params=params, headers={'Referer': subtitle.page_link}, timeout=10)
url = self.server_url + 'subtitle/download/{}/'.format(subtitle.language.alpha2)
params = {
'v': subtitle.release,
'key': subtitle.subtitle_key,
'sub_id': subtitle.subtitle_id
}
data = {
'user': self.user_id,
'token': self.token
}
r = self.session.post(url, data=data, params=params, timeout=10)
r.raise_for_status()

# open the zip
try:
with zipfile.ZipFile(io.BytesIO(r.content)) as zf:
# remove some filenames from the namelist
namelist = [n for n in zf.namelist() if not n.endswith('.txt')]
if len(namelist) > 1:
raise ProviderError('More than one file to unzip')

subtitle.content = fix_line_ending(zf.read(namelist[0]))
except zipfile.BadZipfile:
# if no zip file was retrieved, daily downloads limit has exceeded
raise ProviderError('Daily limit exceeded')
with zipfile.ZipFile(io.BytesIO(r.content)) as zf:
# remove some filenames from the namelist
namelist = [n for n in zf.namelist() if not n.endswith('.txt')]
if len(namelist) > 1:
raise ProviderError('More than one file to unzip')

subtitle.content = fix_line_ending(zf.read(namelist[0]))
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ def __repr__(self):
class SubsCenterProvider(_SubsCenterProvider):
subtitle_class = SubsCenterSubtitle
hearing_impaired_verifiable = True
server_url = 'http://www.subscenter.info/he/'
server_url = 'http://www.cinemast.org/he/cinemast/api/'

0 comments on commit f783758

Please sign in to comment.