Exemple #1
0
def show_root_menu():
    """ Show the plugin root menu """
    li_style = xbmcgui.ListItem('[B]' + G.LANGUAGE(32001) + '[/B]',
                                offscreen=True)
    li_style.setArt({
        'thumb': os.path.join(G.THUMB_PATH, 'camera.jpg'),
        'fanart': G.FANART_PATH
    })
    add_directory_item({"mode": "camera"}, li_style)

    li_style = xbmcgui.ListItem('[B]' + G.LANGUAGE(32002) + '[/B]',
                                offscreen=True)
    li_style.setArt({
        'thumb': os.path.join(G.THUMB_PATH, 'senato.png'),
        'fanart': G.FANART_PATH
    })
    add_directory_item({"mode": "senato"}, li_style)

    li_style = xbmcgui.ListItem('[B]' + G.LANGUAGE(32003) + '[/B]',
                                offscreen=True)
    li_style.setArt({
        'thumb': os.path.join(G.THUMB_PATH, 'tv.png'),
        'fanart': G.FANART_PATH
    })
    add_directory_item({"mode": "tv"}, li_style)

    li_style = xbmcgui.ListItem('[B]' + G.LANGUAGE(32004) + '[/B]',
                                offscreen=True)
    li_style.setArt({
        'thumb': os.path.join(G.THUMB_PATH, 'radio.png'),
        'fanart': G.FANART_PATH
    })
    add_directory_item({"mode": "radio"}, li_style)

    xbmcplugin.endOfDirectory(handle=G.PLUGIN_HANDLE, succeeded=True)
def migrate_library():
    # Migrate the Kodi library to the new format of STRM path
    # - Old STRM: '/play/show/xxxxxxxx/season/xxxxxxxx/episode/xxxxxxxx/' (used before ver 1.7.0)
    # - New STRM: '/play_strm/show/xxxxxxxx/season/xxxxxxxx/episode/xxxxxxxx/' (used from ver 1.7.0)
    folders = get_library_subfolders(
        FOLDER_NAME_MOVIES) + get_library_subfolders(FOLDER_NAME_SHOWS)
    if not folders:
        return
    LOG.debug('Start migrating STRM files')
    try:
        with ui.ProgressDialog(True,
                               title='Migrating library to new format',
                               max_value=len(folders)) as progress_bar:
            for folder_path in folders:
                folder_name = os.path.basename(
                    G.py2_decode(xbmc.translatePath(folder_path)))
                progress_bar.set_message('PLEASE WAIT - Migrating: ' +
                                         folder_name)
                _migrate_strm_files(folder_path)
    except Exception as exc:  # pylint: disable=broad-except
        LOG.error('Migrating failed: {}', exc)
        import traceback
        LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
        ui.show_ok_dialog('Migrating library to new format', (
            'Library migration has failed.[CR]'
            'Before try play a Netflix video from library, you must run manually the library migration, '
            'otherwise you will have add-on malfunctions.[CR][CR]'
            'Open add-on settings on "Library" section, and select "Import existing library".'
        ))
Exemple #3
0
def load(account_hash):
    """Load cookies for a given account and check them for validity"""
    filename = cookie_filename(account_hash)
    if not xbmcvfs.exists(filename):
        common.debug('Cookies file does not exist')
        raise MissingCookiesError()
    common.debug('Loading cookies from {}', G.py2_decode(filename))
    cookie_file = xbmcvfs.File(filename, 'rb')
    try:
        if G.PY_IS_VER2:
            # pickle.loads on py2 wants string
            cookie_jar = pickle.loads(cookie_file.read())
        else:
            cookie_jar = pickle.loads(cookie_file.readBytes())
    except Exception as exc:  # pylint: disable=broad-except
        import traceback
        common.error('Failed to load cookies from file: {exc}', exc=exc)
        common.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
        raise MissingCookiesError()
    finally:
        cookie_file.close()
    # Clear flwssn cookie if present, as it is trouble with early expiration
    if 'flwssn' in cookie_jar:
        cookie_jar.clear(domain='.netflix.com', path='/', name='flwssn')
    log_cookie(cookie_jar)
    return cookie_jar
