def playlist(output, **kwargs): user_providers = [x.lower() for x in userdata.get('merge_providers', [])] if not user_providers: raise PluginError(_.NO_PROVIDERS) avail_providers = _providers(playlist=True) providers = [x for x in avail_providers if x in user_providers] if not providers: raise PluginError(_.NO_PROVIDERS) with codecs.open(output, 'w', encoding='utf8') as f: f.write(u'#EXTM3U') for key in sorted( providers, key=lambda x: (avail_providers[x]['sort'], avail_providers[x]['name'].lower())): provider = avail_providers[key] for channel in sorted(provider['channels'], key=lambda x: x['title'].lower().strip()): f.write( u'\n#EXTINF:-1 tvg-id="{id}" tvg-name="{name}" tvg-logo="{logo}" group-title="{provider}",{name}\n{url}' .format( id=channel['id'], name=channel['title'], logo=channel['thumb'], provider=provider['name'], url=plugin.url_for(play, id=channel['id'], _is_live=True), ))
def epg(output, **kwargs): user_providers = [x.lower() for x in userdata.get('merge_providers', [])] if not user_providers: raise PluginError(_.NO_PROVIDERS) avail_providers = _providers(epg=True) providers = [x for x in avail_providers if x in user_providers] if not providers: raise PluginError(_.NO_PROVIDERS) with codecs.open(output, 'w', encoding='utf8') as f: f.write(u'<?xml version="1.0" encoding="utf-8" ?><tv>') for key in providers: provider = avail_providers[key] for channel in provider['channels']: f.write( u'<channel id="{id}"></channel>'.format(id=channel['id'])) def write_program(program): if not program: return start = arrow.get(program['airTime']).to('utc') stop = start.shift(minutes=program['duration']) series = program.get('seasonNumber') or 0 episode = program.get('episodeNumber') or 0 icon = program.get('primaryImageUrl') desc = program.get('description') subtitle = program.get('episodeTitle') icon = u'<icon src="{}"/>'.format( escape(icon)) if icon else '' episode = u'<episode-num system="onscreen">S{}E{}</episode-num>'.format( series, episode) if series and episode else '' subtitle = u'<sub-title>{}</sub-title>'.format( escape(subtitle)) if subtitle else '' desc = u'<desc>{}</desc>'.format( escape(desc)) if desc else '' f.write( u'<programme channel="{id}" start="{start}" stop="{stop}"><title>{title}</title>{subtitle}{icon}{episode}{desc}</programme>' .format(id=channel['id'], start=start.format('YYYYMMDDHHmmss Z'), stop=stop.format('YYYYMMDDHHmmss Z'), title=escape(program['title']), subtitle=subtitle, episode=episode, icon=icon, desc=desc)) write_program(channel['currentEpisode']) for program in channel['upcomingEpisodes']: write_program(program) f.write(u'</tv>')
def play(id, **kwargs): data = api.play(id) headers = {} headers.update(HEADERS) drm_info = data.get('drmInfo') or {} cookies = data.get('cookie') or {} if drm_info: if drm_info['drmScheme'].lower() == 'widevine': ia = inputstream.Widevine(license_key=drm_info['drmLicenseUrl'], ) headers.update(drm_info.get('drmKeyRequestProperties') or {}) else: raise PluginError('Unsupported Stream!') else: ia = inputstream.HLS(live=True) return plugin.Item( path=data['url'], inputstream=ia, headers=headers, cookies=cookies, resume_from=LIVE_HEAD, ## Need to seek to live over multi-periods )
def categories(id=None, **kwargs): folder = plugin.Folder(_.CATEGORIES) rows = api.categories() if id: row = _search_category(rows, id) if not row: raise PluginError(_(_.CATEGORY_NOT_FOUND, category_id=id)) folder.title = row['label'] rows = row.get('subcategories', []) for row in rows: subcategories = row.get('subcategories', []) if subcategories: path = plugin.url_for(categories, id=row['id']) else: path = plugin.url_for(media, title=row['label'], filterby='category', term=row['name']) folder.add_item( label=row['label'], art={'thumb': _image(row, 'image_url')}, path=path, ) return folder
def playlist(output, **kwargs): data = _app_data() regions = userdata.get('merge_regions', []) regions = [x for x in regions if x in data['regions']] if not regions: raise PluginError(_.NO_REGIONS) with codecs.open(output, 'w', encoding='utf8') as f: f.write(u'#EXTM3U') added = [] for code in regions: region = data['regions'][code] channels = region['channels'] for id in sorted(channels.keys(), key=lambda x: channels[x]['chno']): if id in added: continue added.append(id) channel = channels[id] f.write(u'\n#EXTINF:-1 tvg-id="{id}" tvg-chno="{chno}" tvg-name="{name}" tvg-logo="{logo}" group-title="{group}",{name}\n{url}'.format( id=id, chno=channel['chno'], name=channel['name'], logo=channel['logo'], group=channel['group'], url=plugin.url_for(play, id=id, _is_live=True), ))
def _add_profile(taken_names, taken_avatars): ## PROFILE AVATAR ## options = [ plugin.Item(label=_(_.RANDOM_AVATAR, _bold=True)), ] values = [ ['_random', None], ] avatars = [] unused = [] for icon_set in api.profile_icons(): for row in icon_set['icons']: icon_info = [icon_set['iconSet'], row['iconIndex']] values.append(icon_info) avatars.append(icon_info) if row['iconImage'] in taken_avatars: label = _(_.AVATAR_USED, label=icon_set['label']) else: label = icon_set['label'] unused.append(icon_info) options.append( plugin.Item(label=label, art={'thumb': row['iconImage']})) index = gui.select(_.SELECT_AVATAR, options=options, useDetails=True) if index < 0: return avatar = values[index] if avatar[0] == '_random': avatar = random.choice(unused or avatars) ## PROFLE KIDS ## kids = gui.yes_no(_.KIDS_PROFILE_INFO, heading=_.KIDS_PROFILE) ## PROFILE NAME ## name = '' while True: name = gui.input(_.PROFILE_NAME, default=name).strip() if not name: return elif name.lower() in taken_names: gui.notification(_(_.PROFILE_NAME_TAKEN, name=name)) else: break ## ADD PROFILE ## profile = api.add_profile(name, icon_set=avatar[0], icon_index=avatar[1], kids=kids) if 'message' in profile: raise PluginError(profile['message']) _set_profile(profile)
def _add_profile(taken_names, taken_avatars): ## PROFILE AVATAR ## options = [plugin.Item(label=_(_.RANDOM_AVATAR, _bold=True)),] values = ['_random',] avatars = {} unused = [] data = api.collection_by_slug('avatars', 'avatars') for container in data['containers']: if container['set']['contentClass'] == 'hidden': continue category = _get_text(container['set']['texts'], 'title', 'set') for row in container['set'].get('items', []): if row['images'][0]['url'] in taken_avatars: label = _(_.AVATAR_USED, label=category) else: label = category unused.append(row['avatarId']) options.append(plugin.Item(label=label, art={'thumb': row['images'][0]['url']})) values.append(row['avatarId']) avatars[row['avatarId']] = row['images'][0]['url'] index = gui.select(_.SELECT_AVATAR, options=options, useDetails=True) if index < 0: return avatar = values[index] if avatar == '_random': avatar = random.choice(unused or avatars.keys()) ## PROFLE KIDS ## kids = gui.yes_no(_.KIDS_PROFILE_INFO, heading=_.KIDS_PROFILE) ## PROFILE NAME ## name = '' while True: name = gui.input(_.PROFILE_NAME, default=name).strip() if not name: return elif name in taken_names: gui.notification(_(_.PROFILE_NAME_TAKEN, name=name)) else: break profile = api.add_profile(name, kids=kids, avatar=avatar) profile['_avatar'] = avatars[avatar] if 'errors' in profile: raise PluginError(profile['errors'][0].get('description')) _set_profile(profile)
def get_game(slug): game = Game.get_or_none(Game.slug == slug) if not game: try: game = api.fetch_game(slug) game.save() except: raise PluginError(_.ERROR_GAME_NOT_FOUND) return game
def _add_profile(taken_names, taken_avatars): ## PROFILE AVATAR ## options = [ plugin.Item(label=_(_.RANDOM_AVATAR, _bold=True)), ] values = [ '_random', ] avatars = [] unused = [] for avatar in api.profile_config()['avatars']: values.append(avatar['id']) avatars.append(avatar['id']) if avatar['id'] in taken_avatars: label = _(_.AVATAR_USED, _bold=True) else: label = _.AVATAR_NOT_USED unused.append(avatar['id']) options.append( plugin.Item(label=label, art={'thumb': _get_avatar(avatar['id'])})) index = gui.select(_.SELECT_AVATAR, options=options, useDetails=True) if index < 0: return avatar_id = values[index] if avatar_id == '_random': avatar_id = random.choice(unused or avatars) ## PROFILE NAME ## name = '' while True: name = gui.input(_.PROFILE_NAME, default=name).strip() if not name: return elif name.lower() in taken_names: gui.notification(_(_.PROFILE_NAME_TAKEN, name=name)) else: break ## ADD PROFILE ## profile = api.add_profile(name, avatar_id) if 'message' in profile: raise PluginError(profile['message']) _set_profile(profile)
def play(media_id, media_type, start=None, duration=None, **kwargs): if start: start = int(start) now = arrow.utcnow() if start > now.timestamp: raise PluginError(_.NOT_STARTED_YET) elif start < now.shift(hours=-24).timestamp: raise PluginError(_.EVENT_EXPIRED) data = api.play(media_id, media_type, start, duration) headers = HEADERS headers.update({'Authorization': 'bearer {}'.format(data['drmToken'])}) item = plugin.Item( path = data['path'], inputstream = inputstream.Widevine(license_key=WIDEVINE_URL), headers = headers, ) if media_type == MEDIA_CHANNEL: item.inputstream.properties['manifest_update_parameter'] = 'full' return item
def _get_play_item(game, game_type, play_type=PLAY_FROM_LIVE): play_type = int(play_type) item = parse_game(game) is_live = game.state == Game.LIVE item.inputstream = inputstream.HLS(live=is_live) if play_type == PLAY_FROM_START or (play_type == PLAY_FROM_ASK and not gui.yes_no(_.PLAY_FROM, yeslabel=_.PLAY_FROM_LIVE, nolabel=_.PLAY_FROM_START)): item.properties['ResumeTime'] = '1' item.properties['TotalTime'] = '1' if is_live and not item.inputstream.check(): raise PluginError(_.HLS_REQUIRED) item.path = api.get_play_url(game, game_type) return item
def play_vod(slug, **kwargs): data = api.content(slug) url = None for row in data.get('items', []): for row2 in row.get('items', []): if row2.get('videoUrl'): url = row2['videoUrl'] break if not url: raise PluginError(_.NO_VIDEO) parsed = urlparse(url) params = dict(parse_qsl(parsed.query)) return _play(params['accountId'], params['referenceId'], live=False)
def play(slug, game_type, play_type=PLAY_FROM_LIVE, **kwargs): play_type = int(play_type) game = get_game(slug) item = parse_game(game) is_live = game.state == Game.LIVE item.inputstream = inputstream.HLS(live=is_live) if play_type == PLAY_FROM_START or ( play_type == PLAY_FROM_ASK and not gui.yes_no(_.PLAY_FROM, yeslabel=_.PLAY_FROM_LIVE, nolabel=_.PLAY_FROM_START)): item.resume_from = 1 if is_live and not item.inputstream.check(): raise PluginError(_.HLS_REQUIRED) item.path = api.get_play_url(game, game_type) return item
def login(register=0, **kwargs): register = int(register) username = gui.input(_.ASK_USERNAME, default=userdata.get('username', '')).strip() if not username: return userdata.set('username', username) password = gui.input(_.ASK_PASSWORD, hide_input=True).strip() if not password: return if register and gui.input(_.CONFIRM_PASSWORD, hide_input=True).strip() != password: raise PluginError(_.PASSWORD_NOT_MATCH) api.login(username, password, register=register) gui.refresh()
def merge(**kwargs): if get_kodi_string('_iptv_merge_force_run'): raise PluginError(_.MERGE_IN_PROGRESS) else: set_kodi_string('_iptv_merge_force_run', '1')
def play(id, start_from=0, play_type=PLAY_FROM_LIVE, **kwargs): asset = api.stream(id) start_from = int(start_from) play_type = int(play_type) is_live = kwargs.get(ROUTE_LIVE_TAG) == ROUTE_LIVE_SUFFIX streams = [asset['recommendedStream']] streams.extend(asset['alternativeStreams']) streams = [s for s in streams if s['mediaFormat'] in SUPPORTED_FORMATS] if not streams: raise PluginError(_.NO_STREAM) prefer_cdn = settings.getEnum('prefer_cdn', AVAILABLE_CDNS) if prefer_cdn == CDN_AUTO: try: prefer_cdn = api.use_cdn(is_live)['useCDN'] except Exception as e: log.debug('Failed to get preferred cdn') prefer_cdn = None providers = [prefer_cdn] providers.extend([s['provider'] for s in streams]) streams = sorted(streams, key=lambda k: (providers.index(k['provider']), SUPPORTED_FORMATS.index(k['mediaFormat']))) stream = streams[0] log.debug('Stream CDN: {provider} | Stream Format: {mediaFormat}'.format( **stream)) item = plugin.Item( path=stream['manifest']['uri'], art=False, headers=HEADERS, ) item.headers.update( {'authorization': 'Bearer {}'.format(userdata.get('access_token'))}) if is_live and (play_type == PLAY_FROM_LIVE or (play_type == PLAY_FROM_ASK and gui.yes_no(_.PLAY_FROM, yeslabel=_.PLAY_FROM_LIVE, nolabel=_.PLAY_FROM_START))): play_type = PLAY_FROM_LIVE start_from = 0 ## Cloudfront streams start from correct position if stream['provider'] == CDN_CLOUDFRONT and start_from: start_from = 1 if stream['mediaFormat'] == FORMAT_DASH: item.inputstream = inputstream.MPD() elif stream['mediaFormat'] == FORMAT_HLS_TS: force = (is_live and play_type == PLAY_FROM_LIVE and asset['assetType'] != 'live-linear') item.inputstream = inputstream.HLS(force=force, live=is_live) if force and not item.inputstream.check(): raise PluginError(_.HLS_REQUIRED) elif stream['mediaFormat'] == FORMAT_HLS_FMP4: item.inputstream = inputstream.HLS(force=True, live=is_live) if not item.inputstream.check(): raise PluginError(_.HLS_REQUIRED) elif stream['mediaFormat'] in (FORMAT_DRM_DASH, FORMAT_DRM_DASH_HEVC): item.inputstream = inputstream.Widevine(license_key=LICENSE_URL, ) if start_from and not kwargs[ROUTE_RESUME_TAG]: item.properties['ResumeTime'] = start_from item.properties['TotalTime'] = start_from return item
def play(id, start_from=0, play_type=PLAY_FROM_LIVE, **kwargs): asset = api.stream(id) start_from = int(start_from) play_type = int(play_type) is_live = kwargs.get(ROUTE_LIVE_TAG) == ROUTE_LIVE_SUFFIX streams = [asset['recommendedStream']] streams.extend(asset['alternativeStreams']) streams = [s for s in streams if s['mediaFormat'] in SUPPORTED_FORMATS] if not streams: raise PluginError(_.NO_STREAM) providers = SUPPORTED_PROVIDERS[:] providers.extend([s['provider'] for s in streams]) streams = sorted(streams, key=lambda k: (providers.index(k['provider']), SUPPORTED_FORMATS.index(k['mediaFormat']))) stream = streams[0] log.debug('Stream CDN: {provider} | Stream Format: {mediaFormat}'.format( **stream)) item = plugin.Item( path=stream['manifest']['uri'], art=False, headers=HEADERS, use_proxy=True, #required to support dolby 5.1 and license requests ) if is_live and (play_type == PLAY_FROM_LIVE or (play_type == PLAY_FROM_ASK and gui.yes_no(_.PLAY_FROM, yeslabel=_.PLAY_FROM_LIVE, nolabel=_.PLAY_FROM_START))): play_type = PLAY_FROM_LIVE start_from = 0 if stream['mediaFormat'] == FORMAT_DASH: item.inputstream = inputstream.MPD() elif stream['mediaFormat'] == FORMAT_HLS_TS: force = (is_live and play_type == PLAY_FROM_LIVE) item.inputstream = inputstream.HLS(force=force, live=is_live) if force and not item.inputstream.check(): raise PluginError(_.HLS_REQUIRED) elif stream['mediaFormat'] == FORMAT_HLS_FMP4: item.inputstream = inputstream.HLS(force=True, live=is_live) if not item.inputstream.check(): raise PluginError(_.HLS_REQUIRED) elif stream['mediaFormat'] in (FORMAT_DRM_DASH, FORMAT_DRM_DASH_HEVC): item.inputstream = inputstream.Widevine( license_key=plugin.url_for(license_request)) if start_from: item.properties['ResumeTime'] = start_from item.properties['TotalTime'] = start_from return item
def play(content_id=None, family_id=None, skip_intro=None, **kwargs): if KODI_VERSION > 18: ver_required = '2.6.0' else: ver_required = '2.4.5' ia = inputstream.Widevine( license_key=api.get_config()['services']['drm']['client']['endpoints'] ['widevineLicense']['href'], manifest_type='hls', mimetype='application/vnd.apple.mpegurl', ) if not ia.check() or not inputstream.require_version(ver_required): gui.ok( _(_.IA_VER_ERROR, kodi_ver=KODI_VERSION, ver_required=ver_required)) if family_id: data = api.video_bundle(family_id) if not data.get('video'): raise PluginError(_.NO_VIDEO_FOUND) video = data['video'] else: data = api.videos(content_id) if not data.get('videos'): raise PluginError(_.NO_VIDEO_FOUND) video = data['videos'][0] playback_url = video['mediaMetadata']['playbackUrls'][0]['href'] playback_data = api.playback_data(playback_url) media_stream = playback_data['stream']['complete'] original_language = video.get('originalLanguage') or 'en' headers = api.session.headers ia.properties['original_audio_language'] = original_language ## Allow fullres worldwide ## media_stream = media_stream.replace('/mickey/ps01/', '/ps01/') ############## item = _parse_video(video) item.update( path=media_stream, inputstream=ia, headers=headers, proxy_data={ 'default_language': original_language, 'original_language': original_language }, ) if kwargs[ROUTE_RESUME_TAG] and settings.getBool('disney_sync', False): continue_watching = api.continue_watching() item.resume_from = continue_watching.get(video['contentId'], 0) item.force_resume = True elif (int(skip_intro) if skip_intro is not None else settings.getBool( 'skip_intros', False)): item.resume_from = _get_milestone( video.get('milestones'), 'intro_end', default=0) / 1000 item.play_next = {} if settings.getBool('skip_credits', False): next_start = _get_milestone( video.get('milestones'), 'up_next', default=0) / 1000 item.play_next['time'] = next_start if video['programType'] == 'episode' and settings.getBool( 'play_next_episode', True): data = api.up_next(video['contentId']) for row in data.get('items', []): if row['type'] == 'DmcVideo' and row[ 'programType'] == 'episode' and row[ 'encodedSeriesId'] == video['encodedSeriesId']: item.play_next['next_file'] = _get_play_path(row['contentId']) break elif video['programType'] != 'episode' and settings.getBool( 'play_next_movie', False): data = api.up_next(video['contentId']) for row in data.get('items', []): if row['type'] == 'DmcVideo' and row['programType'] != 'episode': item.play_next['next_file'] = _get_play_path(row['contentId']) break if settings.getBool('wv_secure', False): item.inputstream.properties['license_flags'] = 'force_secure_decoder' if settings.getBool('disney_sync', False): telemetry = playback_data['tracking']['telemetry'] item.callback = { 'type': 'interval', 'interval': 20, 'callback': plugin.url_for(callback, media_id=telemetry['mediaId'], fguid=telemetry['fguid']), } return item