Browse Source

Merge branch 'develop' into kodi-release-date

pull/7256/head
Stefaan Ghysels 7 years ago
committed by GitHub
parent
commit
91bdaa1d90
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .gitignore
  2. 2
      couchpotato/core/downloaders/sabnzbd.py
  3. 15
      couchpotato/core/downloaders/transmission.py
  4. 41
      couchpotato/core/media/_base/providers/torrent/bithdtv.py
  5. 10
      couchpotato/core/media/_base/providers/torrent/passthepopcorn.py
  6. 32
      couchpotato/core/media/_base/providers/torrent/torrentz.py
  7. 4
      couchpotato/core/media/_base/providers/torrent/yts.py
  8. 11
      couchpotato/core/media/movie/_base/static/details.js
  9. 28
      couchpotato/core/media/movie/providers/automation/letterboxd.py
  10. 1
      couchpotato/core/media/movie/providers/metadata/xbmc.py
  11. 4
      couchpotato/core/media/movie/providers/nzb/binsearch.py
  12. 2
      couchpotato/core/media/movie/providers/torrent/alpharatio.py
  13. 8
      couchpotato/core/media/movie/providers/torrent/torrentleech.py
  14. 2
      couchpotato/core/media/movie/providers/torrent/torrentz.py
  15. 1
      couchpotato/static/images/icons/dark/safari.svg
  16. 1
      couchpotato/static/images/icons/safari.svg
  17. 9
      couchpotato/static/scripts/combined.plugins.min.js
  18. 3
      couchpotato/templates/index.html
  19. 18
      libs/subliminal/services/__init__.py
  20. 183
      libs/subliminal/services/subscenter.py

2
.gitignore

@ -14,3 +14,5 @@ nosetests.xml
# Visual Studio
/.vs
.DS_Store

2
couchpotato/core/downloaders/sabnzbd.py

@ -100,7 +100,7 @@ class Sabnzbd(DownloaderBase):
# the version check will work even with wrong api key, so we need the next check as well
sab_data = self.call({
'mode': 'qstatus',
'mode': 'queue',
})
if not sab_data:
return False

15
couchpotato/core/downloaders/transmission.py

@ -143,12 +143,21 @@ class Transmission(DownloaderBase):
log.debug('name=%s / id=%s / downloadDir=%s / hashString=%s / percentDone=%s / status=%s / isStalled=%s / eta=%s / uploadRatio=%s / isFinished=%s / incomplete-dir-enabled=%s / incomplete-dir=%s',
(torrent['name'], torrent['id'], torrent['downloadDir'], torrent['hashString'], torrent['percentDone'], torrent['status'], torrent.get('isStalled', 'N/A'), torrent['eta'], torrent['uploadRatio'], torrent['isFinished'], session['incomplete-dir-enabled'], session['incomplete-dir']))
"""
https://trac.transmissionbt.com/browser/branches/2.8x/libtransmission/transmission.h#L1853
0 = Torrent is stopped
1 = Queued to check files
2 = Checking files
3 = Queued to download
4 = Downloading
5 = Queued to seed
6 = Seeding
"""
status = 'busy'
if torrent.get('isStalled') and not torrent['percentDone'] == 1 and self.conf('stalled_as_failed'):
status = 'failed'
elif torrent['status'] == 0 and torrent['percentDone'] == 1:
status = 'completed'
elif torrent['status'] == 16 and torrent['percentDone'] == 1:
elif torrent['status'] == 0 and torrent['percentDone'] == 1 and torrent['isFinished']:
status = 'completed'
elif torrent['status'] in [5, 6]:
status = 'seeding'

41
couchpotato/core/media/_base/providers/torrent/bithdtv.py

@ -13,9 +13,6 @@ log = CPLog(__name__)
class Base(TorrentProvider):
urls = {
'test': 'https://www.bit-hdtv.com/',
'login': 'https://www.bit-hdtv.com/takelogin.php',
'login_check': 'https://www.bit-hdtv.com/messages.php',
'detail': 'https://www.bit-hdtv.com/details.php?id=%s',
'search': 'https://www.bit-hdtv.com/torrents.php?',
'download': 'https://www.bit-hdtv.com/download.php?id=%s',
@ -31,7 +28,7 @@ class Base(TorrentProvider):
url = "%s&%s" % (self.urls['search'], query)
data = self.getHTMLData(url)
data = self.getHTMLData(url, headers = self.getRequestHeaders())
if data:
# Remove BiT-HDTV's output garbage so outdated BS4 versions successfully parse the HTML
@ -42,11 +39,12 @@ class Base(TorrentProvider):
html = BeautifulSoup(data, 'html.parser')
try:
result_tables = html.find_all('table', attrs = {'width': '750', 'class': ''})
result_tables = html.find_all('table', attrs = {'width': '800', 'class': ''})
if result_tables is None:
return
result_table = result_tables[1]
# Take first result
result_table = result_tables[0]
if result_table is None:
return
@ -72,10 +70,10 @@ class Base(TorrentProvider):
except:
log.error('Failed getting results from %s: %s', (self.getName(), traceback.format_exc()))
def getLoginParams(self):
def getRequestHeaders(self):
cookies = 'h_sl={};h_sp={};h_su={}'.format(self.conf('cookiesettingsl') or '', self.conf('cookiesettingsp') or '', self.conf('cookiesettingsu') or '')
return {
'username': self.conf('username'),
'password': self.conf('password'),
'Cookie': cookies
}
def getMoreInfo(self, item):
@ -87,11 +85,13 @@ class Base(TorrentProvider):
item['description'] = description
return item
def loginSuccess(self, output):
return 'logout.php' in output.lower()
loginCheckSuccess = loginSuccess
def download(self, url = '', nzb_id = ''):
try:
return self.urlopen(url, headers=self.getRequestHeaders())
except:
log.error('Failed getting release from %s: %s', (self.getName(), traceback.format_exc()))
return 'try_next'
config = [{
'name': 'bithdtv',
@ -110,13 +110,22 @@ config = [{
'default': False,
},
{
'name': 'username',
'name': 'cookiesettingsl',
'label': 'Cookies (h_sl)',
'default': '',
'description': 'Cookie h_sl from session',
},
{
'name': 'cookiesettingsp',
'label': 'Cookies (h_sp)',
'default': '',
'description': 'Cookie h_sp from session',
},
{
'name': 'password',
'name': 'cookiesettingsu',
'label': 'Cookies (h_su)',
'default': '',
'type': 'password',
'description': 'Cookie h_su from session',
},
{
'name': 'seed_ratio',

10
couchpotato/core/media/_base/providers/torrent/passthepopcorn.py

@ -73,6 +73,8 @@ class Base(TorrentProvider):
torrentdesc += ' Scene'
if self.conf('prefer_scene'):
torrentscore += 2000
if self.conf('no_scene'):
torrentscore -= 2000
if 'RemasterTitle' in torrent and torrent['RemasterTitle']:
torrentdesc += self.htmlToASCII(' %s' % torrent['RemasterTitle'])
@ -258,6 +260,14 @@ config = [{
'description': 'Favors scene-releases over non-scene releases.'
},
{
'name': 'no_scene',
'advanced': True,
'type': 'bool',
'label': 'Reject scene',
'default': 0,
'description': 'Reject scene-releases over non-scene releases.'
},
{
'name': 'require_approval',
'advanced': True,
'type': 'bool',

32
couchpotato/core/media/_base/providers/torrent/torrentz.py

@ -15,25 +15,19 @@ log = CPLog(__name__)
class Base(TorrentMagnetProvider, RSS):
urls = {
'detail': 'https://torrentz.eu/%s',
'search': 'https://torrentz.eu/feed?q=%s',
'verified_search': 'https://torrentz.eu/feed_verified?q=%s'
'detail': 'https://torrentz2.eu/%s',
'search': 'https://torrentz2.eu/feed?f=%s'
}
http_time_between_calls = 0
def _searchOnTitle(self, title, media, quality, results):
search_url = self.urls['verified_search'] if self.conf('verified_only') else self.urls['search']
search_url = self.urls['search']
# Create search parameters
search_params = self.buildUrl(title, media, quality)
smin = quality.get('size_min')
smax = quality.get('size_max')
if smin and smax:
search_params += ' size %sm - %sm' % (smin, smax)
min_seeds = tryInt(self.conf('minimal_seeds'))
if min_seeds:
search_params += ' seed > %s' % (min_seeds - 1)
@ -52,17 +46,24 @@ class Base(TorrentMagnetProvider, RSS):
magnet = splitString(detail_url, '/')[-1]
magnet_url = 'magnet:?xt=urn:btih:%s&dn=%s&tr=%s' % (magnet.upper(), tryUrlencode(name), tryUrlencode('udp://tracker.openbittorrent.com/announce'))
reg = re.search('Size: (?P<size>\d+) MB Seeds: (?P<seeds>[\d,]+) Peers: (?P<peers>[\d,]+)', six.text_type(description))
reg = re.search('Size: (?P<size>\d+) (?P<unit>[KMG]B) Seeds: (?P<seeds>[\d,]+) Peers: (?P<peers>[\d,]+)', six.text_type(description))
size = reg.group('size')
unit = reg.group('unit')
seeds = reg.group('seeds').replace(',', '')
peers = reg.group('peers').replace(',', '')
multiplier = 1
if unit == 'GB':
multiplier = 1000
elif unit == 'KB':
multiplier = 0
results.append({
'id': magnet,
'name': six.text_type(name),
'url': magnet_url,
'detail_url': detail_url,
'size': tryInt(size),
'size': tryInt(size)*multiplier,
'seeders': tryInt(seeds),
'leechers': tryInt(peers),
})
@ -78,7 +79,7 @@ config = [{
'tab': 'searcher',
'list': 'torrent_providers',
'name': 'Torrentz',
'description': 'Torrentz is a free, fast and powerful meta-search engine. <a href="https://torrentz.eu/" target="_blank">Torrentz</a>',
'description': 'Torrentz.eu was a free, fast and powerful meta-search engine combining results from dozens of search engines, Torrentz2.eu is trying to replace it. <a href="https://torrentz2.eu/" target="_blank">Torrentz2</a>',
'wizard': True,
'icon': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAQklEQVQ4y2NgAALjtJn/ycEMlGiGG0IVAxiwAKzOxaKGARcgxgC8YNSAwWoAzuRMjgsIugqfAUR5CZcBRIcHsWEAADSA96Ig020yAAAAAElFTkSuQmCC',
'options': [
@ -88,13 +89,6 @@ config = [{
'default': True
},
{
'name': 'verified_only',
'type': 'bool',
'default': True,
'advanced': True,
'description': 'Only search verified releases',
},
{
'name': 'minimal_seeds',
'type': 'int',
'default': 1,

4
couchpotato/core/media/_base/providers/torrent/yts.py

@ -11,8 +11,8 @@ class Base(TorrentMagnetProvider):
# Only qualities allowed: 720p/1080p/3D - the rest will fail.
# All YTS.ag torrents are verified
urls = {
'detail': 'https://yts.ag/api#list_movies',
'search': 'https://yts.ag/api/v2/list_movies.json?query_term=%s&limit=%s&page=%s'
'detail': 'https://yts.am/api#list_movies',
'search': 'https://yts.am/api/v2/list_movies.json?query_term=%s&limit=%s&page=%s'
}
def _search(self, movie, quality, results):

11
couchpotato/core/media/movie/_base/static/details.js

@ -77,7 +77,6 @@ var MovieDetails = new Class({
'class': parent.get('title') == t ? 'icon-ok' : ''
}));
});
},
addSection: function(name, section_el){
@ -101,7 +100,7 @@ var MovieDetails = new Class({
var self = this;
self.el.addClass('show');
document.onkeyup = self.keyup.bind(self);
//if(!App.mobile_screen){
// $(self.content).getElements('> .head, > .section').each(function(section, nr){
// dynamics.css(section, {
@ -130,12 +129,19 @@ var MovieDetails = new Class({
},
keyup: function(e) {
if (e.keyCode == 27 /* Esc */) {
this.close();
}
},
close: function(){
var self = this;
var ended = function() {
self.el.dispose();
self.overlay.removeEventListener('transitionend', ended);
document.onkeyup = null;
};
self.overlay.addEventListener('transitionend', ended, false);
@ -165,5 +171,4 @@ var MovieDetails = new Class({
App.removeEvent('history.push', self.outer_click);
}
});

28
couchpotato/core/media/movie/providers/automation/letterboxd.py

@ -13,7 +13,7 @@ autoload = 'Letterboxd'
class Letterboxd(Automation):
url = 'http://letterboxd.com/%s/watchlist/'
url = 'http://letterboxd.com/%s/watchlist/page/%d/'
pattern = re.compile(r'(.*)\((\d*)\)')
interval = 1800
@ -46,18 +46,30 @@ class Letterboxd(Automation):
if not enablers[index]:
continue
soup = BeautifulSoup(self.getHTMLData(self.url % username))
soup = BeautifulSoup(self.getHTMLData(self.url % (username, 1)))
for movie in soup.find_all('li', attrs = {'class': 'poster-container'}):
img = movie.find('img', movie)
title = img.get('alt')
pagination = soup.find_all('li', attrs={'class': 'paginate-page'})
number_of_pages = tryInt(pagination[-1].find('a').get_text()) if pagination else 1
pages = range(1, number_of_pages)
movies.append({
'title': title
})
for page in pages:
soup = BeautifulSoup(self.getHTMLData(self.url % (username, page)))
movies += self.getMoviesFromHTML(soup)
return movies
def getMoviesFromHTML(self, html):
movies = []
for movie in html.find_all('li', attrs={'class': 'poster-container'}):
img = movie.find('img')
title = img.get('alt')
movies.append({
'title': title
})
return movies
config = [{
'name': 'letterboxd',

1
couchpotato/core/media/movie/providers/metadata/xbmc.py

@ -3,6 +3,7 @@ import os
import re
import traceback
import xml.dom.minidom
import time
from couchpotato.core.media.movie.providers.metadata.base import MovieMetaData
from couchpotato.core.helpers.encoding import toUnicode

4
couchpotato/core/media/movie/providers/nzb/binsearch.py

@ -21,7 +21,7 @@ class BinSearch(MovieProvider, Base):
'adv_sort': 'date',
'adv_col': 'on',
'adv_nfo': 'on',
'minsize': quality.get('size_min'),
'maxsize': quality.get('size_max'),
'xminsize': quality.get('size_min'),
'xmaxsize': quality.get('size_max'),
})
return query

2
couchpotato/core/media/movie/providers/torrent/alpharatio.py

@ -19,7 +19,7 @@ class AlphaRatio(MovieProvider, Base):
cat_ids = [
([7, 9], ['bd50']),
([7, 9], ['720p', '1080p']),
([7, 9], ['720p', '1080p', '2160p']),
([6, 8], ['dvdr']),
([6, 8], ['brrip', 'dvdrip']),
]

8
couchpotato/core/media/movie/providers/torrent/torrentleech.py

@ -11,12 +11,14 @@ autoload = 'TorrentLeech'
class TorrentLeech(MovieProvider, Base):
cat_ids = [
([13], ['720p', '1080p', 'bd50']),
([41, 47], ['2160p']),
([13, 14, 37, 43], ['720p', '1080p']),
([13], ['bd50']),
([8], ['cam']),
([9], ['ts', 'tc']),
([10], ['r5', 'scr']),
([10, 11, 37], ['r5', 'scr']),
([11], ['dvdrip']),
([13, 14], ['brrip']),
([13, 14, 37, 43], ['brrip']),
([12], ['dvdr']),
]

2
couchpotato/core/media/movie/providers/torrent/torrentz.py

@ -11,4 +11,4 @@ autoload = 'Torrentz'
class Torrentz(MovieProvider, Base):
def buildUrl(self, title, media, quality):
return tryUrlencode('"%s %s"' % (title, media['info']['year']))
return tryUrlencode('%s %s' % (title, media['info']['year']))

1
couchpotato/static/images/icons/dark/safari.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 16 16"><g><path d="m5.3009374 1.8040626c-3.8007467 0-5.06281241 4.9177307-5.06281241 7.5434374 0 2.6112 1.03020071 3.568437 3.17718751 3.568437 1.3201067 0 3.3655217-0.754232 4.134375-2.698125l-0.3337501-0.145c-0.6673065 0.899413-1.6972299 1.450625-2.8287499 1.450625-1.3926401 0-1.7409374-0.928511-1.7409376-2.2631245 0-2.9593602 1.4217667-6.7746875 3.0175002-6.7746875 0.6527998 0 0.8849998 0.4207867 0.885 0.885 0 0.5512532-0.3193402 1.1606075-0.6675002 1.3346875C6.1568766 5.0244592 6.4760182 5.0825 6.708125 5.0825c0.2872515 0 0.5038558-0.086278 0.6621875-0.235625-0.080472 0.2391332-0.1240626 0.4831773-0.1240626 0.72875 0 1.04448 0.5512275 1.4071875 1.5521875 1.4071875-0.014507-0.13056-0.2031249-0.2467224-0.2031249-0.9865626 0-2.2050133 1.4074195-3.5249998 3.7574995-3.525 1.276588 0 1.798751 0.8993192 1.798751 1.9728127 0 1.5522132-1.073578 3.4526966-2.538751 3.5687499l1.03-4.8453125-2.146875 0.2903125-1.9731245 9.2262505h2.0890625l0.885-4.1343755h0.0725c2.6112 0 4.1925-2.0454657 4.1925-3.8878125 0-1.4941867-1.044656-2.8578124-3.42375-2.8578124-1.828958 0-3.7977012 1.0501832-4.653125 2.395 0.02578-0.1395922 0.03875-0.2906221 0.03875-0.4512501 0-1.4071467-1.0591859-1.9437499-2.4228126-1.9437499z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

1
couchpotato/static/images/icons/safari.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 16 16"><g><path d="m5.3009374 1.8040626c-3.8007467 0-5.06281241 4.9177307-5.06281241 7.5434374 0 2.6112 1.03020071 3.568437 3.17718751 3.568437 1.3201067 0 3.3655217-0.754232 4.134375-2.698125l-0.3337501-0.145c-0.6673065 0.899413-1.6972299 1.450625-2.8287499 1.450625-1.3926401 0-1.7409374-0.928511-1.7409376-2.2631245 0-2.9593602 1.4217667-6.7746875 3.0175002-6.7746875 0.6527998 0 0.8849998 0.4207867 0.885 0.885 0 0.5512532-0.3193402 1.1606075-0.6675002 1.3346875C6.1568766 5.0244592 6.4760182 5.0825 6.708125 5.0825c0.2872515 0 0.5038558-0.086278 0.6621875-0.235625-0.080472 0.2391332-0.1240626 0.4831773-0.1240626 0.72875 0 1.04448 0.5512275 1.4071875 1.5521875 1.4071875-0.014507-0.13056-0.2031249-0.2467224-0.2031249-0.9865626 0-2.2050133 1.4074195-3.5249998 3.7574995-3.525 1.276588 0 1.798751 0.8993192 1.798751 1.9728127 0 1.5522132-1.073578 3.4526966-2.538751 3.5687499l1.03-4.8453125-2.146875 0.2903125-1.9731245 9.2262505h2.0890625l0.885-4.1343755h0.0725c2.6112 0 4.1925-2.0454657 4.1925-3.8878125 0-1.4941867-1.044656-2.8578124-3.42375-2.8578124-1.828958 0-3.7977012 1.0501832-4.653125 2.395 0.02578-0.1395922 0.03875-0.2906221 0.03875-0.4512501 0-1.4071467-1.0591859-1.9437499-2.4228126-1.9437499z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

9
couchpotato/static/scripts/combined.plugins.min.js

@ -382,16 +382,23 @@ var MovieDetails = new Class({
open: function() {
var self = this;
self.el.addClass("show");
document.onkeyup = self.keyup.bind(self);
self.outer_click = function() {
self.close();
};
App.addEvent("history.push", self.outer_click);
},
keyup: function(e) {
if (e.keyCode == 27) {
this.close();
}
},
close: function() {
var self = this;
var ended = function() {
self.el.dispose();
self.overlay.removeEventListener("transitionend", ended);
document.onkeyup = null;
};
self.overlay.addEventListener("transitionend", ended, false);
self.el.removeClass("show");
@ -3416,7 +3423,7 @@ var QualityBase = new Class({
try {
return this.qualities.filter(function(q) {
return q.identifier == identifier;
}).pick();
}).pick() || {};
} catch (e) {}
return {};
},

3
couchpotato/templates/index.html

@ -15,6 +15,9 @@
<!-- IOS -->
<link rel="apple-touch-icon-precomposed" href="{{ themed_icon_path }}ios.png" />
<!-- Safari Pinned Tab Icon -->
<link rel="mask-icon" href="{{ themed_icon_path }}safari.svg" color="#AC0000">
<!-- Android -->
<link rel="icon" type="image/png" href="{{ themed_icon_path }}android.png" sizes="192x192">

18
libs/subliminal/services/__init__.py

@ -183,16 +183,21 @@ class ServiceBase(object):
return False
return True
def download_file(self, url, filepath):
def download_file(self, url, filepath, data=None):
"""Attempt to download a file and remove it in case of failure
:param string url: URL to download
:param string filepath: destination path
:param string data: data to add to the post request
"""
logger.info(u'Downloading %s in %s' % (url, filepath))
try:
r = self.session.get(url, timeout = 10, headers = {'Referer': url, 'User-Agent': self.user_agent})
headers = {'Referer': url, 'User-Agent': self.user_agent}
if data:
r = self.session.post(url, data=data, timeout=10, headers=headers)
else:
r = self.session.get(url, timeout=10, headers=headers)
with open(filepath, 'wb') as f:
f.write(r.content)
except Exception as e:
@ -202,18 +207,23 @@ class ServiceBase(object):
raise DownloadFailedError(str(e))
logger.debug(u'Download finished')
def download_zip_file(self, url, filepath):
def download_zip_file(self, url, filepath, data=None):
"""Attempt to download a zip file and extract any subtitle file from it, if any.
This cleans up after itself if anything fails.
:param string url: URL of the zip file to download
:param string filepath: destination path for the subtitle
:param string data: data to add to the post request
"""
logger.info(u'Downloading %s in %s' % (url, filepath))
try:
zippath = filepath + '.zip'
r = self.session.get(url, timeout = 10, headers = {'Referer': url, 'User-Agent': self.user_agent})
headers = {'Referer': url, 'User-Agent': self.user_agent}
if data:
r = self.session.post(url, data=data, timeout=10, headers=headers)
else:
r = self.session.get(url, timeout=10, headers=headers)
with open(zippath, 'wb') as f:
f.write(r.content)
if not zipfile.is_zipfile(zippath):

183
libs/subliminal/services/subscenter.py

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

Loading…
Cancel
Save