Exemple #4
0
def parse_profiles(data):
    """Parse profile information from Netflix response"""
    profiles_list = jgraph_get_list('profilesList', data)
    try:
        if not profiles_list:
            raise InvalidProfilesError('It has not been possible to obtain the list of profiles.')
        sort_order = 0
        current_guids = []
        for index, profile_data in iteritems(profiles_list):  # pylint: disable=unused-variable
            summary = jgraph_get('summary', profile_data)
            guid = summary['guid']
            current_guids.append(guid)
            LOG.debug('Parsing profile {}', summary['guid'])
            avatar_url = _get_avatar(profile_data, data, guid)
            is_active = summary.pop('isActive')
            G.LOCAL_DB.set_profile(guid, is_active, sort_order)
            G.SHARED_DB.set_profile(guid, sort_order)
            # Add profile language description translated from locale
            summary['language_desc'] = G.py2_decode(xbmc.convertLanguage(summary['language'][:2], xbmc.ENGLISH_NAME))
            if LOG.level == LOG.LEVEL_VERBOSE:
                for key, value in iteritems(summary):
                    if key in PROFILE_DEBUG_INFO:
                        LOG.debug('Profile info {}', {key: value})
            # Translate the profile name, is coded as HTML
            summary['profileName'] = parse_html(summary['profileName'])
            summary['avatar'] = avatar_url
            G.LOCAL_DB.insert_profile_configs(summary, guid)
            sort_order += 1
        _delete_non_existing_profiles(current_guids)
    except Exception as exc:  # pylint: disable=broad-except
        import traceback
        LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
        LOG.error('Profile list data: {}', profiles_list)
        raise_from(InvalidProfilesError, exc)
Exemple #5
0
def make_http_call_cache(callname, params, data):
    """Make an IPC call via HTTP and wait for it to return.
    The contents of data will be expanded to kwargs and passed into the target function."""
    try:  # Python 3
        from urllib.request import build_opener, install_opener, ProxyHandler, HTTPError, URLError, Request, urlopen
    except ImportError:  # Python 2
        from urllib2 import build_opener, install_opener, ProxyHandler, HTTPError, URLError, Request, urlopen
    import json
    # debug('Handling HTTP IPC call to {}'.format(callname))
    # Note: On python 3, using 'localhost' slowdown the call (Windows OS is affected) not sure if it is an urllib issue
    url = 'http://127.0.0.1:{}/{}'.format(G.LOCAL_DB.get_value('cache_service_port', 8002), callname)
    install_opener(build_opener(ProxyHandler({})))  # don't use proxy for localhost
    r = Request(url=url, data=data, headers={'Params': json.dumps(params)})
    try:
        result = urlopen(r, timeout=IPC_TIMEOUT_SECS).read()
    except HTTPError as exc:
        if exc.reason in exceptions.__dict__:
            raise_from(exceptions.__dict__[exc.reason], exc)
        raise_from(Exception('The service has returned: {}'.format(exc.reason)), exc)
    except URLError as exc:
        # On PY2 the exception message have to be decoded with latin-1 for system with symbolic characters
        err_msg = G.py2_decode(str(exc), 'latin-1')
        if '10049' in err_msg:
            err_msg += '\r\nPossible cause is wrong localhost settings in your operative system.'
        LOG.error(err_msg)
        raise_from(exceptions.BackendNotReady(G.py2_encode(err_msg, encoding='latin-1')), exc)
    return result
