Skip to content

Commit 6782774

Browse files
committed
Fixed android rar support, works with smb shares
1 parent f2e8e1d commit 6782774

File tree

5 files changed

+137
-63
lines changed

5 files changed

+137
-63
lines changed

addon.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
22
<addon id="service.subtitles.bsplayer"
33
name="BSPlayer"
4-
version="0.1.0"
4+
version="0.2.0"
55
provider-name="realgam3">
66
<requires>
77
<import addon="xbmc.python" version="2.1.0"/>

changelog.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1+
0.2.0 - 07/11/2015
2+
- Fixed android rar support, works with smb shares
3+
14
0.1.0 - 27/09/2015
25
- Initial release
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
msgid ""
2+
msgstr ""
3+
"Project-Id-Version: XBMC Main Translation Project (Frodo)\n"
4+
"Report-Msgid-Bugs-To: http://trac.xbmc.org/\n"
5+
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
6+
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
7+
"Last-Translator: XBMC Translation Team\n"
8+
"Language-Team: English (http://www.transifex.com/projects/p/XBMC-Main-Frodo/language/en/)\n"
9+
"MIME-Version: 1.0\n"
10+
"Content-Type: text/plain; charset=UTF-8\n"
11+
"Content-Transfer-Encoding: 8bit\n"
12+
"Language: en\n"
13+
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
14+
15+
#English
16+
17+
msgctxt "#32001"
18+
msgid "Streaming not supported."
19+
msgstr ""
20+
21+
msgctxt "#32002"
22+
msgid "Manual search not supported."
23+
msgstr ""

resources/lib/bsplayer.py

Lines changed: 100 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,102 @@
1+
import cookielib
12
import gzip
2-
import struct
3+
import logging
34
import random
5+
import struct
46
import urllib2
5-
import logging
6-
from os import path
77
from StringIO import StringIO
8-
from xml.etree import ElementTree
98
from httplib import HTTPConnection
9+
from os import path
10+
from time import sleep
11+
from xml.etree import ElementTree
1012

11-
import rarfile
12-
13+
import xbmcvfs
1314

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

1920

20-
def check_connectivity(url, timeout=5):
21-
try:
22-
urllib2.urlopen(url, timeout=timeout)
23-
except urllib2.URLError:
24-
return False
25-
return True
26-
27-
2821
def get_sub_domain():
2922
sub_domains_end = len(SUB_DOMAINS) - 1
30-
url = API_URL_TEMPLATE.format(sub_domain=SUB_DOMAINS[random.randint(0, sub_domains_end)])
23+
return API_URL_TEMPLATE.format(sub_domain=SUB_DOMAINS[random.randint(0, sub_domains_end)])
3124

32-
while not check_connectivity(url):
33-
url = API_URL_TEMPLATE.format(sub_domain=SUB_DOMAINS[random.randint(0, sub_domains_end)])
3425

35-
return url
26+
def get_session(proxies=None):
27+
cj = cookielib.CookieJar()
28+
proxy_handler = urllib2.ProxyHandler(proxies)
29+
return urllib2.build_opener(urllib2.HTTPCookieProcessor(cj), proxy_handler)
3630

3731

3832
def python_logger(module, msg):
3933
logger = logging.getLogger('BSPlayer')
40-
logger.log((u"### [%s] - %s" % (module, msg)), level=logging.DEBUG)
41-
42-
43-
def generic_open(file_path):
44-
rf = None
45-
if path.splitext(file_path)[1] == '.rar':
46-
rf = rarfile.RarFile(file_path)
47-
rfi = rf.infolist()[0]
48-
return rf, rf.open(rfi, 'r'), rfi.file_size
49-
return rf, open(file_path, 'rb'), path.getsize(file_path)
34+
logger.log(logging.DEBUG, (u"### [%s] - %s" % (module, msg)))
35+
36+
37+
def OpensubtitlesHashRar(firsrarfile):
38+
# log(__name__, "Hash Rar file")
39+
f = xbmcvfs.File(firsrarfile)
40+
a = f.read(4)
41+
if a != 'Rar!':
42+
raise Exception('ERROR: This is not rar file.')
43+
seek = 0
44+
for i in range(4):
45+
f.seek(max(0, seek), 0)
46+
a = f.read(100)
47+
type, flag, size = struct.unpack('<BHH', a[2:2 + 5])
48+
if 0x74 == type:
49+
if 0x30 != struct.unpack('<B', a[25:25 + 1])[0]:
50+
raise Exception('Bad compression method! Work only for "store".')
51+
s_partiizebodystart = seek + size
52+
s_partiizebody, s_unpacksize = struct.unpack('<II', a[7:7 + 2 * 4])
53+
if (flag & 0x0100):
54+
s_unpacksize = (struct.unpack('<I', a[36:36 + 4])[0] << 32) + s_unpacksize
55+
# log(__name__, 'Hash untested for files biger that 2gb. May work or may generate bad hash.')
56+
lastrarfile = getlastsplit(firsrarfile, (s_unpacksize - 1) / s_partiizebody)
57+
hash = addfilehash(firsrarfile, s_unpacksize, s_partiizebodystart)
58+
hash = addfilehash(lastrarfile, hash, (s_unpacksize % s_partiizebody) + s_partiizebodystart - 65536)
59+
f.close()
60+
return (s_unpacksize, "%016x" % hash)
61+
seek += size
62+
raise Exception('ERROR: Not Body part in rar file.')
63+
64+
65+
def getlastsplit(firsrarfile, x):
66+
if firsrarfile[-3:] == '001':
67+
return firsrarfile[:-3] + ('%03d' % (x + 1))
68+
if firsrarfile[-11:-6] == '.part':
69+
return firsrarfile[0:-6] + ('%02d' % (x + 1)) + firsrarfile[-4:]
70+
if firsrarfile[-10:-5] == '.part':
71+
return firsrarfile[0:-5] + ('%1d' % (x + 1)) + firsrarfile[-4:]
72+
return firsrarfile[0:-2] + ('%02d' % (x - 1))
73+
74+
75+
def addfilehash(name, hash, seek):
76+
f = xbmcvfs.File(name)
77+
f.seek(max(0, seek), 0)
78+
for i in range(8192):
79+
hash += struct.unpack('<q', f.read(8))[0]
80+
hash &= 0xffffffffffffffff
81+
f.close()
82+
return hash
5083

5184

5285
def movie_size_and_hash(file_path):
53-
try:
54-
longlong_format = '<q' # little-endian long long
55-
byte_size = struct.calcsize(longlong_format)
86+
file_ext = path.splitext(file_path)[1]
87+
if file_ext == '.rar' or file_ext =='.001':
88+
return OpensubtitlesHashRar(file_path)
89+
90+
longlong_format = '<q' # little-endian long long
91+
byte_size = struct.calcsize(longlong_format)
5692

57-
rf, f, file_size = generic_open(file_path)
58-
movie_hash = file_size
93+
file_size = path.getsize(file_path)
94+
movie_hash = file_size
5995

60-
if file_size < 65536 * 2:
61-
return "SizeError"
96+
if file_size < 65536 * 2:
97+
raise Exception("SizeError")
6298

99+
with open(file_path, 'rb') as f:
63100
for x in range(65536 / byte_size):
64101
buff = f.read(byte_size)
65102
(l_value,) = struct.unpack(longlong_format, buff)
@@ -74,14 +111,7 @@ def movie_size_and_hash(file_path):
74111
movie_hash &= 0xFFFFFFFFFFFFFFFF
75112
returned_movie_hash = "%016x" % movie_hash
76113

77-
# Close File And RarFile
78-
f.close()
79-
if rf:
80-
rf.close()
81-
82-
return file_size, returned_movie_hash
83-
except IOError:
84-
return "IOError"
114+
return file_size, returned_movie_hash
85115

