Skip to content

Commit 205c57a

Browse files
authored
Kodi 19 - Python3 Support (#7)
* bsplayer code migrated to py3
1 parent 15a1fa3 commit 205c57a

File tree

6 files changed

+117
-95
lines changed

6 files changed

+117
-95
lines changed

addon.xml

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
22
<addon id="service.subtitles.bsplayer"
33
name="BSPlayer"
4-
version="0.2.1"
4+
version="0.3.0"
55
provider-name="realgam3">
6-
<requires>
7-
<import addon="xbmc.python" version="2.1.0"/>
8-
</requires>
9-
<extension point="xbmc.subtitle.module"
10-
library="service.py" />
11-
<extension point="xbmc.addon.metadata">
12-
<summary lang="en">Kodi / XBMC BSPlayer Subtitles allows for downloading subtitles from the bsplayer subtitles community.</summary>
13-
<description lang="en">Search and download various subtitles from the bsplayer subtitles community.</description>
14-
<source>https://github.com/realgam3/service.subtitles.bsplayer</source>
15-
<email>[email protected]</email>
16-
<license>GNU GENERAL PUBLIC LICENSE. Version 2, June 1991</license>
17-
</extension>
6+
<requires>
7+
<import addon="xbmc.python" version="3.0.0"/>
8+
</requires>
9+
<extension point="xbmc.subtitle.module" library="service.py"/>
10+
<extension point="xbmc.addon.metadata">
11+
<summary lang="en">
12+
BSPlayer Subtitles
13+
</summary>
14+
<description lang="en">
15+
Search and download various subtitles from the BSPlayer subtitles community.
16+
</description>
17+
<source>https://github.com/realgam3/service.subtitles.bsplayer</source>
18+
<email>[email protected]</email>
19+
<license>GNU GENERAL PUBLIC LICENSE. Version 2, June 1991</license>
20+
<platform>all</platform>
21+
<assets>
22+
<icon>logo.png</icon>
23+
</assets>
24+
</extension>
1825
</addon>

changelog.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
0.3.0 - 15/05/2022
2+
- Migrating to python 3, support kodi 19
3+
14
0.2.1 - 07/11/2015
25
- Fixed android non rar support, rearrange code
36

resources/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +0,0 @@
1-
# Dummy file to make this directory a package.

resources/lib/bsplayer.py

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,24 @@
11
import gzip
2+
import socket
23
import random
34
from time import sleep
4-
from StringIO import StringIO
55
from xml.etree import ElementTree
6+
from urllib.request import Request
67

7-
from utils import movie_size_and_hash, get_session, log
8-
9-
# s1-9, s101-109
10-
SUB_DOMAINS = ['s1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9',
11-
's101', 's102', 's103', 's104', 's105', 's106', 's107', 's108', 's109']
12-
API_URL_TEMPLATE = "http://{sub_domain}.api.bsplayer-subtitles.com/v1.php"
13-
14-
15-
def get_sub_domain():
16-
sub_domains_end = len(SUB_DOMAINS) - 1
17-
return API_URL_TEMPLATE.format(sub_domain=SUB_DOMAINS[random.randint(0, sub_domains_end)])
8+
from .utils import movie_size_and_hash, get_session, log
189

1910

2011
class BSPlayer(object):
12+
VERSION = "2.67"
13+
DOMAIN = "api.bsplayer-subtitles.com"
14+
SUB_DOMAINS = [
15+
's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8',
16+
's101', 's102', 's103', 's104', 's105', 's106', 's107', 's108', 's109'
17+
]
18+
2119
def __init__(self, search_url=None, proxies=None):
2220
self.session = get_session(proxies=proxies)
23-
self.search_url = search_url or get_sub_domain()
21+
self.search_url = search_url or self.get_sub_domain()
2422
self.token = None
2523

2624
def __enter__(self):
@@ -30,33 +28,43 @@ def __enter__(self):
3028
def __exit__(self, exc_type, exc_val, exc_tb):
3129
return self.logout()
3230

31+
def get_sub_domain(self, tries=5):
32+
for t in range(tries):
33+
domain = f"{random.choice(self.SUB_DOMAINS)}.{self.DOMAIN}"
34+
try:
35+
socket.gethostbyname(domain)
36+
return f"http://{domain}/v1.php"
37+
except socket.gaierror:
38+
continue
39+
raise Exception("API Domain not found")
40+
3341
def api_request(self, func_name='logIn', params='', tries=5):
3442
headers = {
3543
'User-Agent': 'BSPlayer/2.x (1022.12360)',
3644
'Content-Type': 'text/xml; charset=utf-8',
3745
'Connection': 'close',
38-
'SOAPAction': '"http://api.bsplayer-subtitles.com/v1.php#{func_name}"'.format(func_name=func_name)
46+
'SOAPAction': f'"http://{self.DOMAIN}/v1.php#{func_name}"'
3947
}
4048
data = (
4149
'<?xml version="1.0" encoding="UTF-8"?>\n'
4250
'<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" '
4351
'xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" '
4452
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '
45-
'xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns1="{search_url}">'
53+
f'xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns1="{self.search_url}">'
4654
'<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'
47-
'<ns1:{func_name}>{params}</ns1:{func_name}></SOAP-ENV:Body></SOAP-ENV:Envelope>'
48-
).format(search_url=self.search_url, func_name=func_name, params=params)
55+
f'<ns1:{func_name}>{params}</ns1:{func_name}></SOAP-ENV:Body></SOAP-ENV:Envelope>'
56+
)
4957

5058
log('BSPlayer.api_request', 'Sending request: %s.' % func_name)
51-
for i in xrange(tries):
59+
for i in range(tries):
5260
try:
53-
self.session.addheaders.extend(headers.items())
54-
res = self.session.open(self.search_url, data)
61+
req = Request(self.search_url, data=data.encode(), headers=headers, method="POST")
62+
res = self.session.open(req)
5563
return ElementTree.fromstring(res.read())
56-
except Exception, ex:
64+
except Exception as ex:
5765
log("BSPlayer.api_request", "ERROR: %s." % ex)
5866
if func_name == 'logIn':
59-
self.search_url = get_sub_domain()
67+
self.search_url = self.get_sub_domain()
6068
sleep(1)
6169
log('BSPlayer.api_request', 'ERROR: Too many tries (%d)...' % tries)
6270
raise Exception('Too many tries...')
@@ -68,9 +76,11 @@ def login(self):
6876

6977
root = self.api_request(
7078
func_name='logIn',
71-
params=('<username></username>'
72-
'<password></password>'
73-
'<AppID>BSPlayer v2.67</AppID>')
79+
params=(
80+
'<username></username>'
81+
'<password></password>'
82+
f'<AppID>BSPlayer v{self.VERSION}</AppID>'
83+
)
7484
)
7585
res = root.find('.//return')
7686
if res.find('status').text == 'OK':
@@ -86,7 +96,7 @@ def logout(self):
8696

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

105-
movie_size, movie_hash = movie_size_and_hash(movie_path)
106-
log('BSPlayer.search_subtitles', 'Movie Size: %s, Movie Hash: %s.' % (movie_size, movie_hash))
115+
try:
116+
movie_size, movie_hash = movie_size_and_hash(movie_path)
117+
except Exception as ex:
118+
print(ex)
119+
exit(1)
120+
log('BSPlayer.search_subtitles', f'Movie Size: {movie_size}, Movie Hash: {movie_hash}.')
107121
root = self.api_request(
108122
func_name='searchSubtitles',
109123
params=(
110-
'<handle>{token}</handle>'
111-
'<movieHash>{movie_hash}</movieHash>'
112-
'<movieSize>{movie_size}</movieSize>'
113-
'<languageId>{language_ids}</languageId>'
124+
f'<handle>{self.token}</handle>'
125+
f'<movieHash>{movie_hash}</movieHash>'
126+
f'<movieSize>{movie_size}</movieSize>'
127+
f'<languageId>{language_ids}</languageId>'
114128
'<imdbId>*</imdbId>'
115-
).format(token=self.token, movie_hash=movie_hash,
116-
movie_size=movie_size, language_ids=language_ids)
129+
)
117130
)
118131
res = root.find('.//return/result')
119132
if res.find('status').text != 'OK':
@@ -129,7 +142,8 @@ def search_subtitles(self, movie_path, language_ids='heb,eng', logout=False):
129142
subDownloadLink=item.find('subDownloadLink').text,
130143
subLang=item.find('subLang').text,
131144
subName=item.find('subName').text,
132-
subFormat=item.find('subFormat').text
145+
subFormat=item.find('subFormat').text,
146+
subRating=item.find('subRating').text or '0'
133147
))
134148

