First working Leia version for testing
This commit is contained in:
parent
2c785729ee
commit
ae69548107
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<addon id="plugin.audio.subsonic" name="Subsonic" version="3.0.2" provider-name="BasilFX,warwickh">
|
<addon id="plugin.audio.subsonic" name="Subsonic" version="2.1.0" provider-name="BasilFX,warwickh">
|
||||||
<requires>
|
<requires>
|
||||||
<import addon="xbmc.python" version="3.0.0"/>
|
<import addon="xbmc.python" version="2.7.0"/>
|
||||||
</requires>
|
</requires>
|
||||||
<extension point="xbmc.python.pluginsource" library="main.py">
|
<extension point="xbmc.python.pluginsource" library="main.py">
|
||||||
<provides>audio</provides>
|
<provides>audio</provides>
|
||||||
|
|
|
@ -18,11 +18,14 @@ along with py-sonic. If not, see <http://www.gnu.org/licenses/>
|
||||||
from libsonic.errors import *
|
from libsonic.errors import *
|
||||||
from netrc import netrc
|
from netrc import netrc
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
import urllib.request
|
import urllib2
|
||||||
import urllib.error
|
import httplib
|
||||||
import urllib.parse
|
#import urllib2.request
|
||||||
from http import client as http_client
|
#import urllib.error
|
||||||
from urllib.parse import urlencode
|
#import urllib.parse
|
||||||
|
#from http import client as http_client
|
||||||
|
#from urllib.parse import urlencode
|
||||||
|
from urllib import urlencode
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
@ -37,7 +40,7 @@ API_VERSION = '1.16.1'
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class HTTPSConnectionChain(http_client.HTTPSConnection):
|
class HTTPSConnectionChain(httplib.HTTPSConnection):
|
||||||
def _create_sock(self):
|
def _create_sock(self):
|
||||||
sock = socket.create_connection((self.host, self.port), self.timeout)
|
sock = socket.create_connection((self.host, self.port), self.timeout)
|
||||||
if self._tunnel_host:
|
if self._tunnel_host:
|
||||||
|
@ -53,14 +56,14 @@ class HTTPSConnectionChain(http_client.HTTPSConnection):
|
||||||
except:
|
except:
|
||||||
sock.close()
|
sock.close()
|
||||||
|
|
||||||
class HTTPSHandlerChain(urllib.request.HTTPSHandler):
|
class HTTPSHandlerChain(urllib2.HTTPSHandler):
|
||||||
def https_open(self, req):
|
def https_open(self, req):
|
||||||
return self.do_open(HTTPSConnectionChain, req, context=self._context)
|
return self.do_open(HTTPSConnectionChain, req, context=self._context)
|
||||||
|
|
||||||
# install opener
|
# install opener
|
||||||
urllib.request.install_opener(urllib.request.build_opener(HTTPSHandlerChain()))
|
urllib2.install_opener(urllib2.build_opener(HTTPSHandlerChain()))
|
||||||
|
|
||||||
class PysHTTPRedirectHandler(urllib.request.HTTPRedirectHandler):
|
class PysHTTPRedirectHandler(urllib2.HTTPRedirectHandler):
|
||||||
"""
|
"""
|
||||||
This class is used to override the default behavior of the
|
This class is used to override the default behavior of the
|
||||||
HTTPRedirectHandler, which does *not* redirect POST data
|
HTTPRedirectHandler, which does *not* redirect POST data
|
||||||
|
@ -76,7 +79,7 @@ class PysHTTPRedirectHandler(urllib.request.HTTPRedirectHandler):
|
||||||
data = None
|
data = None
|
||||||
if req.data:
|
if req.data:
|
||||||
data = req.data
|
data = req.data
|
||||||
return urllib.request.Request(newurl,
|
return urllib2.Request(newurl,
|
||||||
data=data,
|
data=data,
|
||||||
headers=newheaders,
|
headers=newheaders,
|
||||||
origin_req_host=req.origin_req_host,
|
origin_req_host=req.origin_req_host,
|
||||||
|
@ -240,7 +243,7 @@ class Connection(object):
|
||||||
viewName = '%s.view' % methodName
|
viewName = '%s.view' % methodName
|
||||||
|
|
||||||
req = self._getRequest(viewName)
|
req = self._getRequest(viewName)
|
||||||
xbmc.log("Pinging %s"%str(req.full_url),xbmc.LOGDEBUG)
|
xbmc.log("Pinging %s"%str(req.get_full_url()),xbmc.LOGDEBUG)
|
||||||
try:
|
try:
|
||||||
res = self._doInfoReq(req)
|
res = self._doInfoReq(req)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -906,8 +909,8 @@ class Connection(object):
|
||||||
'converted': converted})
|
'converted': converted})
|
||||||
|
|
||||||
req = self._getRequest(viewName, q)
|
req = self._getRequest(viewName, q)
|
||||||
#xbmc.log("Requesting %s"%str(req.full_url),xbmc.LOGDEBUG)
|
#xbmc.log("Requesting %s"%str(req.get_full_url()),xbmc.LOGDEBUG)
|
||||||
return_url = req.full_url
|
return_url = req.get_full_url()
|
||||||
if self._insecure:
|
if self._insecure:
|
||||||
return_url += '&verifypeer=false'
|
return_url += '&verifypeer=false'
|
||||||
xbmc.log("Request is insecure %s"%return_url,level=xbmc.LOGDEBUG)
|
xbmc.log("Request is insecure %s"%return_url,level=xbmc.LOGDEBUG)
|
||||||
|
@ -955,8 +958,8 @@ class Connection(object):
|
||||||
q = self._getQueryDict({'id': aid, 'size': size})
|
q = self._getQueryDict({'id': aid, 'size': size})
|
||||||
|
|
||||||
req = self._getRequest(viewName, q)
|
req = self._getRequest(viewName, q)
|
||||||
#xbmc.log("Requesting %s"%str(req.full_url),xbmc.LOGDEBUG)
|
#xbmc.log("Requesting %s"%str(req.get_full_url()),xbmc.LOGDEBUG)
|
||||||
return_url = req.full_url
|
return_url = req.get_full_url()
|
||||||
if self._insecure:
|
if self._insecure:
|
||||||
return_url += '&verifypeer=false'
|
return_url += '&verifypeer=false'
|
||||||
xbmc.log("Request is insecure %s"%return_url,level=xbmc.LOGDEBUG)
|
xbmc.log("Request is insecure %s"%return_url,level=xbmc.LOGDEBUG)
|
||||||
|
@ -2007,7 +2010,7 @@ class Connection(object):
|
||||||
q['musicFolderId'] = musicFolderId
|
q['musicFolderId'] = musicFolderId
|
||||||
|
|
||||||
req = self._getRequest(viewName, q)
|
req = self._getRequest(viewName, q)
|
||||||
xbmc.log("Requesting %s"%str(req.full_url),xbmc.LOGDEBUG)
|
xbmc.log("Requesting %s"%str(req.get_full_url()),xbmc.LOGDEBUG)
|
||||||
res = self._doInfoReq(req)
|
res = self._doInfoReq(req)
|
||||||
self._checkStatus(res)
|
self._checkStatus(res)
|
||||||
return res
|
return res
|
||||||
|
@ -2765,7 +2768,7 @@ class Connection(object):
|
||||||
|
|
||||||
url = '%s:%d/%s/%s?%s' % (self._baseUrl, self._port,
|
url = '%s:%d/%s/%s?%s' % (self._baseUrl, self._port,
|
||||||
self._separateServerPath(), viewName, methodName)
|
self._separateServerPath(), viewName, methodName)
|
||||||
req = urllib.request.Request(url)
|
req = urllib2.Request(url)
|
||||||
res = self._opener.open(req)
|
res = self._opener.open(req)
|
||||||
res_msg = res.msg.lower()
|
res_msg = res.msg.lower()
|
||||||
return res_msg == 'ok'
|
return res_msg == 'ok'
|
||||||
|
@ -2779,7 +2782,7 @@ class Connection(object):
|
||||||
if sys.version_info[:3] >= (2, 7, 9) and self._insecure:
|
if sys.version_info[:3] >= (2, 7, 9) and self._insecure:
|
||||||
https_chain = HTTPSHandlerChain(
|
https_chain = HTTPSHandlerChain(
|
||||||
context=ssl._create_unverified_context())
|
context=ssl._create_unverified_context())
|
||||||
opener = urllib.request.build_opener(
|
opener = urllib2.build_opener(
|
||||||
PysHTTPRedirectHandler,
|
PysHTTPRedirectHandler,
|
||||||
https_chain,
|
https_chain,
|
||||||
)
|
)
|
||||||
|
@ -2821,11 +2824,11 @@ class Connection(object):
|
||||||
viewName)
|
viewName)
|
||||||
#xbmc.log("Standard URL %s"%url,level=xbmc.LOGDEBUG)
|
#xbmc.log("Standard URL %s"%url,level=xbmc.LOGDEBUG)
|
||||||
#xbmc.log("Qdict %s"%str(qdict),level=xbmc.LOGDEBUG)
|
#xbmc.log("Qdict %s"%str(qdict),level=xbmc.LOGDEBUG)
|
||||||
req = urllib.request.Request(url, urlencode(qdict).encode('utf-8'))
|
req = urllib2.Request(url, urlencode(qdict).encode('utf-8'))
|
||||||
if(self._useGET or ('getCoverArt' in viewName) or ('stream' in viewName)):
|
if(self._useGET or ('getCoverArt' in viewName) or ('stream' in viewName)):
|
||||||
url += '?%s' % urlencode(qdict)
|
url += '?%s' % urlencode(qdict)
|
||||||
#xbmc.log("UseGET URL %s"%(url),xbmc.LOGDEBUG)
|
#xbmc.log("UseGET URL %s"%(url),xbmc.LOGDEBUG)
|
||||||
req = urllib.request.Request(url)
|
req = urllib2.Request(url)
|
||||||
return req
|
return req
|
||||||
|
|
||||||
def _getRequestWithList(self, viewName, listName, alist, query={}):
|
def _getRequestWithList(self, viewName, listName, alist, query={}):
|
||||||
|
@ -2841,7 +2844,7 @@ class Connection(object):
|
||||||
data.write(urlencode(qdict))
|
data.write(urlencode(qdict))
|
||||||
for i in alist:
|
for i in alist:
|
||||||
data.write('&%s' % urlencode({listName: i}))
|
data.write('&%s' % urlencode({listName: i}))
|
||||||
req = urllib.request.Request(url, data.getvalue().encode('utf-8'))
|
req = urllib2.Request(url, data.getvalue().encode('utf-8'))
|
||||||
|
|
||||||
if self._useGET:
|
if self._useGET:
|
||||||
url += '?%s' % data.getvalue()
|
url += '?%s' % data.getvalue()
|
||||||
|
@ -2868,7 +2871,7 @@ class Connection(object):
|
||||||
for k, l in listMap.items():
|
for k, l in listMap.items():
|
||||||
for i in l:
|
for i in l:
|
||||||
data.write('&%s' % urlencode({k: i}))
|
data.write('&%s' % urlencode({k: i}))
|
||||||
req = urllib.request.Request(url, data.getvalue().encode('utf-8'))
|
req = urllib2.Request(url, data.getvalue().encode('utf-8'))
|
||||||
|
|
||||||
if self._useGET:
|
if self._useGET:
|
||||||
url += '?%s' % data.getvalue()
|
url += '?%s' % data.getvalue()
|
||||||
|
|
|
@ -19,7 +19,7 @@ import inspect
|
||||||
import time
|
import time
|
||||||
import hashlib
|
import hashlib
|
||||||
import pickle
|
import pickle
|
||||||
from collections.abc import MutableMapping
|
from collections import MutableMapping
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
@ -27,7 +27,9 @@ from shutil import copyfile
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from platform import uname
|
from platform import uname
|
||||||
from urllib.parse import urlencode, quote_plus, urlparse, unquote_plus, parse_qs
|
#from urllib.parse import urlencode, quote_plus, urlparse, unquote_plus, parse_qs
|
||||||
|
from urlparse import parse_qs
|
||||||
|
from urllib import urlencode
|
||||||
import xbmcaddon
|
import xbmcaddon
|
||||||
import xbmc
|
import xbmc
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
|
@ -36,7 +38,7 @@ import xbmcvfs
|
||||||
__all__ = ['SimplePluginError', 'Storage', 'MemStorage', 'Addon', 'Plugin',
|
__all__ = ['SimplePluginError', 'Storage', 'MemStorage', 'Addon', 'Plugin',
|
||||||
'RoutedPlugin', 'Params', 'log_exception', 'translate_path']
|
'RoutedPlugin', 'Params', 'log_exception', 'translate_path']
|
||||||
|
|
||||||
getargspec = inspect.getfullargspec
|
getargspec = inspect.getargspec
|
||||||
|
|
||||||
Route = namedtuple('Route', ['pattern', 'func'])
|
Route = namedtuple('Route', ['pattern', 'func'])
|
||||||
|
|
||||||
|
@ -1086,7 +1088,6 @@ class Plugin(Addon):
|
||||||
return action_callable(self._params)
|
return action_callable(self._params)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RoutedPlugin(Plugin):
|
class RoutedPlugin(Plugin):
|
||||||
"""
|
"""
|
||||||
Plugin class that implements "pretty URL" routing similar to Flask and Bottle
|
Plugin class that implements "pretty URL" routing similar to Flask and Bottle
|
||||||
|
|
43
main.py
43
main.py
|
@ -12,11 +12,11 @@ import time
|
||||||
import hashlib
|
import hashlib
|
||||||
import random
|
import random
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from collections.abc import MutableMapping
|
from collections import MutableMapping
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
# Add the /lib folder to sys
|
# Add the /lib folder to sys
|
||||||
sys.path.append(xbmcvfs.translatePath(os.path.join(xbmcaddon.Addon("plugin.audio.subsonic").getAddonInfo("path"), "lib")))
|
sys.path.append(xbmc.translatePath(os.path.join(xbmcaddon.Addon("plugin.audio.subsonic").getAddonInfo("path"), "lib")))
|
||||||
|
|
||||||
import libsonic
|
import libsonic
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ def get_connection():
|
||||||
if connection==None:
|
if connection==None:
|
||||||
connected = False
|
connected = False
|
||||||
# Create connection
|
# Create connection
|
||||||
try:
|
if 1:#try:
|
||||||
connection = libsonic.Connection(
|
connection = libsonic.Connection(
|
||||||
baseUrl=Addon().get_setting('subsonic_url'),
|
baseUrl=Addon().get_setting('subsonic_url'),
|
||||||
username=Addon().get_setting('username', convert=False),
|
username=Addon().get_setting('username', convert=False),
|
||||||
|
@ -56,8 +56,8 @@ def get_connection():
|
||||||
useGET=Addon().get_setting('useget'),
|
useGET=Addon().get_setting('useget'),
|
||||||
)
|
)
|
||||||
connected = connection.ping()
|
connected = connection.ping()
|
||||||
except:
|
#except:
|
||||||
pass
|
# pass
|
||||||
|
|
||||||
if connected==False:
|
if connected==False:
|
||||||
popup('Connection error')
|
popup('Connection error')
|
||||||
|
@ -1373,7 +1373,8 @@ def walk_index(folder_id=None):
|
||||||
plugin.log("artist: %s"%artist)
|
plugin.log("artist: %s"%artist)
|
||||||
yield artist
|
yield artist
|
||||||
except KeyError:
|
except KeyError:
|
||||||
yield from ()
|
for emp in ():
|
||||||
|
yield emp
|
||||||
|
|
||||||
def walk_playlists():
|
def walk_playlists():
|
||||||
"""
|
"""
|
||||||
|
@ -1384,7 +1385,8 @@ def walk_playlists():
|
||||||
for child in response["playlists"]["playlist"]:
|
for child in response["playlists"]["playlist"]:
|
||||||
yield child
|
yield child
|
||||||
except KeyError:
|
except KeyError:
|
||||||
yield from ()
|
for emp in ():
|
||||||
|
yield emp
|
||||||
|
|
||||||
def walk_playlist(playlist_id):
|
def walk_playlist(playlist_id):
|
||||||
"""
|
"""
|
||||||
|
@ -1395,7 +1397,8 @@ def walk_playlist(playlist_id):
|
||||||
for child in response["playlist"]["entry"]:
|
for child in response["playlist"]["entry"]:
|
||||||
yield child
|
yield child
|
||||||
except KeyError:
|
except KeyError:
|
||||||
yield from ()
|
for emp in ():
|
||||||
|
yield emp
|
||||||
|
|
||||||
def walk_folders():
|
def walk_folders():
|
||||||
response = connection.getMusicFolders()
|
response = connection.getMusicFolders()
|
||||||
|
@ -1403,7 +1406,8 @@ def walk_folders():
|
||||||
for child in response["musicFolders"]["musicFolder"]:
|
for child in response["musicFolders"]["musicFolder"]:
|
||||||
yield child
|
yield child
|
||||||
except KeyError:
|
except KeyError:
|
||||||
yield from ()
|
for emp in ():
|
||||||
|
yield emp
|
||||||
|
|
||||||
def walk_directory(directory_id, merge_artist = True):
|
def walk_directory(directory_id, merge_artist = True):
|
||||||
"""
|
"""
|
||||||
|
@ -1419,7 +1423,8 @@ def walk_directory(directory_id, merge_artist = True):
|
||||||
else:
|
else:
|
||||||
yield child
|
yield child
|
||||||
except KeyError:
|
except KeyError:
|
||||||
yield from ()
|
for emp in ():
|
||||||
|
yield emp
|
||||||
|
|
||||||
def walk_artist(artist_id):
|
def walk_artist(artist_id):
|
||||||
"""
|
"""
|
||||||
|
@ -1431,7 +1436,8 @@ def walk_artist(artist_id):
|
||||||
for child in response["artist"]["album"]:
|
for child in response["artist"]["album"]:
|
||||||
yield child
|
yield child
|
||||||
except KeyError:
|
except KeyError:
|
||||||
yield from ()
|
for emp in ():
|
||||||
|
yield emp
|
||||||
|
|
||||||
def walk_artists():
|
def walk_artists():
|
||||||
"""
|
"""
|
||||||
|
@ -1444,7 +1450,8 @@ def walk_artists():
|
||||||
for artist in index["artist"]:
|
for artist in index["artist"]:
|
||||||
yield artist
|
yield artist
|
||||||
except KeyError:
|
except KeyError:
|
||||||
yield from ()
|
for emp in ():
|
||||||
|
yield emp
|
||||||
|
|
||||||
def walk_genres():
|
def walk_genres():
|
||||||
"""
|
"""
|
||||||
|
@ -1456,7 +1463,8 @@ def walk_genres():
|
||||||
for genre in response["genres"]["genre"]:
|
for genre in response["genres"]["genre"]:
|
||||||
yield genre
|
yield genre
|
||||||
except KeyError:
|
except KeyError:
|
||||||
yield from ()
|
for emp in ():
|
||||||
|
yield emp
|
||||||
|
|
||||||
def walk_albums(ltype, size=None, fromYear=None,toYear=None, genre=None, offset=None):
|
def walk_albums(ltype, size=None, fromYear=None,toYear=None, genre=None, offset=None):
|
||||||
"""
|
"""
|
||||||
|
@ -1490,7 +1498,8 @@ def walk_album(album_id):
|
||||||
for song in response["album"]["song"]:
|
for song in response["album"]["song"]:
|
||||||
yield song
|
yield song
|
||||||
except KeyError:
|
except KeyError:
|
||||||
yield from ()
|
for emp in ():
|
||||||
|
yield emp
|
||||||
|
|
||||||
def walk_tracks_random(size=None, genre=None, fromYear=None,toYear=None):
|
def walk_tracks_random(size=None, genre=None, fromYear=None,toYear=None):
|
||||||
"""
|
"""
|
||||||
|
@ -1502,7 +1511,8 @@ def walk_tracks_random(size=None, genre=None, fromYear=None,toYear=None):
|
||||||
for song in response["randomSongs"]["song"]:
|
for song in response["randomSongs"]["song"]:
|
||||||
yield song
|
yield song
|
||||||
except KeyError:
|
except KeyError:
|
||||||
yield from ()
|
for emp in ():
|
||||||
|
yield emp
|
||||||
|
|
||||||
def walk_tracks_starred():
|
def walk_tracks_starred():
|
||||||
"""
|
"""
|
||||||
|
@ -1513,7 +1523,8 @@ def walk_tracks_starred():
|
||||||
for song in response["starred"]["song"]:
|
for song in response["starred"]["song"]:
|
||||||
yield song
|
yield song
|
||||||
except KeyError:
|
except KeyError:
|
||||||
yield from ()
|
for emp in ():
|
||||||
|
yield emp
|
||||||
|
|
||||||
# Start plugin from within Kodi.
|
# Start plugin from within Kodi.
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -4,7 +4,7 @@ import xbmcvfs
|
||||||
import os
|
import os
|
||||||
import xbmcaddon
|
import xbmcaddon
|
||||||
# Add the /lib folder to sys
|
# Add the /lib folder to sys
|
||||||
sys.path.append(xbmcvfs.translatePath(os.path.join(xbmcaddon.Addon("plugin.audio.subsonic").getAddonInfo("path"), "lib")))
|
sys.path.append(xbmc.translatePath(os.path.join(xbmcaddon.Addon("plugin.audio.subsonic").getAddonInfo("path"), "lib")))
|
||||||
|
|
||||||
import libsonic
|
import libsonic
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue