Add support for proxies (#126)

* Add support for proxies
This commit is contained in:
Michaël Arnauts 2023-07-21 22:54:53 +02:00 committed by GitHub
parent 48c993a4e7
commit bc82711886
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 84 additions and 14 deletions

View File

@ -4,7 +4,9 @@ pytest
pytest-cov pytest-cov
pytest-timeout pytest-timeout
python-dateutil python-dateutil
pysocks
requests requests
git+https://github.com/tamland/kodi-plugin-routing@master#egg=routing git+https://github.com/tamland/kodi-plugin-routing@master#egg=routing
six six
sakee sakee
win-inet-pton; platform_system=="Windows"

View File

@ -17,6 +17,7 @@ try: # Python 3
from html import unescape from html import unescape
except ImportError: # Python 2 except ImportError: # Python 2
from HTMLParser import HTMLParser from HTMLParser import HTMLParser
unescape = HTMLParser().unescape unescape = HTMLParser().unescape
ADDON = xbmcaddon.Addon() ADDON = xbmcaddon.Addon()
@ -478,6 +479,67 @@ def set_global_setting(key, value):
return jsonrpc(method='Settings.SetSettingValue', params={'setting': key, 'value': value}) return jsonrpc(method='Settings.SetSettingValue', params={'setting': key, 'value': value})
def has_socks():
"""Test if socks is installed, and use a static variable to remember"""
if hasattr(has_socks, 'cached'):
return getattr(has_socks, 'cached')
try:
import socks # noqa: F401; pylint: disable=unused-variable,unused-import
except ImportError:
has_socks.cached = False
return None # Detect if this is the first run
has_socks.cached = True
return True
def get_proxies():
"""Return a usable proxies dictionary from Kodi proxy settings"""
# Use proxy settings from environment variables
env_http_proxy = os.environ.get('HTTP_PROXY')
env_https_proxy = os.environ.get('HTTPS_PROXY')
if env_http_proxy:
return {'http': env_http_proxy, 'https': env_https_proxy or env_http_proxy}
usehttpproxy = get_global_setting('network.usehttpproxy')
if usehttpproxy is not True:
return None
try:
httpproxytype = int(get_global_setting('network.httpproxytype'))
except ValueError:
httpproxytype = 0
socks_supported = has_socks()
if httpproxytype != 0 and not socks_supported:
# Only open the dialog the first time (to avoid multiple popups)
if socks_supported is None:
ok_dialog('', localize(30966)) # Requires PySocks
return None
proxy_types = ['http', 'socks4', 'socks4a', 'socks5', 'socks5h']
proxy = {
'scheme': proxy_types[httpproxytype] if 0 <= httpproxytype < 5 else 'http',
'server': get_global_setting('network.httpproxyserver'),
'port': get_global_setting('network.httpproxyport'),
'username': get_global_setting('network.httpproxyusername'),
'password': get_global_setting('network.httpproxypassword')
}
if proxy.get('username') and proxy.get('password') and proxy.get('server') and proxy.get('port'):
proxy_address = '{scheme}://{username}:{password}@{server}:{port}'.format(**proxy)
elif proxy.get('username') and proxy.get('server') and proxy.get('port'):
proxy_address = '{scheme}://{username}@{server}:{port}'.format(**proxy)
elif proxy.get('server') and proxy.get('port'):
proxy_address = '{scheme}://{server}:{port}'.format(**proxy)
elif proxy.get('server'):
proxy_address = '{scheme}://{server}'.format(**proxy)
else:
return None
return {'http': proxy_address, 'https': proxy_address}
def get_cond_visibility(condition): def get_cond_visibility(condition):
"""Test a condition in XBMC""" """Test a condition in XBMC"""
return xbmc.getCondVisibility(condition) return xbmc.getCondVisibility(condition)

View File

@ -44,7 +44,6 @@ class CognitoIdentity:
'x-amz-target': 'AWSCognitoIdentityService.GetId', 'x-amz-target': 'AWSCognitoIdentityService.GetId',
'content-type': 'application/x-amz-json-1.1', 'content-type': 'application/x-amz-json-1.1',
}) })
_LOGGER.debug(response.text)
result = json.loads(response.text) result = json.loads(response.text)
@ -64,7 +63,6 @@ class CognitoIdentity:
'x-amz-target': 'AWSCognitoIdentityService.GetCredentialsForIdentity', 'x-amz-target': 'AWSCognitoIdentityService.GetCredentialsForIdentity',
'content-type': 'application/x-amz-json-1.1', 'content-type': 'application/x-amz-json-1.1',
}) })
_LOGGER.debug(response.text)
result = json.loads(response.text) result = json.loads(response.text)

View File

@ -77,7 +77,6 @@ class CognitoIdp:
self.k = self.__hex_to_long(self.__hex_hash('00' + self.n_hex + '0' + self.g_hex)) # pylint: disable=invalid-name self.k = self.__hex_to_long(self.__hex_hash('00' + self.n_hex + '0' + self.g_hex)) # pylint: disable=invalid-name
self.small_a_value = self.__generate_random_small_a() self.small_a_value = self.__generate_random_small_a()
self.large_a_value = self.__calculate_a() self.large_a_value = self.__calculate_a()
_LOGGER.debug("Created %s", self)
def authenticate(self, username, password): def authenticate(self, username, password):
""" Authenticate with a username and password. """ """ Authenticate with a username and password. """

