Skip to content

Commit

Permalink
Kodi 19 - Python3 Support (#7)
Browse files Browse the repository at this point in the history
* bsplayer code migrated to py3
  • Loading branch information
realgam3 authored May 15, 2022
1 parent 15a1fa3 commit 205c57a
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 95 deletions.
33 changes: 20 additions & 13 deletions addon.xml
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="service.subtitles.bsplayer"
name="BSPlayer"
version="0.2.1"
version="0.3.0"
provider-name="realgam3">
<requires>
<import addon="xbmc.python" version="2.1.0"/>
</requires>
<extension point="xbmc.subtitle.module"
library="service.py" />
<extension point="xbmc.addon.metadata">
<summary lang="en">Kodi / XBMC BSPlayer Subtitles allows for downloading subtitles from the bsplayer subtitles community.</summary>
<description lang="en">Search and download various subtitles from the bsplayer subtitles community.</description>
<source>https://github.com/realgam3/service.subtitles.bsplayer</source>
<email>[email protected]</email>
<license>GNU GENERAL PUBLIC LICENSE. Version 2, June 1991</license>
</extension>
<requires>
<import addon="xbmc.python" version="3.0.0"/>
</requires>
<extension point="xbmc.subtitle.module" library="service.py"/>
<extension point="xbmc.addon.metadata">
<summary lang="en">
BSPlayer Subtitles
</summary>
<description lang="en">
Search and download various subtitles from the BSPlayer subtitles community.
</description>
<source>https://github.com/realgam3/service.subtitles.bsplayer</source>
<email>[email protected]</email>
<license>GNU GENERAL PUBLIC LICENSE. Version 2, June 1991</license>
<platform>all</platform>
<assets>
<icon>logo.png</icon>
</assets>
</extension>
</addon>
3 changes: 3 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
0.3.0 - 15/05/2022
- Migrating to python 3, support kodi 19

0.2.1 - 07/11/2015
- Fixed android non rar support, rearrange code

Expand Down
1 change: 0 additions & 1 deletion resources/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
# Dummy file to make this directory a package.
88 changes: 51 additions & 37 deletions resources/lib/bsplayer.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
import gzip
import socket
import random
from time import sleep
from StringIO import StringIO
from xml.etree import ElementTree
from urllib.request import Request

from utils import movie_size_and_hash, get_session, log

# s1-9, s101-109
SUB_DOMAINS = ['s1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9',
's101', 's102', 's103', 's104', 's105', 's106', 's107', 's108', 's109']
API_URL_TEMPLATE = "http://{sub_domain}.api.bsplayer-subtitles.com/v1.php"


def get_sub_domain():
sub_domains_end = len(SUB_DOMAINS) - 1
return API_URL_TEMPLATE.format(sub_domain=SUB_DOMAINS[random.randint(0, sub_domains_end)])
from .utils import movie_size_and_hash, get_session, log


class BSPlayer(object):
VERSION = "2.67"
DOMAIN = "api.bsplayer-subtitles.com"
SUB_DOMAINS = [
's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8',
's101', 's102', 's103', 's104', 's105', 's106', 's107', 's108', 's109'
]

def __init__(self, search_url=None, proxies=None):
self.session = get_session(proxies=proxies)
self.search_url = search_url or get_sub_domain()
self.search_url = search_url or self.get_sub_domain()
self.token = None

def __enter__(self):
Expand All @@ -30,33 +28,43 @@ def __enter__(self):
def __exit__(self, exc_type, exc_val, exc_tb):
return self.logout()

def get_sub_domain(self, tries=5):
for t in range(tries):
domain = f"{random.choice(self.SUB_DOMAINS)}.{self.DOMAIN}"
try:
socket.gethostbyname(domain)
return f"http://{domain}/v1.php"
except socket.gaierror:
continue
raise Exception("API Domain not found")

def api_request(self, func_name='logIn', params='', tries=5):
headers = {
'User-Agent': 'BSPlayer/2.x (1022.12360)',
'Content-Type': 'text/xml; charset=utf-8',
'Connection': 'close',
'SOAPAction': '"http://api.bsplayer-subtitles.com/v1.php#{func_name}"'.format(func_name=func_name)
'SOAPAction': f'"http://{self.DOMAIN}/v1.php#{func_name}"'
}
data = (
'<?xml version="1.0" encoding="UTF-8"?>\n'
'<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" '
'xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" '
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '
'xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns1="{search_url}">'
f'xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns1="{self.search_url}">'
'<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'
'<ns1:{func_name}>{params}</ns1:{func_name}></SOAP-ENV:Body></SOAP-ENV:Envelope>'
).format(search_url=self.search_url, func_name=func_name, params=params)
f'<ns1:{func_name}>{params}</ns1:{func_name}></SOAP-ENV:Body></SOAP-ENV:Envelope>'
)