86116

87117
class HTTP10Connection(HTTPConnection):
@@ -95,8 +125,9 @@ def http_open(self, req):
95125

96126

97127
class BSPlayer(object):
98-
def __init__(self, search_url=get_sub_domain(), log=python_logger):
99-
self.search_url = search_url
128+
def __init__(self, search_url=None, log=python_logger, proxies=None):
129+
self.session = get_session(proxies)
130+
self.search_url = search_url or get_sub_domain()
100131
self.token = None
101132
self.log = log
102133
if self.log.__name__ == 'python_logger':
@@ -113,7 +144,7 @@ def __enter__(self):
113144
def __exit__(self, exc_type, exc_val, exc_tb):
114145
return self.logout()
115146

116-
def api_request(self, func_name='logIn', params=''):
147+
def api_request(self, func_name='logIn', params='', tries=5):
117148
headers = {
118149
'User-Agent': 'BSPlayer/2.x (1022.12360)',
119150
'Content-Type': 'text/xml; charset=utf-8',
@@ -130,8 +161,19 @@ def api_request(self, func_name='logIn', params=''):
130161
'<ns1:{func_name}>{params}</ns1:{func_name}></SOAP-ENV:Body></SOAP-ENV:Envelope>'
131162
).format(search_url=self.search_url, func_name=func_name, params=params)
132163

133-
req = urllib2.Request(self.search_url, data, headers)
134-
return ElementTree.fromstring(urllib2.urlopen(req).read())
164+
self.log("BSPlayer.api_request", 'Sending request: %s' % func_name)
165+
for i in xrange(tries):
166+
try:
167+
self.session.addheaders.extend(headers.items())
168+
res = self.session.open(self.search_url, data)
169+
return ElementTree.fromstring(res.read())
170+
except Exception, ex:
171+
self.log("BSPlayer.api_request", ex)
172+
if func_name == 'logIn':
173+
self.search_url = get_sub_domain()
174+
sleep(1)
175+
176+
raise Exception('Too many tries...')
135177

136178
def login(self):
137179
# If already logged in
@@ -175,6 +217,7 @@ def search_subtitles(self, movie_path, language_ids='heb,eng', logout=False):
175217
language_ids = ",".join(language_ids)
176218

177219
movie_size, movie_hash = movie_size_and_hash(movie_path)
220+
self.log('BSPlayer.search_subtitles', 'Movie Size: %s, Movie Hash: %s' % (movie_size, movie_hash))
178221
root = self.api_request(
179222
func_name='searchSubtitles',
180223
params=(
@@ -209,8 +252,9 @@ def search_subtitles(self, movie_path, language_ids='heb,eng', logout=False):
209252
return subtitles
210253

211254
@staticmethod
212-
def download_subtitles(download_url, dest_path=r"c:\tomerz.srt"):
213-
opener = urllib2.build_opener(HTTP10Handler)
255+
def download_subtitles(download_url, dest_path="Subtitle.srt", proxies=None):
256+
proxy_handler = urllib2.ProxyHandler(proxies)
257+
opener = urllib2.build_opener(HTTP10Handler, proxy_handler)
214258
opener.addheaders = [('User-Agent', 'Mozilla/4.0 (compatible; Synapse)'),
215259
('Content-Length', 0)]
216260
res = opener.open(download_url)
@@ -222,13 +266,14 @@ def download_subtitles(download_url, dest_path=r"c:\tomerz.srt"):
222266
gf.close()
223267
return True
224268
return False
225-
#
269+
226270
#
227271
# if __name__ == '__main__':
228-
# bsp = BSPlayer()
272+
# bsp = BSPlayer(proxies={'http': '207.91.10.234:8080'})
229273
# subs = bsp.search_subtitles(
230-
# r'..\..\..\Jurassic.World.2015.720p.BluRay.x264-SPARKS\jurassic.world.2015.720p.bluray.x264-sparks.rar',
274+
# r'V:\Movies\Jackass.Presents.Bad.Grandpa.0.5.2014.720p.Bluray.x264.DTS-EVO\Jackass.Presents.Bad.Grandpa.0.5.2014.720p.Bluray.x264.DTS-EVO.mkv',
231275
# logout=True
232276
# )
233-
# print subs[0]['subDownloadLink']
234-
# print bsp.download_subtitles(subs[0]['subDownloadLink'])
277+
# for sub in subs:
278+
# print bsp.download_subtitles(sub['subDownloadLink'], proxies={'http': '207.91.10.234:8080'})
279+
# break

service.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,13 @@ def get_params(params_str=""):
4040

4141
def get_video_path(xbmc_path=''):
4242
xbmc_path = xbmc_path or urlparse.unquote(xbmc.Player().getPlayingFile().decode('utf-8'))
43-
4443
if xbmc_path.startswith('rar://'):
4544
return path.dirname(xbmc_path.replace('rar://', ''))
4645
elif xbmc_path.startswith('stack://'):
4746
return xbmc_path.split(" , ")[0].replace('stack://', '')
47+
elif xbmc_path.startswith('http://') or xbmc_path.startswith('https://'):
48+
xbmc.executebuiltin((u'Notification(%s,%s)' % (__scriptname__ , __language__(32001))).encode('utf-8'))
49+
log("BSPlayer.get_video_path", "Streaming not supported.")
4850

4951
return xbmc_path
5052

@@ -62,12 +64,12 @@ def get_languages_dict(languages_param):
6264

6365

6466
params = get_params()
65-
log("BSPlayers.params", "Current Action: %s." % params['action'])
67+
log("BSPlayer.params", "Current Action: %s." % params['action'])
6668
if params['action'] == 'search':
6769
video_path = get_video_path()
68-
log("BSPlayers.video_path", "Current Video Path: %s." % video_path)
70+
log("BSPlayer.video_path", "Current Video Path: %s." % video_path)
6971
languages = get_languages_dict(params['languages'])
70-
log("BSPlayers.languages", "Current Languages: %s." % languages)
72+
log("BSPlayer.languages", "Current Languages: %s." % languages)
7173

7274
with BSPlayer(log=log) as bsp:
7375
subtitles = bsp.search_subtitles(video_path, language_ids=languages.keys())
@@ -87,10 +89,11 @@ def get_languages_dict(languages_param):
8789
format=subtitle['subFormat']
8890
))
8991
)
90-
log("BSPlayers.plugin_url", "Plugin Url Created: %s." % plugin_url)
92+
log("BSPlayer.plugin_url", "Plugin Url Created: %s." % plugin_url)
9193
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=plugin_url, listitem=list_item, isFolder=False)
9294
elif params['action'] == 'manualsearch':
93-
log("BSPlayer.manualsearch", "Cannot Search Manually.")
95+
xbmc.executebuiltin((u'Notification(%s,%s)' % (__scriptname__ , __language__(32002))).encode('utf-8'))
96+
log("BSPlayer.manualsearch", "Manual search not supported.")
9497
elif params['action'] == 'download':
9598
if xbmcvfs.exists(__temp__):
9699
shutil.rmtree(__temp__)
@@ -101,7 +104,7 @@ def get_languages_dict(languages_param):
101104
if BSPlayer.download_subtitles(params['link'], subtitle_path):
102105
log("BSPlayer.download_subtitles", "Subtitles Download Successfully From: %s." % params['link'])
103106
list_item = xbmcgui.ListItem(label=subtitle_path)
104-
log("BSPlayer.download", "Downloaded Subtitle Path: %s." % subtitle_path)
107+
log("BSPlayer.download", "Downloaded Subtitle Path: %s" % subtitle_path)
105108
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=subtitle_path, listitem=list_item, isFolder=False)
106109

107110
xbmcplugin.endOfDirectory(int(sys.argv[1]))

0 commit comments

Comments
 (0)