def clean_artwork(mediaitem):
def remove_specific_arttype(mediaitem, arttype):
def _get_clean_art(arttype, url): CANT_FIND_MOVIESET = 32032
CANT_FIND_MUSICVIDEO = 32033
class MediaItem(object):
    def __init__(self, jsondata):
def build_music_label(jsondata):
def is_known_mediatype(jsondata):
def get_mediatype_id(jsondata):
def get_own_artwork(jsondata):
def has_generated_thumbnail(jsondata):
def has_art_todownload(artmap, mediatype):
def arttype_matches_base(arttype, basetype):
def iter_urls_for_arttype(art, arttype):
def iter_base_arttypes(artkeys):
def fill_multiart(original_art, basetype, artchanges=((), ())):
def item_has_generated_thumbnail(mediaitem):
def iter_missing_arttypes(mediaitem, existingart):
def _has_localart(arttype, existingart, fromtypes):
def keep_arttype(mediatype, arttype, arturl):
def get_basetype(arttype):
def format_arttype(basetype, index):
def update_art_in_library(mediatype, dbid, updatedart):
def remove_local_from_texturecache(urls, include_generated=False):
def add_additional_iteminfo(mediaitem, processed, search=None):
def add_movieset_movies(mediaitem):
def _get_seasons_artwork(seasons):
def _remove_set_movieposters(mediaitem):
def _identify_parent_movieset(mediaitem):
def _identify_album_folders(mediaitem):
def _shared_albumfolder(folder):
def _get_uniqueids(jsondata, mediatype):
def _get_sourcemedia(mediapath):
def can_saveartwork(mediaitem):
def build_artwork_basepath(mediaitem, arttype):
    def snum(num):
def _saveextrafanart(mediatype, arttype):
def find_central_infodir(mediaitem):
def _find_existing(basedir, name, uniqueslug=None, mediayear=None, files=False):
def _get_uniqueslug(mediaitem, slug_mediatype):
def cacheit(func):
    def wrapper(*args):
def get_cached_listdir(path):
def get_cached_artists(artistname):
def get_cached_albums(artistname, dbid):
def get_cached_songs(dbid):
def get_cached_songs_bypath(path):
def get_cached_tvshow(dbid):
def get_cached_tvshows():
def clear_cache(): TVSHOW = 'tvshow'
MOVIE = 'movie'
EPISODE = 'episode'
SEASON = 'season'
MOVIESET = 'set'
MUSICVIDEO = 'musicvideo'
ARTIST = 'artist'
ALBUM = 'album'
SONG = 'song'
PREFERRED_SOURCE_SHARED = {'0': None, '1': 'fanart.tv'}
PREFERRED_SOURCE_MEDIA = {'tvshows': ('thetvdb.com', (TVSHOW, SEASON, EPISODE)),
    'movies': ('themoviedb.org', (MOVIE, MOVIESET)),
    'music': ('theaudiodb.com', audiotypes),
    'musicvideos': ('theaudiodb.com', (MUSICVIDEO,))}
def get_artinfo(mediatype, arttype):
def hack_mediaarttype(mediatype, arttype):
def disabled(mediatype):
def iter_every_arttype(mediatype):
def downloadartwork(mediatype, arttype):
def downloadanyartwork(mediatype):
def _split_arttype(arttype):
def generatethumb(mediatype):
def haspreferred_source(mediatype):
def ispreferred_source(mediatype, provider):
def only_filesystem(mediatype):
def update_settings():
def _get_autolimit_from_setting(settingid):
def _set_allmulti(always_multi): ADDONID = 'script.artwork.beef'
def get_main_addon():
def is_addon_watched():
def get_kodi_version():
def localize(messageid):
def get_conditional(conditional):
def get_infolabel(infolabel):
def execute_builtin(builtin_command):
def datetime_now():
def datetime_strptime(date_string, format_string):
def execute_jsonrpc(jsonrpc_command):
def log(message, level=xbmc.LOGDEBUG, tag=None):
def scrub_message(message):
def set_log_scrubstring(key, string):
def get_language(language_format=xbmc.ENGLISH_NAME, region=False):
def unquoteimage(imagestring):
def quoteimage(imagestring):
def unquotearchive(filepath):
def get_command(*first_arg_keys):
def get_busydialog():
    def __init__(self, *args, **kwargs):
    def get_setting(self, settingid):
    def set_setting(self, settingid, value):
    def __init__(self, *args, **kwargs):
    def default(self, obj):
    def __init__(self, *args, **kwargs):
    def iterencode(self, obj, _one_shot=False):
    def raw_decode(self, s, idx=0):
    def _json_unicode_to_str(self, jsoninput):
    def __init__(self):
    def create(self):
    def close(self):
    def __del__(self):
class Addon(xbmcaddon.Addon):
class ObjectJSONEncoder(json.JSONEncoder):
class UTF8PrettyJSONEncoder(ObjectJSONEncoder):
class UTF8JSONDecoder(json.JSONDecoder):
class DialogBusy(object): def _needupgrade(mediatype):
def _upgradeproperties():
def _upgradeitem(mediaitem, mediatype):
def get_item_details(dbid, mediatype):
def get_item_list(mediatype, extraparams=None, overrideprops=None):
def _inner_get_item_list(mediatype, extraparams=None, overrideprops=None):
def _inner_get_result(json_result, mediatype):
def gen_chunked_item_list(mediatype, extraparams=None, overrideprops=None, chunksize=1000):
def get_albums(artistname=None, dbid=None):
def get_artists_byname(artistname):
def get_songs(mediatype=None, dbid=None, songfilter=None):
def get_tvshows(moreprops=False, includeprops=True):
def get_episodes(tvshow_id=None, limit=None):
def get_seasons(tvshow_id=-1):
def _inner_get_seasons(tvshow_id=-1):
def _get_all_seasons_jarvis():
def set_item_details(dbid, mediatype, **details):
def get_textures(url=None):
def remove_texture(textureid):
def remove_texture_byurl(url):
def get_base_json_request(method):
def get_application_properties(properties):
def get_settingvalue(setting):
def check_json_result(json_result, result_key, json_request):
    def __init__(self, json_request, json_result):
class JSONException(Exception): def natural_sort(string, split_regex=re.compile(r'([0-9]+)')):
def get_pathsep(path):
def parent_dir(path):
def get_simpledict_updates(original, newdict):
def get_movie_path_list(stackedpath):
def path_component(string):
def iter_possible_cleannames(originalname, uniqueslug=None):
def build_cleanest_name(originalname, uniqueslug=None): DEFAULT_IMAGESIZE = '1'
AVAILABLE_IMAGESIZES = {'0': (10000, 10000, 600), '1': (1920, 1080, 600), '2': (1280, 720, 480)}
PROGRESS_DISPLAY_FULLPROGRESS = '0'
PROGRESS_DISPLAY_WARNINGSERRORS = '1'
PROGRESS_DISPLAY_NONE = '2' # Only add-on crashes
EXCLUSION_PATH_TYPE_FOLDER = '0'
EXCLUSION_PATH_TYPE_PREFIX = '1'
EXCLUSION_PATH_TYPE_REGEX = '2'
class Settings(object):
    def __init__(self):
    def update_useragent(self):
    def update_settings(self):
    def get_apikey(self, provider):
    def get_apienabled(self, provider):
    def get_api_config(self, provider):
    def autoadd_episodes(self):
    def autoadd_episodes(self, value):
def get_projectkey(provider): def localize(messageid):
    if isinstance(messageid, basestring):
        result = messageid
    elif messageid >= 32000 and messageid < 33000:
        result = get_main_addon().getLocalizedString(messageid)
    else:
        result = xbmc.getLocalizedString(messageid)
    return result.encode('utf-8') if isinstance(result, unicode) else result def log(message, level=xbmc.LOGDEBUG, tag=None):
    if is_addon_watched() and level < xbmc.LOGNOTICE:
        # Messages from this add-on are being watched, elevate to NOTICE so Kodi logs it
        level_tag = _log_level_tag_lookup[level] + ': ' if level in _log_level_tag_lookup else ''
        level = xbmc.LOGNOTICE
    else:
        level_tag = ''

    if isinstance(message, (dict, list)) and len(message) > 300:
        message = str(message)
    elif isinstance(message, unicode):
        message = message.encode('utf-8')
    elif not isinstance(message, str):
        message = json.dumps(message, cls=UTF8PrettyJSONEncoder)

    addontag = ADDONID if not tag else ADDONID + ':' + tag
    file_message = '%s[%s] %s' % (level_tag, addontag, scrub_message(message))
    xbmc.log(file_message, level) class Getter(object):
    def __init__(self, contenttype=None, login=lambda: False, session=None):
        self.session = session or retryable_session()
        self.login = login
        if contenttype:
            self.session.headers['Accept'] = contenttype

    def __call__(self, url, **kwargs):
        try:
            return self._inner_call(url, **kwargs)
        except (Timeout, ConnectionError, RequestException) as ex:
            message = ex.response.reason if getattr(ex, 'response', None) is not None else type(ex).__name__
            raise GetterError(message, ex, not isinstance(ex, RequestException))

    def _inner_call(self, url, **kwargs):
        if 'timeout' not in kwargs:
            kwargs['timeout'] = 20
        result = self.session.get(url, **kwargs)
        if result is None:
            return
        if result.status_code == 401:
            if self.login():
                result = self.session.get(url, **kwargs)
                if result is None:
                    return

        if result.status_code == 404:
            return
        result.raise_for_status()
        return result class GetterError(Exception):
    def __init__(self, message, cause, connection_error):
        super(GetterError, self).__init__()
        self.message = message
        self.cause = cause
        self.connection_error = connection_error
        self.request = getattr(cause, 'request', None)
        self.response = getattr(cause, 'response', None)
import os
import re
import threading
import urllib
    import urllib.parse as urlparse
    import urlparse
import xbmc
import xbmcvfs
from contextlib import closing
from lib import cleaner
from lib.libs import mediainfo as info, mediatypes, pykodi, quickjson, utils
from lib.libs.addonsettings import settings
from lib.libs.pykodi import localize as L, log
from lib.libs.webhelper import Getter, GetterError
try:
except ImportError: # py2


CANT_CONTACT_PROVIDER = 32034
HTTP_ERROR = 32035
CANT_WRITE_TO_FILE = 32037
REMOTE_CONTROL_REQUIRED = 32039

FILEERROR_LIMIT = 3
PROVIDERERROR_LIMIT = 3

TEMP_DIR = 'special://temp/recycledartwork/'

typemap = {'image/jpeg': 'jpg', 'image/png': 'png', 'image/gif': 'gif'}

# REVIEW: Deleting replaced artwork. If [movie base name]-fanart.jpg exists and AB is
#  configured for fanart.jpg, downloading a new artwork will save to the short name but
#  leave the long name, and the next scan will pick up the long name.
#  ditto scanning 'logo.png' at first and saving new 'clearlogo.png', but clearlogo will be picked
#  first by the next scan so that's not such a big deal.

class FileManager(object):
    def __init__(self, debug=False, bigcache=False):
        self.getter = Getter()
        self.getter.session.headers['User-Agent'] = settings.useragent
        self.size = 0
        self.fileerror_count = 0
        self.provider_errors = {}
        self.debug = debug
        self.alreadycached = None if not bigcache else []
        self._build_imagecachebase()

    def _build_imagecachebase(self):
        result = pykodi.execute_jsonrpc({"jsonrpc": "2.0", "id": 1, "method": "Settings.GetSettings",
            "params": {"filter": {"category": "control", "section": "services"}}})
        port = 80
        username = ''
        password = ''
        secure = False
        server_enabled = True
        if result.get('result', {}).get('settings'):
            for setting in result['result']['settings']:
                if setting['id'] == 'services.webserver' and not setting['value']:
                    server_enabled = False
                    break
                if setting['id'] == 'services.webserverusername':
                    username = setting['value']
                elif setting['id'] == 'services.webserverport':
                    port = setting['value']
                elif setting['id'] == 'services.webserverpassword':
                    password = setting['value']
                elif setting['id'] == 'services.webserverssl' and setting['value']:
                    secure = True
            username = '{0}:{1}@'.format(username, password) if username and password else ''
        else:
            server_enabled = False
        if server_enabled:
            protocol = 'https' if secure else 'http'
            self.imagecachebase = '{0}://{1}localhost:{2}/image/'.format(protocol, username, port)
        else:
            self.imagecachebase = None
            log(L(REMOTE_CONTROL_REQUIRED), xbmc.LOGWARNING)

    def downloadfor(self, mediaitem, allartwork=True):
        if self.fileerror_count >= FILEERROR_LIMIT:
            return False, ''
        if not info.can_saveartwork(mediaitem):
            return False, ''
        to_download = get_downloadable_art(mediaitem, allartwork)
        if not to_download:
            return False, ''
        services_hit = False
        error = ''
        localfiles = get_local_art(mediaitem, allartwork)
        for arttype, url in to_download.items():
            hostname = urlparse.urlparse(url).netloc
            if self.provider_errors.get(hostname, 0) >= PROVIDERERROR_LIMIT:
                continue

            full_basefilepath = info.build_artwork_basepath(mediaitem, arttype)