View File

@ -13,6 +13,7 @@ from datetime import datetime
import requests import requests
from resources.lib import kodiutils
from resources.lib.kodiutils import STREAM_DASH, STREAM_HLS, html_to_kodi from resources.lib.kodiutils import STREAM_DASH, STREAM_HLS, html_to_kodi
from resources.lib.viervijfzes import ResolvedStream from resources.lib.viervijfzes import ResolvedStream
@ -29,6 +30,8 @@ CACHE_AUTO = 1 # Allow to use the cache, and query the API if no cache is avail
CACHE_ONLY = 2 # Only use the cache, don't use the API CACHE_ONLY = 2 # Only use the cache, don't use the API
CACHE_PREVENT = 3 # Don't use the cache CACHE_PREVENT = 3 # Don't use the cache
PROXIES = kodiutils.get_proxies()
class UnavailableException(Exception): class UnavailableException(Exception):
""" Is thrown when an item is unavailable. """ """ Is thrown when an item is unavailable. """
@ -396,7 +399,8 @@ class ContentApi:
# No manifest url found, get manifest from Server-Side Ad Insertion service # No manifest url found, get manifest from Server-Side Ad Insertion service
if data.get('adType') == 'SSAI' and data.get('ssai'): if data.get('adType') == 'SSAI' and data.get('ssai'):
url = 'https://pubads.g.doubleclick.net/ondemand/dash/content/%s/vid/%s/streams' % (data.get('ssai').get('contentSourceID'), data.get('ssai').get('videoID')) url = 'https://pubads.g.doubleclick.net/ondemand/dash/content/%s/vid/%s/streams' % (
data.get('ssai').get('contentSourceID'), data.get('ssai').get('videoID'))
ad_data = json.loads(self._post_url(url, data='')) ad_data = json.loads(self._post_url(url, data=''))
# Server-Side Ad Insertion DASH stream # Server-Side Ad Insertion DASH stream
@ -409,7 +413,6 @@ class ContentApi:
raise UnavailableException raise UnavailableException
def get_program_tree(self, cache=CACHE_AUTO): def get_program_tree(self, cache=CACHE_AUTO):
""" Get a content tree with information about all the programs. """ Get a content tree with information about all the programs.
:type cache: str :type cache: str
@ -772,9 +775,9 @@ class ContentApi:
if authentication: if authentication:
response = self._session.get(url, params=params, headers={ response = self._session.get(url, params=params, headers={
'authorization': authentication, 'authorization': authentication,
}) }, proxies=PROXIES)
else: else:
response = self._session.get(url, params=params) response = self._session.get(url, params=params, proxies=PROXIES)
if response.status_code != 200: if response.status_code != 200:
_LOGGER.error(response.text) _LOGGER.error(response.text)
@ -791,9 +794,9 @@ class ContentApi:
if authentication: if authentication:
response = self._session.post(url, params=params, json=data, headers={ response = self._session.post(url, params=params, json=data, headers={
'authorization': authentication, 'authorization': authentication,
}) }, proxies=PROXIES)
else: else:
response = self._session.post(url, params=params, json=data) response = self._session.post(url, params=params, json=data, proxies=PROXIES)
if response.status_code not in (200, 201): if response.status_code not in (200, 201):
_LOGGER.error(response.text) _LOGGER.error(response.text)
@ -810,9 +813,9 @@ class ContentApi:
if authentication: if authentication:
response = self._session.delete(url, params=params, headers={ response = self._session.delete(url, params=params, headers={
'authorization': authentication, 'authorization': authentication,
}) }, proxies=PROXIES)
else: else:
response = self._session.delete(url, params=params) response = self._session.delete(url, params=params, proxies=PROXIES)
if response.status_code != 200: if response.status_code != 200:
_LOGGER.error(response.text) _LOGGER.error(response.text)

View File

@ -11,6 +11,8 @@ import dateutil.parser
import dateutil.tz import dateutil.tz
import requests import requests
from resources.lib import kodiutils
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
GENRE_MAPPING = { GENRE_MAPPING = {
@ -31,6 +33,8 @@ GENRE_MAPPING = {
'Voetbal': 0x43, 'Voetbal': 0x43,
} }
PROXIES = kodiutils.get_proxies()
class EpgProgram: class EpgProgram:
""" Defines a Program in the EPG. """ """ Defines a Program in the EPG. """
@ -177,7 +181,7 @@ class EpgApi:
:type url: str :type url: str
:rtype str :rtype str
""" """
response = self._session.get(url) response = self._session.get(url, proxies=PROXIES)
if response.status_code != 200: if response.status_code != 200:
raise Exception('Could not fetch data') raise Exception('Could not fetch data')

View File

@ -13,6 +13,8 @@ from resources.lib.viervijfzes.content import CACHE_ONLY, ContentApi, Program
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
PROXIES = kodiutils.get_proxies()
class SearchApi: class SearchApi:
""" GoPlay Search API """ """ GoPlay Search API """
@ -37,9 +39,9 @@ class SearchApi:
"query": query, "query": query,
"page": 0, "page": 0,
"mode": "programs" "mode": "programs"
} },
proxies=PROXIES
) )
_LOGGER.debug(response.content)
response.raise_for_status() response.raise_for_status()
data = json.loads(response.text) data = json.loads(response.text)