log('BSPlayer.api_request', 'Sending request: %s.' % func_name)
for i in xrange(tries):
for i in range(tries):
try:
self.session.addheaders.extend(headers.items())
res = self.session.open(self.search_url, data)
req = Request(self.search_url, data=data.encode(), headers=headers, method="POST")
res = self.session.open(req)
return ElementTree.fromstring(res.read())
except Exception, ex:
except Exception as ex:
log("BSPlayer.api_request", "ERROR: %s." % ex)
if func_name == 'logIn':
self.search_url = get_sub_domain()
self.search_url = self.get_sub_domain()
sleep(1)
log('BSPlayer.api_request', 'ERROR: Too many tries (%d)...' % tries)
raise Exception('Too many tries...')
Expand All @@ -68,9 +76,11 @@ def login(self):

root = self.api_request(
func_name='logIn',
params=('<username></username>'
'<password></password>'
'<AppID>BSPlayer v2.67</AppID>')
params=(
'<username></username>'
'<password></password>'
f'<AppID>BSPlayer v{self.VERSION}</AppID>'
)
)
res = root.find('.//return')
if res.find('status').text == 'OK':
Expand All @@ -86,7 +96,7 @@ def logout(self):

root = self.api_request(
func_name='logOut',
params='<handle>{token}</handle>'.format(token=self.token)
params=f'<handle>{self.token}</handle>'
)
res = root.find('.//return')
self.token = None
Expand All @@ -102,18 +112,21 @@ def search_subtitles(self, movie_path, language_ids='heb,eng', logout=False):
if isinstance(language_ids, (tuple, list, set)):
language_ids = ",".join(language_ids)

movie_size, movie_hash = movie_size_and_hash(movie_path)
log('BSPlayer.search_subtitles', 'Movie Size: %s, Movie Hash: %s.' % (movie_size, movie_hash))
try:
movie_size, movie_hash = movie_size_and_hash(movie_path)
except Exception as ex:
print(ex)
exit(1)
log('BSPlayer.search_subtitles', f'Movie Size: {movie_size}, Movie Hash: {movie_hash}.')
root = self.api_request(
func_name='searchSubtitles',
params=(
'<handle>{token}</handle>'
'<movieHash>{movie_hash}</movieHash>'
'<movieSize>{movie_size}</movieSize>'
'<languageId>{language_ids}</languageId>'
f'<handle>{self.token}</handle>'
f'<movieHash>{movie_hash}</movieHash>'
f'<movieSize>{movie_size}</movieSize>'
f'<languageId>{language_ids}</languageId>'
'<imdbId>*</imdbId>'
).format(token=self.token, movie_hash=movie_hash,
movie_size=movie_size, language_ids=language_ids)
)
)
res = root.find('.//return/result')
if res.find('status').text != 'OK':
Expand All @@ -129,7 +142,8 @@ def search_subtitles(self, movie_path, language_ids='heb,eng', logout=False):
subDownloadLink=item.find('subDownloadLink').text,
subLang=item.find('subLang').text,
subName=item.find('subName').text,
subFormat=item.find('subFormat').text
subFormat=item.find('subFormat').text,
subRating=item.find('subRating').text or '0'
))

if logout:
Expand All @@ -141,10 +155,10 @@ def search_subtitles(self, movie_path, language_ids='heb,eng', logout=False):
def download_subtitles(download_url, dest_path, proxies=None):
session = get_session(proxies=proxies, http_10=True)
session.addheaders = [('User-Agent', 'Mozilla/4.0 (compatible; Synapse)'),
('Content-Length', 0)]
('Content-Length', 0)]
res = session.open(download_url)
if res:
gf = gzip.GzipFile(fileobj=StringIO(res.read()))
gf = gzip.GzipFile(fileobj=res)
with open(dest_path, 'wb') as f:
f.write(gf.read())
f.flush()
Expand Down
40 changes: 19 additions & 21 deletions resources/lib/utils.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
import sys
import struct
import urllib2
import urlparse
import cookielib
from os import path
from httplib import HTTPConnection
from urllib import parse, request
from http.cookiejar import CookieJar
from http.client import HTTPConnection

