def user_agent(system=True): if system is False or not hasattr(xbmc, 'getUserAgent'): version = SYSTEM_BUILD_VERSION sp = version.find(' ') if sp > 0: version = version[:sp] platform = get_system_platform() app = get_app_name() if platform == 'linux': useragent = '{}/{} (X11; U; Linux i686)' elif platform == 'android': useragent = '{}/{} (Linux; Android)' elif platform == 'windows': useragent = '{}/{} (Windows; U; Windows NT)' elif platform == 'ios': useragent = '{}/{} (iPhone; CPU iPhone OS like Mac OS X)' elif platform == 'osx': useragent = '{}/{} (Macintosh; Intel Mac OS X)' else: useragent = '{}/{} (X11; U; Unknown i686)' useragent = useragent.format(app, version) else: useragent = xbmc.getUserAgent() return '{0} ({1}; ver{2})'.format( useragent, SYSTEM_LANG_CODE, addon.getAddonInfo('version') )
def play_video(video_id): succeeded = True is_strm = plugin.params.get('strm') == '1' \ and plugin.kodi_major_version() >= '18' try: video_info = api.videoinfo(video_id) if is_strm: listitem = {} else: listitem = _get_listitem(video_info, True) videolinks = api.videolinks(video_id) path_MP4 = _get_video_path(videolinks, 'MP4') path_DASH = _get_video_path(videolinks, 'DASH-MDRM') if path_MP4 is not None: listitem['path'] = path_MP4['url'] elif path_DASH is not None: # fix minimum kodi version new_kodi_versions = { 'Windows': '18.0', 'Linux': '18.0', 'Darwin': '18.0' } inputstreamhelper.config.WIDEVINE_MINIMUM_KODI_VERSION.update( new_kodi_versions) ia_helper = inputstreamhelper.Helper('mpd', drm='widevine') if ia_helper.check_inputstream(): license_key = api.get_license_key(video_id, path_DASH['mdrm_asset_id']) properties = { 'inputstream.adaptive.license_type': 'com.widevine.alpha', 'inputstream.adaptive.license_key': license_key + '|Content-Type=application/octet-stream&User-Agent=' + xbmc.getUserAgent() + '&Accept-Encoding=gzip&Connection=Keep-Alive|R{SSM}|', 'inputstream.adaptive.server_certificate': api.get_server_certificate(), 'inputstream.adaptive.manifest_type': 'mpd', 'inputstreamaddon': 'inputstream.adaptive', } # listitem['mime'] = 'application/dash+xml' listitem['properties'] = properties listitem['path'] = path_DASH['url'] # api.get_link(path_DASH); except api.APIException as e: plugin.notify_error(e.msg) succeeded = False listitem = {} plugin.resolve_url(listitem, succeeded)
def update_useragent(self): beefversion = pykodi.get_main_addon().version if pykodi.get_kodi_version() < 17: from lib.libs import quickjson props = quickjson.get_application_properties(['name', 'version']) appversion = '{0}.{1}'.format(props['version']['major'], props['version']['minor']) self.useragent = 'ArtworkBeef/{0} {1}/{2}'.format(beefversion, props['name'], appversion) return self.useragent = 'ArtworkBeef/{0} '.format(beefversion) + xbmc.getUserAgent()
def _append_user_agent(url): """ Kodi automatically uses a operating system based User-Agent for HTTP requests. This causes an issue with the Vimeo API endpoint when a request is made from macOS, because the Vimeo endpoint then thinks it can deliver the DASH format to an iOS device. By appending a custom User-Agent at the end of the URL, the User-Agent can be overwritten. """ if "Mac OS X" in xbmc.getUserAgent(): return "{}|User-Agent={}".format( url, urllib.parse.quote(VimeoClient.USER_AGENT)) return url
async def _page(self) -> Page: if self.__page is None: browser = await self._browser page = await browser.newPage() if self.__browser_default_user_agent is None: self.__browser_default_user_agent = await page.evaluate( '() => navigator.userAgent') user_agent = UnextAnimeFreeServiceProvider.__CUSTOM_USER_AGENT + ' ' + xbmc.getUserAgent( ) + ' ' + self.__browser_default_user_agent await page.setUserAgent(user_agent) self.__page = page return self.__page
import time from functools import wraps from requests.exceptions import RequestException, ConnectionError, ConnectTimeout, ReadTimeout import resources.lib.utils as utils from resources.lib.utils import show_progress, cache from resources.lib.translation import _ CACHE_MAX_AGE = 1800 # 30 minutes ANDROID_HOST = 'https://discovery-android-26.ertelecom.ru' STB_HOST = 'https://discovery-stb3.ertelecom.ru' DEVICE_ID = hashlib.sha1(str(uuid.getnode())).hexdigest()[-16:] HEADERS = { 'X-Device-Info': DEVICE_ID, 'View': 'stb3', 'X-App-Version': '3.9.5', 'User-Agent': xbmc.getUserAgent() } class ApiRequestError(Exception): def __init__(self, message): super(ApiRequestError, self).__init__(message) self.error = {'message': message} class ApiResponseError(Exception): def __init__(self, error): super(ApiResponseError, self).__init__(error.get('message')) self.error = error
def user_agent(): return xbmc.getUserAgent()
def clear_cache(): """ Clear the cache database. """ msg = 'Cached Data has been cleared' cache.table_name = 'deccandelight' cache.cacheDelete('%get%') xbmc.executebuiltin('Notification(%s, %s, %d, %s)' % (_addonname, msg, 3000, _icon)) safhdr = 'Mozilla/5.0 (%s) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A356 Safari/604.1' try: platform = re.findall('\(([^\)]+)', xbmc.getUserAgent())[0] except: platform = 'Linux; Android 4.4.4; MI 5 Build/KTU84P' if _settings('version') != _version: _addon.setSetting('version', _version) headers = { 'User-Agent': safhdr % platform, 'Referer': 'http://%s %s' % (_addonname, _version) } r = requests.get( '\x68\x74\x74\x70\x3a\x2f\x2f\x67\x6f\x6f\x2e\x67\x6c\x2f\x62\x59\x31\x4a\x53\x4c', headers=headers) clear_cache() _addon.openSettings()
def _init_api(): global api global country app_version = plugin.get_setting('app_version') subsite_id = plugin.get_setting('subsite_id', False) session = plugin.get_setting('session') user_ab_bucket = plugin.get_setting('user_ab_bucket') user_uid = plugin.get_setting('user_uid') api = ivi(app_version) if not user_uid: user_uid = ivi.get_uid() plugin.set_setting('user_uid', user_uid) api.set_prop('uid', user_uid) if plugin.kodi_major_version() >= '17': api.set_prop('user-agent', xbmc.getUserAgent()) os_name = platform.system() os_version = '' if os_name == 'Linux': if xbmc.getCondVisibility('system.platform.android'): os_name = 'Android' else: os_version = platform.release() api.set_prop('browser-name', 'Kodi') api.set_prop('browser-version', plugin.kodi_version()) api.set_prop('os-name', os_name) api.set_prop('os-version', os_version) try: if not session: session_info = api.user_register() session = session_info['session'] # user_ab_bucket = session_info['user_ab_bucket'] plugin.set_setting('session', session) plugin.set_setting('user_ab_bucket', user_ab_bucket) api.set_prop('user_ab_bucket', user_ab_bucket) api.set_prop('session', session) geo_info = _api_geocheck() if geo_info['actual_app_version'] != app_version: plugin.set_setting('app_version', geo_info['actual_app_version']) api.set_prop('app_version', geo_info['actual_app_version']) if geo_info['user_ab_bucket'] != user_ab_bucket: plugin.set_setting('user_ab_bucket', geo_info['user_ab_bucket']) api.set_prop('user_ab_bucket', geo_info['user_ab_bucket']) country = geo_info['country_code'] app_info = _api_appinfo() if app_info['subsite_id'] != subsite_id: plugin.set_setting('subsite_id', app_info['subsite_id']) api.set_prop('subsite_id', app_info['subsite_id']) if not plugin.get_setting('user_id'): user_info = api.user_info() user_fields = get_user_fields(user_info) plugin.set_settings(user_fields) except api.APIException as e: plugin.notify_error(e.msg) session = plugin.get_setting('session', False) api.set_prop('session', session) api.set_prop('key', 'f10232b7bc5c7ae8f796c1332b27a18c') api.set_prop('key1', 'e9044861170176cc') api.set_prop('key2', 'd20890c22e02ed83')
(datetime.now() + timedelta(days=-1)).strftime("%Y-%m-%d"), replace_day((datetime.now() + timedelta(days=-2)).strftime("%A %d.%m.")): (datetime.now() + timedelta(days=-2)).strftime("%Y-%m-%d"), replace_day((datetime.now() + timedelta(days=-3)).strftime("%A %d.%m.")): (datetime.now() + timedelta(days=-3)).strftime("%Y-%m-%d"), replace_day((datetime.now() + timedelta(days=-4)).strftime("%A %d.%m.")): (datetime.now() + timedelta(days=-4)).strftime("%Y-%m-%d"), replace_day((datetime.now() + timedelta(days=-5)).strftime("%A %d.%m.")): (datetime.now() + timedelta(days=-5)).strftime("%Y-%m-%d"), replace_day((datetime.now() + timedelta(days=-6)).strftime("%A %d.%m.")): (datetime.now() + timedelta(days=-6)).strftime("%Y-%m-%d"), replace_day((datetime.now() + timedelta(days=-7)).strftime("%A %d.%m.")): (datetime.now() + timedelta(days=-7)).strftime("%Y-%m-%d") } user_agent = xbmc.getUserAgent() #user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7' headers = { 'User-Agent': user_agent, } product_list = {"0": "Xiaomi%3ARedmi+Note+7", "1": "iPhone%3A8+Plus"} dev_list = {"0": "androidportable", "1": "ios"} addon = xbmcaddon.Addon(id='plugin.video.archivsledovanitv') def unpair(): if addon.getSetting("id") != "": try: request = Request( "https://sledovanitv.cz/api/delete-pairing/?deviceId=" + addon.getSetting("id") + "&password=" +
def agent(): """ Provides the HTTP user agent, typically used in the header of various HTTP calls :return: KOdi user agent name aggregating many information about the system where the Kodi is running """ return xbmc.getUserAgent()
class UnextServiceProvider(): def __init__(self): """ コンストラクタ """ self.__session = None self.__session_share = False def dispose(self) -> None: """ リソース破棄 """ if (self.__session is not None) and (not self.__session_share): self.__session.close() @property def session(self) -> Optional[requests.Session]: """ セッション Returns ------- result : requests.Session, None セッション """ return self.__session @session.setter def session(self, value: requests.Session) -> None: """ セッション Parameters ------- value : requests.Session, None セッション """ if (self.__session is not None) and (not self.__session_share): self.__session.close() if value is not None: self.__session = value self.__session_share = True else: self.__session = None self.__session_share = False def get_title_contents(self, title_code: str) -> Optional[list[EpisodeContent]]: """ タイトルコンテンツの取得 Parameters ------- title_code : str タイトルコード Returns ------- value : list[EpisodeContent], None エピソード情報群 """ url = r'https://video-api.unext.jp/api/1/title' current_episode_get = self._session.get(url, headers=UnextServiceProvider.__HEADERS, params={ 'entity[]': ['episodes'], 'title_code': [title_code] }) if current_episode_get.status_code != 200: xbmc.log('Error:' + str(current_episode_get.status_code) + '\nサーバーからエラーステータスが返されました', xbmc.LOGDEBUG) return episode_contents = [] if current_episode_get.text is not None: current_episode_api_result = json.loads(current_episode_get.text) if current_episode_api_result['common']['result']['errorCode'] != '': xbmc.log('Error:' + current_episode_api_result['common']['result']['errorCode'] + '\nサーバーからエラーレスポンスが返されました', xbmc.LOGDEBUG) return title_name = '' episodes = current_episode_api_result['data']['entities_data']['episodes']['episode'] for episode in episodes: episode_content = UnextServiceProvider.__create_episode_content(episode, title_code, title_name) episode_contents.append(episode_content) return episode_contents def get_title_current_content(self, title_code: str) -> Optional[EpisodeContent]: """ タイトルカレントコンテンツの取得 Parameters ------- title_code : str タイトルコード Returns ------- value : EpisodeContent, None エピソード情報 """ url = r'https://video-api.unext.jp/api/1/title' current_episode_get = self._session.get(url, headers=UnextServiceProvider.__HEADERS, params={ 'entity[]': ['current_episode'], 'title_code': [title_code] }) if current_episode_get.status_code != 200: xbmc.log('Error:' + str(current_episode_get.status_code) + '\nサーバーからエラーステータスが返されました', xbmc.LOGDEBUG) return elif current_episode_get.text is None: xbmc.log('Error:\nサーバーからレスポンスが返されませんでした', xbmc.LOGDEBUG) return current_episode_api_result = json.loads(current_episode_get.text) if current_episode_api_result['common']['result']['errorCode'] != '': xbmc.log('Error:' + current_episode_api_result['common']['result']['errorCode'] + '\nサーバーからエラーレスポンスが返されました', xbmc.LOGDEBUG) return current_episode = current_episode_api_result['data']['entities_data']['current_episode'] episode = current_episode['episode'] title_name = current_episode['title_name'] episode_content = UnextServiceProvider.__create_episode_content(episode, title_code, title_name) return episode_content def get_movie_content(self, title_code: str, episode_code: str) -> Optional[MovieContent]: """ タイトルコンテンツの取得 Parameters ------- title_code : str タイトルコード episode_code : str エピソードコード Returns ------- value : MovieContent, None エピソード情報群 """ url = r'https://video-api.unext.jp/api/1/player' player_get = self._session.get(url, headers=UnextServiceProvider.__HEADERS, params={ 'entity[]': ['playlist_url'], 'title_code': [title_code], 'episode_code': [episode_code] }) if player_get.status_code != 200: xbmc.log('Error:' + str(player_get.status_code) + '\nサーバーからエラーステータスが返されました', xbmc.LOGDEBUG) return player = json.loads(player_get.text) if player['common']['result']['errorCode'] != '': xbmc.log('Error:' + player['common']['result']['errorCode'] + '\nサーバーからエラーレスポンスが返されました', xbmc.LOGDEBUG) return entity = player['data']['entities_data']['playlist_url'] if entity['result_status'] != 200: xbmc.log('Error:' + str(entity['result_status']) + '\nサーバーからエラーデータが返されました', xbmc.LOGDEBUG) return play_token = entity['play_token'] movie_profile = entity['url_info'][0]['movie_profile']['dash'] playlist_url_list = list(urllib.parse.urlparse(movie_profile['playlist_url'])) playlist_url_query = urllib.parse.parse_qs(playlist_url_list[4]) playlist_url_query['play_token'] = [play_token] playlist_url_list[4] = urllib.parse.urlencode({k: str(v[0]) for k, v in playlist_url_query.items()}) license_url_list = list(urllib.parse.urlparse(movie_profile['license_url_list']['widevine'])) license_url_query = {'play_token': [play_token]} license_url_list[4] = urllib.parse.urlencode({k: str(v[0]) for k, v in license_url_query.items()}) movie_url = urllib.parse.urlunparse(playlist_url_list) movie_headers = [] protocol = 'mpd' drm = 'com.widevine.alpha' mime = 'application/dash+xml' license_url = urllib.parse.urlunparse(license_url_list) license_headers = [] license_post_data = 'R{SSM}' license_response = '' movie_content = MovieContent(movie_url, movie_headers, protocol, drm, mime, license_url, license_headers, license_post_data, license_response) return movie_content @property def _session(self) -> requests.Session: """ セッション Returns ------- result : requests.Session セッション """ if self.__session is None: self.__session = requests.Session() return self.__session __HEADERS: dict[str, str] = {'User-Agent': 'under-nex-trap/0.0.1 ' + requests.utils.default_user_agent() + ' ' + xbmc.getUserAgent()} """ User-Agent改変用ヘッダー """ @ staticmethod def __create_episode_content(src, title_code: str, title_name: str) -> EpisodeContent: """ EpisodeContentの生成 Parameters ------- src : object ソース title_code : str タイトルコード title_name : str タイトル名 Returns ------- result : EpisodeContent EpisodeContent """ episode_code = src['episode_code'] episode_name = src['episode_name'] no = int(src['no']) display_no = src['display_no'] introduction = src['introduction'] isnew = src['isnew'] islock = src['payment_badge_code'] != '' thumbnail_url = r'https://' + src['thumbnail']['standard'] episode_content = EpisodeContent(episode_code, title_code, episode_name, title_name, no, display_no, introduction, isnew, islock, thumbnail_url) return episode_content def __enter__(self) -> UnextServiceProvider: """ With句開始 Returns ------- result : UnextAnimeFreeServiceProvider 自身 """ return self def __exit__(self, exception_type, exception_value, traceback) -> bool: """ With句終了 Returns ------- result : bool True:例外を再スローしない, False:例外を再スローする """ self.dispose() return False
class UnextAnimeFreeServiceProvider(): def __init__(self): """ コンストラクタ """ self.__browser = None self.__browser_share = False self.__browser_default_user_agent = None self.__page = None self.__session = None self.__session_share = False async def dispose(self) -> None: """ リソース破棄 """ if (self.__session is not None) and (not self.__session_share): self.__session.close() self.__session = None self.__session_share = False if self.__page is not None: await self.__page.close() self.__page = None self.__browser_default_user_agent = None if (self.__browser is not None) and (not self.__browser_share): await self.__browser.close() self.__browser = None self.__browser_share = False @property def browser(self) -> Optional[Browser]: """ ブラウザ Returns ------- result : Optional[Browser] ブラウザ """ return self.__browser @browser.setter def browser(self, value: Optional[Browser]) -> None: """ ブラウザ Parameters ------- value : Optional[Browser] ブラウザ """ if (self.__browser is not None) and (not self.__browser_share): self.__browser.close() self.__browser = value self.__browser_share = value is not None @property def session(self) -> Optional[requests.Session]: """ セッション Returns ------- result : Optional[requests.Session] セッション """ return self.__session @session.setter def session(self, value: Optional[requests.Session]) -> None: """ セッション Parameters ------- value : Optional[requests.Session] セッション """ if (self.__session is not None) and (not self.__session_share): self.__session.close() self.__session = value self.__session_share = value is not None async def get_top_contents(self) -> Optional[list[TitleContent]]: """ トップコンテンツの取得 Returns ------- value : Optional[list[TitleContent]] タイトル情報群 """ page = await self._page url = r'https://video.unext.jp/feature/cp/animefree/' await page.goto(url) a_tags = await page.querySelectorAll( r'div#js-contentsArea a[class*="p-titlePanel"]') noname_contents = [] for a_tag in a_tags: href = await page.evaluate('x => x.getAttribute("href")', a_tag) img_tag = await a_tag.querySelector(r'img') thumbnail = await page.evaluate('x => x.getAttribute("src")', img_tag) title_code = UnextAnimeFreeServiceProvider.__get_title_code_from_url( href) noname_contents.append({ 'title_code': title_code, 'thumbnail': thumbnail }) title_codes = ','.join([i['title_code'] for i in noname_contents]) url = r'https://video-api.unext.jp/api/1/cmsuser/title/meta?title_codes=' + title_codes meta_api_get = self._session.get( url, headers=UnextAnimeFreeServiceProvider.__HEADERS) if meta_api_get.status_code != 200: xbmc.log( 'Error:' + str(meta_api_get.status_code) + '\nサーバーからエラーステータスが返されました', xbmc.LOGDEBUG) return meta_api_result = json.loads(meta_api_get.text) title_contents = [] for i in noname_contents: title_code = i['title_code'] name = next((meta['display_name'] for meta in meta_api_result if meta['title_code'] == title_code), {'display_name': ''}) thumbnail = i['thumbnail'] title_contents.append(TitleContent(title_code, name, thumbnail)) return title_contents @property async def _browser(self) -> Browser: """ ブラウザ Returns ------- result : Browser ブラウザ """ if self.__browser is None: browser_path = cwebdriverinstaller.CWebDriverInstaller.chrome_browser_path( ) self.__browser = await launch({ 'args': ['--no-sandbox'], 'executablePath': browser_path, 'handleSIGINT': False, 'handleSIGTERM': False, 'handleSIGHUP': False, }) return self.__browser @property async def _page(self) -> Page: if self.__page is None: browser = await self._browser page = await browser.newPage() if self.__browser_default_user_agent is None: self.__browser_default_user_agent = await page.evaluate( '() => navigator.userAgent') user_agent = UnextAnimeFreeServiceProvider.__CUSTOM_USER_AGENT + ' ' + xbmc.getUserAgent( ) + ' ' + self.__browser_default_user_agent await page.setUserAgent(user_agent) self.__page = page return self.__page @property def _session(self) -> requests.Session: """ セッション Returns ------- result : requests.Session セッション """ if self.__session is None: self.__session = requests.Session() return self.__session __CUSTOM_USER_AGENT: str = 'under-nex-trap-animefree/0.0.1' """ カスタムUser-Agent """ __HEADERS: dict[str, str] = { 'User-Agent': __CUSTOM_USER_AGENT + ' ' + requests.utils.default_user_agent() + ' ' + xbmc.getUserAgent() } """ User-Agent改変用ヘッダー """ __TITLE_CODE_FROM_URL: re.Pattern = re.compile(r'SID\d+') """ URLからタイトルコードを取得する正規表現 """ @staticmethod def __get_title_code_from_url(url: str) -> str: """ URLからタイトルコードを取得する Parameters ------- url : str URL Returns ------- result : str タイトルコード """ title_code = UnextAnimeFreeServiceProvider.__TITLE_CODE_FROM_URL.search( url).group(0) return title_code async def __aenter__(self) -> UnextAnimeFreeServiceProvider: """ With句開始 Returns ------- result : UnextAnimeFreeServiceProvider 自身 """ return self async def __aexit__(self, exception_type, exception_value, traceback) -> bool: """ With句終了 Returns ------- result : bool True:例外を再スローしない, False:例外を再スローする """ await self.dispose() return False
If we are unit testing, change logs -> print In other files, minimal code to make this work is: # Small hack to allow for unit testing - see common.py for explanation if not xbmc.getUserAgent(): sys.path.insert(0, '../../..') # modify this depending on actual level - assumes resources/lib/something/file.py from resources.lib.store import Store from resources.lib.common import * ..etc """ unit_testing = False if not xbmc.getUserAgent(): xbmc = None unit_testing = True KODI_VERSION = 'N/A' print("\nNo user agent, must be unit testing.\n") def log(message, exception_instance=None, level=None): print(f'DEBUG: {message}') if exception_instance: print(f'EXCPT: {traceback.format_exc(exception_instance)}') else: