"""
urlresolver Kodi Addon
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
"""
import re
from urllib import quote_plus
from urllib2 import HTTPError
import json
from urlresolver import common
from urlresolver.common import i18n
from urlresolver.resolver import UrlResolver, ResolverError
logger = common.log_utils.Logger.get_logger(__name__)
logger.disable()
AGENT = 'URLResolver for Kodi'
VERSION = common.addon_version
USER_AGENT = '%s/%s' % (AGENT, VERSION)
FORMATS = common.VIDEO_FORMATS
api_url = 'https://api.alldebrid.com'
class AllDebridResolver(UrlResolver):
name = "AllDebrid"
domains = ['*']
def __init__(self):
self.net = common.Net()
self.hosters = None
self.hosts = None
self.headers = {'User-Agent': USER_AGENT}
def get_media_url(self, host, media_id, cached_only=False):
try:
if media_id.lower().startswith('magnet:'):
r = re.search('''magnet:.+?urn:([a-zA-Z0-9]+):([a-zA-Z0-9]+)''', media_id, re.I)
if r:
_hash, _format = r.group(2), r.group(1)
if self.__check_cache(_hash):
logger.log_debug('AllDebrid: BTIH %s is readily available to stream' % _hash)
transfer_id = self.__create_transfer(_hash)
else:
if self.get_setting('cached_only') == 'true' or cached_only:
raise ResolverError('AllDebrid: Cached torrents only allowed to be initiated')
else:
transfer_id = self.__create_transfer(_hash)
self.__initiate_transfer(transfer_id)
transfer_info = self.__list_transfer(transfer_id)
for _link, _file in transfer_info.get('links').items():
if any(_file.lower().endswith(x) for x in FORMATS):
media_id = _link.replace("\/", "/")
break
self.__delete_transfer(transfer_id)
url = '%s/link/unlock?agent=%s&version=%s&token=%s&link=%s' % (api_url, quote_plus(AGENT), quote_plus(VERSION), self.get_setting('token'), media_id)
result = self.net.http_GET(url, headers=self.headers).content
except HTTPError as e:
try:
js_result = json.loads(e.read())
if 'error' in js_result:
msg = '%s (%s)' % (js_result.get('error'), js_result.get('errorCode'))
else:
msg = 'Unknown Error (1)'
except:
msg = 'Unknown Error (2)'
raise ResolverError('AllDebrid Error: %s (%s)' % (msg, e.code))
else:
js_result = json.loads(result)
logger.log_debug('AllDebrid resolve: [%s]' % js_result)
if 'error' in js_result:
raise ResolverError('AllDebrid Error: %s (%s)' % (js_result.get('error'), js_result.get('errorCode')))
elif js_result.get('success', False):
if js_result.get('infos').get('link'):
return js_result.get('infos').get('link')
raise ResolverError('AllDebrid: no stream returned')
def __check_cache(self, media_id):
try:
url = '%s/magnet/instant?agent=%s&version=%s&token=%s&magnet=%s' % (api_url, quote_plus(AGENT), quote_plus(VERSION), self.get_setting('token'), media_id)
result = self.net.http_GET(url, headers=self.headers).content
result = json.loads(result)
if result.get('success', False):
response = result.get('instant', False)
return response
except:
pass
return False
def __list_transfer(self, transfer_id):
try:
url = '%s/magnet/status?agent=%s&version=%s&token=%s&id=%s' % (api_url, quote_plus(AGENT), quote_plus(VERSION), self.get_setting('token'), transfer_id)
response = self.net.http_GET(url, headers=self.headers).content
result = json.loads(response)
if result.get('success', False):
return result
except:
pass
return {}
def __create_transfer(self, media_id):
try:
url = '%s/magnet/upload?agent=%s&version=%s&token=%s&magnet=%s' % (api_url, quote_plus(AGENT), quote_plus(VERSION), self.get_setting('token'), media_id)
response = self.net.http_GET(url, headers=self.headers).content
result = json.loads(response)
if result.get('success', False):
logger.log_debug('Transfer successfully started to the AllDebrid cloud')
return result.get('id', "")
except:
pass
return ""
def __initiate_transfer(self, transfer_id, interval=5):
try:
transfer_info = self.__list_transfer(transfer_id)
if transfer_info:
line1 = transfer_info.get('filename')
line2 = 'Saving torrent to UptoBox via AllDebrid'
line3 = transfer_info.get('status')
with common.kodi.ProgressDialog('URL Resolver AllDebrid Transfer', line1, line2, line3) as pd:
while not transfer_info.get('statusCode') == 4:
common.kodi.sleep(1000 * interval)
transfer_info = self.__list_transfer(transfer_id)
file_size = transfer_info.get('size')
line1 = transfer_info.get('filename')
if transfer_info.get('statusCode') == 1:
download_speed = round(float(transfer_info.get('downloadSpeed')) / (1000**2), 2)
progress = int(float(transfer_info.get('downloaded')) / file_size * 100) if file_size > 0 else 0
line3 = "Downloading at %s MB/s from %s peers, %s%% of %sGB completed" % (download_speed, transfer_info.get('seeders'), progress, round(float(file_size) / (1000 ** 3), 2))
elif transfer_info.get('statusCode') == 3:
upload_speed = round(float(transfer_info.get('uploadSpeed')) / (1000 ** 2), 2)
progress = int(float(transfer_info.get('uploaded')) / file_size * 100) if file_size > 0 else 0
line3 = "Uploading at %s MB/s, %s%% of %s GB completed" % (upload_speed, progress, round(float(file_size) / (1000 ** 3), 2))
else:
line3 = transfer_info.get('status')
progress = 0
logger.log_debug(line3)
pd.update(progress, line1=line1, line3=line3)
if pd.is_canceled():
self.__delete_transfer(transfer_id)
# self.__delete_folder()
raise ResolverError('Transfer ID %s :: Canceled by user' % transfer_id)
elif 5 <= transfer_info.get('statusCode') <= 10:
self.__delete_transfer(transfer_id)
# self.__delete_folder()
raise ResolverError('Transfer ID %s :: %s' % (transfer_id, transfer_info.get('status')))
common.kodi.sleep(1000 * interval) # allow api time to generate the links
return
except Exception as e:
self.__delete_transfer(transfer_id)
raise ResolverError('Transfer ID %s :: %s' % (transfer_id, e))
def __delete_transfer(self, transfer_id):
try:
url = '%s/magnet/delete?agent=%s&version=%s&token=%s&id=%s' % (api_url, quote_plus(AGENT), quote_plus(VERSION), self.get_setting('token'), transfer_id)
response = self.net.http_GET(url, headers=self.headers).content
result = json.loads(response)
if result.get('success', False):
logger.log_debug('Transfer ID "%s" deleted from the AllDebrid cloud' % transfer_id)
return True
except:
pass
return False
def get_url(self, host, media_id):
return media_id
def get_host_and_id(self, url):
return 'www.alldebrid.com', url
@common.cache.cache_method(cache_limit=8)
def get_all_hosters(self):
hosters = []
url = '%s/user/hosts?agent=%s&version=%s&token=%s' % (api_url, quote_plus(AGENT), quote_plus(VERSION), self.get_setting('token'))
try:
js_result = self.net.http_GET(url, headers=self.headers).content
js_data = json.loads(js_result)
if js_data.get('success', False):
regexes = [value.get('regexp').replace('\/', '/') for key, value in js_data.get('hosts', {}).iteritems()
if value.get('status', False)]
logger.log_debug('AllDebrid hosters : %s' % regexes)
hosters = [re.compile(regex) for regex in regexes]
else:
logger.log_error('Error getting AD Hosters')
except Exception as e:
logger.log_error('Error getting AD Hosters: %s' % e)
return hosters
@common.cache.cache_method(cache_limit=8)
def get_hosts(self):
hosts = []
url = '%s/hosts/domains' % api_url
try:
js_result = self.net.http_GET(url, headers=self.headers).content
js_data = json.loads(js_result)
if js_data.get('success', False):
hosts = [host.replace('www.', '') for host in js_data.get('hosts', [])]
if self.get_setting('torrents') == 'true':
hosts.extend([u'torrent', u'magnet'])
logger.log_debug('AllDebrid hosts : %s' % hosts)
else:
logger.log_error('Error getting AD Hosters')
except Exception as e:
logger.log_error('Error getting AD Hosts: %s' % e)
return hosts
def valid_url(self, url, host):
logger.log_debug('in valid_url %s : %s' % (url, host))
if url:
if url.lower().startswith('magnet:') and self.get_setting('torrents') == 'true':
return True
if self.hosters is None:
self.hosters = self.get_all_hosters()
for regexp in self.hosters:
# logger.log_debug('AllDebrid checking host : %s' %str(regexp))
if re.search(regexp, url):
logger.log_debug('AllDebrid Match found')
return True
elif host:
if self.hosts is None:
self.hosts = self.get_hosts()
if any(host in item for item in self.hosts):
return True
return False
# SiteAuth methods
def login(self):
if not self.get_setting('token'):
self.authorize_resolver()
def reset_authorization(self):
self.set_setting('token', '')
def authorize_resolver(self):
url = '%s/pin/get?agent=%s&version=%s' % (api_url, quote_plus(AGENT), quote_plus(VERSION))
js_result = self.net.http_GET(url, headers=self.headers).content
js_data = json.loads(js_result)
line1 = 'Go to URL: %s' % (js_data.get('base_url').replace('\/', '/'))
line2 = 'When prompted enter: %s' % (js_data.get('pin'))
with common.kodi.CountdownDialog('Url Resolver All Debrid Authorization', line1, line2,
countdown=js_data.get('expired_in', 120)) as cd:
result = cd.start(self.__check_auth, [js_data.get('check_url').replace('\/', '/')])
# cancelled
if result is None:
return
return self.__get_token(js_data.get('check_url').replace('\/', '/'))
def __get_token(self, url):
try:
js_result = self.net.http_GET(url, headers=self.headers).content
js_data = json.loads(js_result)
if js_data.get("success", False):
token = js_data.get('token', '')
logger.log_debug('Authorizing All Debrid Result: |%s|' % token)
self.set_setting('token', token)
return True
except Exception as e:
logger.log_debug('All Debrid Authorization Failed: %s' % e)
return False
def __check_auth(self, url):
activated = False
try:
js_result = self.net.http_GET(url, headers=self.headers).content
js_data = json.loads(js_result)
if js_data.get("success", False):
activated = js_data.get('activated', False)
except Exception as e:
logger.log_debug('Exception during AD auth: %s' % e)
return activated
@classmethod
def get_settings_xml(cls):
xml = super(cls, cls).get_settings_xml()
# xml.append('' % (cls.__name__, i18n('auto_primary_link')))
xml.append('' % (cls.__name__, i18n('torrents')))
xml.append('' % (cls.__name__, i18n('cached_only')))
xml.append('' % (cls.__name__, i18n('auth_my_account')))
xml.append('' % (cls.__name__, i18n('reset_my_auth')))
xml.append('' % cls.__name__)
return xml
@classmethod
def _is_enabled(cls):
return cls.get_setting('enabled') == 'true' and cls.get_setting('token')
@classmethod
def isUniversal(self):
return True