135149
if logout:
@@ -141,10 +155,10 @@ def search_subtitles(self, movie_path, language_ids='heb,eng', logout=False):
141155
def download_subtitles(download_url, dest_path, proxies=None):
142156
session = get_session(proxies=proxies, http_10=True)
143157
session.addheaders = [('User-Agent', 'Mozilla/4.0 (compatible; Synapse)'),
144-
('Content-Length', 0)]
158+
('Content-Length', 0)]
145159
res = session.open(download_url)
146160
if res:
147-
gf = gzip.GzipFile(fileobj=StringIO(res.read()))
161+
gf = gzip.GzipFile(fileobj=res)
148162
with open(dest_path, 'wb') as f:
149163
f.write(gf.read())
150164
f.flush()

resources/lib/utils.py

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,28 @@
11
import sys
22
import struct
3-
import urllib2
4-
import urlparse
5-
import cookielib
63
from os import path
7-
from httplib import HTTPConnection
4+
from urllib import parse, request
5+
from http.cookiejar import CookieJar
6+
from http.client import HTTPConnection
87

98
import xbmc
10-
import xbmcvfs
119

1210

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

1614

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

2018

2119
def get_params(params_str=""):
2220
params_str = params_str or sys.argv[2]
23-
return dict(urlparse.parse_qsl(params_str.lstrip('?')))
21+
return dict(parse.parse_qsl(params_str.lstrip('?')))
2422

2523

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

5250

53-
class HTTP10Handler(urllib2.HTTPHandler):
51+
class HTTP10Handler(request.HTTPHandler):
5452
def http_open(self, req):
5553
return self.do_open(HTTP10Connection, req)
5654

5755

5856
def get_session(proxies=None, cookies=True, http_10=False):
5957
handlers = []
6058
if proxies:
61-
handlers.append(urllib2.ProxyHandler(proxies))
59+
handlers.append(request.ProxyHandler(proxies))
6260
if cookies:
63-
cj = cookielib.CookieJar()
64-
handlers.append(urllib2.HTTPCookieProcessor(cj))
61+
cj = CookieJar()
62+
handlers.append(request.HTTPCookieProcessor(cj))
6563
if http_10:
6664
handlers.append(HTTP10Handler)
67-
return urllib2.build_opener(*handlers)
65+
return request.build_opener(*handlers)
6866

6967

7068
def __get_last_split(firs_rar_file, x):
@@ -78,7 +76,7 @@ def __get_last_split(firs_rar_file, x):
7876

7977

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

9088
def __movie_size_and_hash_rar(firs_rar_file):
9189
log('utils.movie_size_and_hash', 'Hashing Rar file...')
92-
f = xbmcvfs.File(firs_rar_file)
90+
f = open(firs_rar_file, 'rb')
9391
a = f.read(4)
94-
if a != 'Rar!':
92+
if a != b'Rar!':
9593
log('utils.movie_size_and_hash', 'ERROR: This is not rar file (%s).' % path.basename(firs_rar_file))
9694
raise Exception('ERROR: This is not rar file.')
9795
seek = 0
@@ -129,23 +127,23 @@ def movie_size_and_hash(file_path):
129127
longlong_format = '<q' # little-endian long long
130128
byte_size = struct.calcsize(longlong_format)
131129

132-
f = xbmcvfs.File(file_path)
133-
file_size = f.size()
130+
file_size = path.getsize(file_path)
131+
f = open(file_path, 'rb')
134132
movie_hash = file_size
135133

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

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

147145
f.seek(max(0, file_size - 65536), 0)
148-
for x in range(65536 / byte_size):
146+
for x in range(65536 // byte_size):
149147
buff = f.read(byte_size)
150148
(l_value,) = struct.unpack(longlong_format, buff)
151149
movie_hash += l_value

0 commit comments

Comments
 (0)