Exemple #6
0
def make_http_call(callname, data):
    """Make an IPC call via HTTP and wait for it to return.
    The contents of data will be expanded to kwargs and passed into the target function."""
    from collections import OrderedDict
    try:  # Python 3
        from urllib.request import build_opener, install_opener, ProxyHandler, HTTPError, URLError, urlopen
    except ImportError:  # Python 2
        from urllib2 import build_opener, install_opener, ProxyHandler, HTTPError, URLError, urlopen
    import json
    LOG.debug('Handling HTTP IPC call to {}'.format(callname))
    # Note: On python 3, using 'localhost' slowdown the call (Windows OS is affected) not sure if it is an urllib issue
    url = 'http://127.0.0.1:{}/{}'.format(G.LOCAL_DB.get_value('ns_service_port', 8001), callname)
    install_opener(build_opener(ProxyHandler({})))  # don't use proxy for localhost
    try:
        result = json.loads(
            urlopen(url=url, data=json.dumps(data).encode('utf-8'), timeout=IPC_TIMEOUT_SECS).read(),
            object_pairs_hook=OrderedDict)
    except HTTPError as exc:
        result = json.loads(exc.reason)
    except URLError as exc:
        # On PY2 the exception message have to be decoded with latin-1 for system with symbolic characters
        err_msg = G.py2_decode(str(exc), 'latin-1')
        if '10049' in err_msg:
            err_msg += '\r\nPossible cause is wrong localhost settings in your operative system.'
        LOG.error(err_msg)
        raise_from(exceptions.BackendNotReady(G.py2_encode(err_msg, encoding='latin-1')),
                   exc)
    _raise_for_error(result)
    return result
 def autoselect_profile_remove(self, pathitems):  # pylint: disable=unused-argument
     """Remove the auto-selection set"""
     G.LOCAL_DB.set_value('autoselect_profile_guid', '')
     G.settings_monitor_suspend(True)
     G.ADDON.setSetting('autoselect_profile_name', '')
     G.ADDON.setSettingBool('autoselect_profile_enabled', False)
     G.settings_monitor_suspend(False)
Exemple #8
0
def _delete_non_existing_profiles(current_guids):
    list_guid = G.LOCAL_DB.get_guid_profiles()
    for guid in list_guid:
        if guid not in current_guids:
            LOG.debug('Deleting non-existing profile {}', guid)
            G.LOCAL_DB.delete_profile(guid)
            G.SHARED_DB.delete_profile(guid)
    # Ensures at least one active profile
    try:
        G.LOCAL_DB.get_active_profile_guid()
    except DBProfilesMissing:
        G.LOCAL_DB.switch_active_profile(G.LOCAL_DB.get_guid_owner_profile())
    G.settings_monitor_suspend(True)
    # Verify if auto select profile exists
    autoselect_profile_guid = G.LOCAL_DB.get_value('autoselect_profile_guid', '')
    if autoselect_profile_guid and autoselect_profile_guid not in current_guids:
        LOG.warn('Auto-selection disabled, the GUID {} not more exists', autoselect_profile_guid)
        G.LOCAL_DB.set_value('autoselect_profile_guid', '')
    # Verify if profile for library auto-sync exists
    sync_mylist_profile_guid = G.SHARED_DB.get_value('sync_mylist_profile_guid')
    if sync_mylist_profile_guid and sync_mylist_profile_guid not in current_guids:
        LOG.warn('Library auto-sync disabled, the GUID {} not more exists', sync_mylist_profile_guid)
        G.ADDON.setSettingBool('lib_sync_mylist', False)
        G.SHARED_DB.delete_key('sync_mylist_profile_guid')
    # Verify if profile for library playback exists
    library_playback_profile_guid = G.LOCAL_DB.get_value('library_playback_profile_guid')
    if library_playback_profile_guid and library_playback_profile_guid not in current_guids:
        LOG.warn('Profile set for playback from library cleared, the GUID {} not more exists',
                 library_playback_profile_guid)
        G.LOCAL_DB.set_value('library_playback_profile_guid', '')
    G.settings_monitor_suspend(False)
Exemple #9
0
def run_addon_configuration(show_end_msg=False):
    """
    Add-on configuration wizard,
    automatically configures profiles and add-ons dependencies, based on user-supplied data and device characteristics
    """
    system = get_system_platform()
    debug('Running add-on configuration wizard ({})', system)
    G.settings_monitor_suspend(True, False)
    is_4k_capable = is_device_4k_capable()

    _set_profiles(system, is_4k_capable)
    _set_kodi_settings(system)
    _set_isa_addon_settings(is_4k_capable, system == 'android')

    # This settings for now used only with android devices and it should remain disabled (keep it for test),
    # in the future it may be useful for other platforms or it may be removed
    G.ADDON.setSettingBool('enable_force_hdcp', False)

    # Enable UpNext if it is installed and enabled
    G.ADDON.setSettingBool(
        'UpNextNotifier_enabled',
        getCondVisibility('System.AddonIsEnabled(service.upnext)'))

    G.settings_monitor_suspend(False)
    if show_end_msg:
        show_ok_dialog(get_local_string(30154), get_local_string(30157))
 def autoselect_profile_set(self, pathitems):  # pylint: disable=unused-argument
     """Save the GUID for profile auto-selection"""
     G.LOCAL_DB.set_value('autoselect_profile_guid', self.params['profile_guid'])
     G.settings_monitor_suspend(True)
     G.ADDON.setSetting('autoselect_profile_name', self.params['profile_name'])
     G.ADDON.setSettingBool('autoselect_profile_enabled', True)
     G.settings_monitor_suspend(False)
     ui.show_notification(common.get_local_string(30058).format(G.py2_decode(self.params['profile_name'])))