import xbmc
import xbmcvfs


def log(module, msg):
xbmc.log((u"### [%s] - %s" % (module, msg)).encode('utf-8'), level=xbmc.LOGDEBUG)
xbmc.log(f"### [{module}] - {msg}", level=xbmc.LOGDEBUG)


def notify(script_name, language, string_id):
xbmc.executebuiltin((u'Notification(%s,%s)' % (script_name, language(string_id))).encode('utf-8'))
xbmc.executebuiltin(f"Notification({script_name}, {language(string_id)})")


def get_params(params_str=""):
params_str = params_str or sys.argv[2]
return dict(urlparse.parse_qsl(params_str.lstrip('?')))
return dict(parse.parse_qsl(params_str.lstrip('?')))


def get_video_path(xbmc_path=''):
xbmc_path = xbmc_path or urlparse.unquote(xbmc.Player().getPlayingFile().decode('utf-8'))
xbmc_path = xbmc_path or parse.unquote(xbmc.Player().getPlayingFile())
if xbmc_path.startswith('rar://'):
return path.dirname(xbmc_path.replace('rar://', ''))
elif xbmc_path.startswith('stack://'):
Expand All @@ -50,21 +48,21 @@ class HTTP10Connection(HTTPConnection):
_http_vsn_str = "HTTP/1.0"


class HTTP10Handler(urllib2.HTTPHandler):
class HTTP10Handler(request.HTTPHandler):
def http_open(self, req):
return self.do_open(HTTP10Connection, req)


def get_session(proxies=None, cookies=True, http_10=False):
handlers = []
if proxies:
handlers.append(urllib2.ProxyHandler(proxies))
handlers.append(request.ProxyHandler(proxies))
if cookies:
cj = cookielib.CookieJar()
handlers.append(urllib2.HTTPCookieProcessor(cj))
cj = CookieJar()
handlers.append(request.HTTPCookieProcessor(cj))
if http_10:
handlers.append(HTTP10Handler)
return urllib2.build_opener(*handlers)
return request.build_opener(*handlers)


def __get_last_split(firs_rar_file, x):
Expand All @@ -78,7 +76,7 @@ def __get_last_split(firs_rar_file, x):


def __add_file_hash(name, file_hash, seek):
f = xbmcvfs.File(name)
f = open(name, "rb")
f.seek(max(0, seek), 0)
for i in range(8192):
file_hash += struct.unpack('<q', f.read(8))[0]
Expand All @@ -89,9 +87,9 @@ def __add_file_hash(name, file_hash, seek):

def __movie_size_and_hash_rar(firs_rar_file):
log('utils.movie_size_and_hash', 'Hashing Rar file...')
f = xbmcvfs.File(firs_rar_file)
f = open(firs_rar_file, 'rb')
a = f.read(4)
if a != 'Rar!':
if a != b'Rar!':
log('utils.movie_size_and_hash', 'ERROR: This is not rar file (%s).' % path.basename(firs_rar_file))
raise Exception('ERROR: This is not rar file.')
seek = 0
Expand Down Expand Up @@ -129,23 +127,23 @@ def movie_size_and_hash(file_path):
longlong_format = '<q' # little-endian long long
byte_size = struct.calcsize(longlong_format)

f = xbmcvfs.File(file_path)
file_size = f.size()
file_size = path.getsize(file_path)
f = open(file_path, 'rb')
movie_hash = file_size

if file_size < 65536 * 2:
f.close()
log('utils.movie_size_and_hash', "ERROR: SizeError (%d)." % file_size)
raise Exception("SizeError")

for x in range(65536 / byte_size):
for x in range(65536 // byte_size):
buff = f.read(byte_size)
(l_value,) = struct.unpack(longlong_format, buff)
movie_hash += l_value
movie_hash &= 0xFFFFFFFFFFFFFFFF # to remain as 64bit number

f.seek(max(0, file_size - 65536), 0)
for x in range(65536 / byte_size):
for x in range(65536 // byte_size):
buff = f.read(byte_size)
(l_value,) = struct.unpack(longlong_format, buff)
movie_hash += l_value
Expand Down
Loading

0 comments on commit 205c57a

Please sign in to comment.