Пример #1
0
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),
                    ))
Пример #2
0
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>')
Пример #3
0
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
    )
Пример #4
0
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),
                ))
Пример #6
0
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)
Пример #7
0
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)
Пример #8
0
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
Пример #9
0
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)
Пример #10
0
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
Пример #11
0
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
Пример #12
0
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)
Пример #13
0
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
Пример #14
0
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()
Пример #15
0
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')
Пример #16
0
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
Пример #17
0
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