Exemple #11
0
def _subtitle_profiles():
    from xbmcaddon import Addon
    isa_version = G.remove_ver_suffix(
        G.py2_decode(Addon('inputstream.adaptive').getAddonInfo('version')))
    subtitle_profile = ['webvtt-lssdh-ios8']
    if G.ADDON.getSettingBool('disable_webvtt_subtitle') \
       or not common.is_minimum_version(isa_version, '2.3.8'):
        subtitle_profile = ['simplesdh']
    return subtitle_profile
Exemple #12
0
def rename_cookie_file():
    # The file "COOKIE_xxxxxx..." will be renamed to "COOKIES"
    list_files = list_dir(G.DATA_PATH)[1]
    for filename in list_files:
        if 'COOKIE_' in G.py2_decode(filename):
            copy_file(join_folders_paths(G.DATA_PATH, G.py2_decode(filename)),
                      join_folders_paths(G.DATA_PATH, 'COOKIES'))
            xbmc.sleep(80)
            delete_file(G.py2_decode(filename))
def run(argv):
    # Initialize globals right away to avoid stale values from the last addon invocation.
    # Otherwise Kodi's reuseLanguageInvoker will cause some really quirky behavior!
    # PR: https://github.com/xbmc/xbmc/pull/13814
    G.init_globals(argv)
    check_service_upgrade()
    netflix_service = NetflixService()
    if netflix_service.init_servers():
        netflix_service.run()
 def onSettingsChanged(self):
     status = G.settings_monitor_suspend_status()
     if status == 'First':
         LOG.warn('SettingsMonitor: triggered but in suspend status (at first change)')
         G.settings_monitor_suspend(False)
         return
     if status == 'True':
         LOG.warn('SettingsMonitor: triggered but in suspend status (permanent)')
         return
     self._on_change()
Exemple #15
0
def run(argv):
    # Initialize globals right away to avoid stale values from the last addon invocation.
    # Otherwise Kodi's reuseLanguageInvoker will cause some really quirky behavior!
    # PR: https://github.com/xbmc/xbmc/pull/13814
    G.init_globals(argv)

    LOG.info('Started (Version {})'.format(G.VERSION_RAW))
    LOG.info('URL is {}'.format(G.URL))
    success = True

    window_cls = Window(10000)  # Kodi home window

    # If you use multiple Kodi profiles you need to distinguish the property of current profile
    prop_nf_service_status = G.py2_encode('nf_service_status_' +
                                          get_current_kodi_profile_name())
    is_external_call = _check_addon_external_call(window_cls,
                                                  prop_nf_service_status)
    service_status = _get_service_status(window_cls, prop_nf_service_status)

    if service_status.get('status') != 'running':
        if not is_external_call:
            if service_status.get('status') == 'error':
                # The services are not started due to an error exception
                from resources.lib.kodi.ui import show_error_info
                show_error_info(
                    get_local_string(30105),
                    get_local_string(30240).format(
                        service_status.get('message')), False, False)
            else:
                # The services are not started yet
                from resources.lib.kodi.ui import show_backend_not_ready
                show_backend_not_ready()
        success = False
    if success:
        cancel_playback = False
        pathitems = [part for part in G.REQUEST_PATH.split('/') if part]
        if G.IS_ADDON_FIRSTRUN:
            is_first_run_install, cancel_playback = check_addon_upgrade()
            if is_first_run_install:
                from resources.lib.config_wizard import run_addon_configuration
                run_addon_configuration()
        if cancel_playback and G.MODE_PLAY in pathitems[:1]:
            # Temporary for migration library STRM to new format. todo: to be removed in future releases
            # When a user do the add-on upgrade, the first time that the add-on will be opened will be executed
            # the library migration. But if a user instead to open the add-on, try to play a video from Kodi
            # library, Kodi will open the old STRM file because the migration is executed after.
            success = False
        else:
            success = route(pathitems)
    if not success:
        from xbmcplugin import endOfDirectory
        endOfDirectory(handle=G.PLUGIN_HANDLE, succeeded=False)
    LOG.log_time_trace()
Exemple #16
0
def convert_to_dash(manifest):
    """Convert a Netflix style manifest to MPEG-DASH manifest"""
    from xbmcaddon import Addon
    isa_version = G.remove_ver_suffix(
        G.py2_decode(Addon('inputstream.adaptive').getAddonInfo('version')))

    # If a CDN server has stability problems it may cause errors with streaming,
    # we allow users to select a different CDN server
    # (should be managed by ISA but is currently is not implemented)
    cdn_index = int(G.ADDON.getSettingString('cdn_server')[-1]) - 1

    seconds = manifest['duration'] / 1000
    init_length = int(seconds / 2 * 12 + 20 * 1000)
    duration = "PT" + str(int(seconds)) + ".00S"

    root = _mpd_manifest_root(duration)
    period = ET.SubElement(root, 'Period', start='PT0S', duration=duration)

    has_video_drm_streams = manifest['video_tracks'][0].get(
        'hasDrmStreams', False)
    video_protection_info = _get_protection_info(
        manifest['video_tracks'][0]) if has_video_drm_streams else None

    for video_track in manifest['video_tracks']:
        _convert_video_track(video_track, period, init_length,
                             video_protection_info, has_video_drm_streams,
                             cdn_index)

    common.fix_locale_languages(manifest['audio_tracks'])
    common.fix_locale_languages(manifest['timedtexttracks'])

    has_audio_drm_streams = manifest['audio_tracks'][0].get(
        'hasDrmStreams', False)

    default_audio_language_index = _get_default_audio_language(manifest)
    for index, audio_track in enumerate(manifest['audio_tracks']):
        _convert_audio_track(audio_track, period, init_length,
                             (index == default_audio_language_index),
                             has_audio_drm_streams, cdn_index)

    default_subtitle_language_index = _get_default_subtitle_language(manifest)
    for index, text_track in enumerate(manifest['timedtexttracks']):
        if text_track['isNoneTrack']:
            continue
        _convert_text_track(text_track, period,
                            (index == default_subtitle_language_index),
                            cdn_index, isa_version)

    xml = ET.tostring(root, encoding='utf-8', method='xml')
    if LOG.level == LOG.LEVEL_VERBOSE:
        common.save_file_def('manifest.mpd', xml)
    return xml.decode('utf-8').replace('\n', '').replace('\r',
                                                         '').encode('utf-8')
def delete_cache_folder():
    # Delete cache folder in the add-on userdata (no more needed with the new cache management)
    cache_path = os.path.join(G.DATA_PATH, 'cache')
    if not os.path.exists(G.py2_decode(xbmc.translatePath(cache_path))):
        return
    LOG.debug('Deleting the cache folder from add-on userdata folder')
    try:
        delete_folder_contents(cache_path, True)
        xbmc.sleep(80)
        xbmcvfs.rmdir(cache_path)
    except Exception:  # pylint: disable=broad-except
        import traceback
        LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
Exemple #18
0
def log_time_trace():
    """Write the time tracing info to the debug log"""
    if not is_debug_verbose() and not G.TIME_TRACE_ENABLED:
        return

    time_trace = ['Execution time info for this run:\n']
    G.TIME_TRACE.reverse()
    for trace in G.TIME_TRACE:
        time_trace.append(' ' * trace[2])
        time_trace.append(format(trace[0], '<30'))
        time_trace.append('{:>5} ms\n'.format(trace[1]))
    debug(''.join(time_trace))
    G.reset_time_trace()
def show_root_menu():
    ''' Show the plugin root menu '''
    liStyle = xbmcgui.ListItem('[B]' + G.LANGUAGE(32002) + '[/B]',
                               offscreen=True)
    liStyle.setArt({
        'thumb': os.path.join(G.THUMB_PATH, 'direttalivela7.jpg'),
        'fanart': G.FANART_PATH
    })
    addDirectoryItem_nodup({"mode": "diretta_la7"}, liStyle, folder=False)
    liStyle = xbmcgui.ListItem('[B]' + G.LANGUAGE(32009) + '[/B]',
                               offscreen=True)
    liStyle.setArt({
        'thumb': os.path.join(G.THUMB_PATH, 'direttalivela7d.jpg'),
        'fanart': G.FANART_PATH
    })
    addDirectoryItem_nodup({"mode": "diretta_la7d"}, liStyle, folder=False)
    liStyle = xbmcgui.ListItem('[B]' + G.LANGUAGE(32001) + '[/B]',
                               offscreen=True)
    liStyle.setArt({
        'thumb': os.path.join(G.THUMB_PATH, 'rivedila7.jpg'),
        'fanart': G.FANART_PATH
    })
    addDirectoryItem_nodup({"mode": "rivedi_la7"}, liStyle)
    liStyle = xbmcgui.ListItem('[B]' + G.LANGUAGE(32004) + '[/B]',
                               offscreen=True)
    liStyle.setArt({
        'thumb': os.path.join(G.THUMB_PATH, 'rivedila7d.jpg'),
        'fanart': G.FANART_PATH
    })
    addDirectoryItem_nodup({"mode": "rivedi_la7d"}, liStyle)
    liStyle = xbmcgui.ListItem('[B]' + G.LANGUAGE(32010) + '[/B]',
                               offscreen=True)
    liStyle.setArt({
        'thumb': os.path.join(G.THUMB_PATH, 'la7prime.jpg'),
        'fanart': G.FANART_PATH
    })
    addDirectoryItem_nodup({"mode": "la7_prime"}, liStyle)
    liStyle = xbmcgui.ListItem('[B]' + G.LANGUAGE(32006) + '[/B]',
                               offscreen=True)
    liStyle.setArt({
        'thumb': os.path.join(G.THUMB_PATH, 'programmila7la7d.jpg'),
        'fanart': G.FANART_PATH
    })
    addDirectoryItem_nodup({"mode": "tutti_programmi"}, liStyle)
    liStyle = xbmcgui.ListItem('[B]' + G.LANGUAGE(32007) + '[/B]',
                               offscreen=True)
    liStyle.setArt({
        'thumb': os.path.join(G.THUMB_PATH, 'tgmeteo.jpg'),
        'fanart': G.FANART_PATH
    })
    addDirectoryItem_nodup({"mode": "tg_meteo"}, liStyle)
    liStyle = xbmcgui.ListItem('[B]' + G.LANGUAGE(32008) + '[/B]',
                               offscreen=True)
    liStyle.setArt({
        'thumb': os.path.join(G.THUMB_PATH, 'techela7.jpg'),
        'fanart': G.FANART_PATH
    })
    addDirectoryItem_nodup({"mode": "teche_la7"}, liStyle)

    xbmcplugin.endOfDirectory(handle=G.PLUGIN_HANDLE, succeeded=True)
Exemple #20
0
def delete_folder_contents(path, delete_subfolders=False):
    """
    Delete all files in a folder
    :param path: Path to perform delete contents
    :param delete_subfolders: If True delete also all subfolders
    """
    directories, files = list_dir(xbmc.translatePath(path))
    for filename in files:
        xbmcvfs.delete(os.path.join(path, G.py2_decode(filename)))
    if not delete_subfolders:
        return
    for directory in directories:
        delete_folder_contents(os.path.join(path, G.py2_decode(directory)), True)
        # Give time because the system performs previous op. otherwise it can't delete the folder
        xbmc.sleep(80)
        xbmcvfs.rmdir(os.path.join(path, G.py2_decode(directory)))
 def _load_msl_data(self, msl_data):
     try:
         self.crypto.load_msl_data(msl_data)
         self.crypto.load_crypto_session(msl_data)
     except Exception:  # pylint: disable=broad-except
         import traceback
         LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
def _create_videolist_item(list_id,
                           video_list,
                           menu_data,
                           common_data,
                           static_lists=False):
    if static_lists and G.is_known_menu_context(video_list['context']):
        pathitems = list(menu_data['path'])  # Make a copy
        pathitems.append(video_list['context'])
    else:
        # It is a dynamic video list / menu context
        if menu_data.get('force_use_videolist_id', False):
            path = 'video_list'
        else:
            path = 'video_list_sorted'
        pathitems = [path, menu_data['path'][1], list_id]
    list_item = ListItemW(label=video_list['displayName'])
    add_info_list_item(list_item,
                       video_list.videoid,
                       video_list,
                       video_list.data,
                       False,
                       common_data,
                       art_item=video_list.artitem)
    # Add possibility to browse the sub-genres (see build_video_listing)
    sub_genre_id = video_list.get('genreId')
    params = {'sub_genre_id': str(sub_genre_id)} if sub_genre_id else None
    return common.build_url(pathitems, params=params,
                            mode=G.MODE_DIRECTORY), list_item, True
def get_library_subfolders(folder_name):
    """Returns all the subfolders contained in a folder of library path"""
    section_path = common.join_folders_paths(get_library_path(), folder_name)
    return [
        common.join_folders_paths(section_path, G.py2_decode(folder))
        for folder in common.list_dir(section_path)[0]
    ]
Exemple #24
0
def show_browse_dialog(title,
                       browse_type=0,
                       default_path=None,
                       multi_selection=False,
                       extensions=None):
    """
    Show a browse dialog to select files or folders
    :param title: The window title
    :param browse_type: Type of dialog as int value (0 = ShowAndGetDirectory, 1 = ShowAndGetFile, ..see doc)
    :param default_path: The initial path
    :param multi_selection: Allow multi selection
    :param extensions: extensions allowed e.g. '.jpg|.png'
    :return: The selected path as string (or tuple of selected items) if user pressed 'Ok', else None
    """
    ret = G.py2_decode(xbmcgui.Dialog().browse(browse_type,
                                               title,
                                               shares='local',
                                               useThumbs=False,
                                               treatAsFolder=False,
                                               defaultt=default_path,
                                               enableMultiple=multi_selection,
                                               mask=extensions))
    # Note: when defaultt is set and the user cancel the action (when enableMultiple is False),
    #       will be returned the defaultt value again, so we avoid this strange behavior...
    return None if not ret or ret == default_path else ret
Exemple #25
0
    def login(self, credentials=None):
        """Perform account login"""
        try:
            # First we get the authentication url without logging in, required for login API call
            react_context = website.extract_json(self.get('login'), 'reactContext')
            auth_url = website.extract_api_data(react_context)['auth_url']
            LOG.debug('Logging in...')
            login_response = self.post(
                'login',
                headers={'Accept-Language': _get_accept_language_string(react_context)},
                data=_login_payload(credentials or common.get_credentials(), auth_url, react_context))

            website.extract_session_data(login_response, validate=True, update_profiles=True)
            if credentials:
                # Save credentials only when login has succeeded
                common.set_credentials(credentials)
            LOG.info('Login successful')
            ui.show_notification(common.get_local_string(30109))
            cookies.save(self.account_hash, self.session.cookies)
            return True
        except LoginValidateError as exc:
            self.session.cookies.clear()
            common.purge_credentials()
            raise_from(LoginError(unicode(exc)), exc)
        except (MbrStatusNeverMemberError, MbrStatusFormerMemberError) as exc:
            self.session.cookies.clear()
            LOG.warn('Membership status {} not valid for login', exc)
            raise_from(LoginError(common.get_local_string(30180)), exc)
        except Exception:  # pylint: disable=broad-except
            self.session.cookies.clear()
            import traceback
            LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
            raise
Exemple #26
0
    def video_list_sorted(self, pathitems):
        """Show a video list sorted of a 'context' name"""
        menu_data = G.MAIN_MENU_ITEMS.get(pathitems[1])
        if not menu_data:  # Dynamic menus
            menu_data = G.LOCAL_DB.get_value(pathitems[1],
                                             table=TABLE_MENU_DATA,
                                             data_type=dict)
        call_args = {
            'pathitems': pathitems,
            'menu_data': menu_data,
            'sub_genre_id': self.params.get(
                'sub_genre_id'
            ),  # Used to show the sub-genre folder when sub-genres exists
            'perpetual_range_start': self.perpetual_range_start,
            'is_dynamic_id': not G.is_known_menu_context(pathitems[2])
        }
        list_data, extra_data = common.make_call('get_video_list_sorted',
                                                 call_args)
        sort_type = 'sort_nothing'
        if menu_data['path'][1] == 'myList' and int(
                G.ADDON.getSettingInt('menu_sortorder_mylist')) == 0:
            # At the moment it is not possible to make a query with results sorted for the 'mylist',
            # so we adding the sort order of kodi
            sort_type = 'sort_label_ignore_folders'

        finalize_directory(convert_list_to_dir_items(list_data),
                           menu_data.get('content_type', G.CONTENT_SHOW),
                           title=get_title(menu_data, extra_data),
                           sort_type=sort_type)

        end_of_directory(self.dir_update_listing)
        return menu_data.get('view')
Exemple #27
0
def _log(msg, level, *args, **kwargs):
    """Log a message to the Kodi logfile."""
    if args or kwargs:
        msg = msg.format(*args, **kwargs)
    xbmc.log(
        G.py2_encode('[{identifier} ({handle})] {msg}'.format(
            identifier=G.ADDON_ID, handle=G.PLUGIN_HANDLE, msg=msg)), level)
Exemple #28
0
def validate_login(react_context):
    path_code_list = PAGE_ITEM_ERROR_CODE_LIST.split('\\')
    path_error_code = PAGE_ITEM_ERROR_CODE.split('/')
    if common.check_path_exists(path_error_code, react_context):
        # If the path exists, a login error occurs
        try:
            error_code_list = common.get_path(path_code_list, react_context)
            error_code = common.get_path(path_error_code, react_context)
            LOG.error('Login not valid, error code {}', error_code)
            error_description = common.get_local_string(30102) + error_code
            if error_code in error_code_list:
                error_description = error_code_list[error_code]
            if 'email_' + error_code in error_code_list:
                error_description = error_code_list['email_' + error_code]
            if 'login_' + error_code in error_code_list:
                error_description = error_code_list['login_' + error_code]
            raise LoginValidateError(common.remove_html_tags(error_description))
        except (AttributeError, KeyError) as exc:
            import traceback
            LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
            error_msg = (
                'Something is wrong in PAGE_ITEM_ERROR_CODE or PAGE_ITEM_ERROR_CODE_LIST paths.'
                'react_context data may have changed.')
            LOG.error(error_msg)
            raise_from(WebsiteParsingError(error_msg), exc)
Exemple #29
0
    def release_license(self, data=None):  # pylint: disable=unused-argument
        """Release the server license"""
        try:
            # When you try to play a video while another one is currently in playing,
            # a new license to be released will be queued, so the oldest license must be released
            url = self.licenses_release_url.pop()
            sid = self.licenses_session_id.pop()
            xid = self.licenses_xid.pop()

            common.debug('Requesting releasing license')
            params = [{
                'url': url,
                'params': {
                    'drmSessionId': sid,
                    'xid': xid
                },
                'echo': 'drmSessionId'
            }]

            response = self.msl_requests.chunked_request(
                ENDPOINTS['license'],
                self.msl_requests.build_request_data('/bundle', params),
                G.get_esn())
            common.debug('License release response: {}', response)
        except IndexError:
            # Example the supplemental media type have no license
            common.debug('No license to release')
def extract_json(content, name):
    """Extract json from netflix content page"""
    LOG.debug('Extracting {} JSON', name)
    json_str = None
    try:
        json_array = recompile(JSON_REGEX.format(name),
                               DOTALL).findall(content.decode('utf-8'))
        json_str = json_array[0]
        json_str_replace = json_str.replace('\\"',
                                            '\\\\"')  # Escape double-quotes
        json_str_replace = json_str_replace.replace('\\s',
                                                    '\\\\s')  # Escape \s
        json_str_replace = json_str_replace.replace(
            '\\n', '\\\\n')  # Escape line feed
        json_str_replace = json_str_replace.replace('\\t',
                                                    '\\\\t')  # Escape tab
        json_str_replace = json_str_replace.encode().decode(
            'unicode_escape')  # Decode the string as unicode
        json_str_replace = sub(
            r'\\(?!["])', r'\\\\', json_str_replace
        )  # Escape backslash (only when is not followed by double quotation marks \")
        return json.loads(json_str_replace)
    except Exception as exc:  # pylint: disable=broad-except
        if json_str:
            LOG.error('JSON string trying to load: {}', json_str)
        import traceback
        LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
        raise_from(WebsiteParsingError('Unable to extract {}'.format(name)),
                   exc)