def _process_chunk(self, medialist, singleitemlist, singleitem): artcount = 0 currentitem = 0 aborted = False for mediaitem in medialist: if is_excluded(mediaitem): continue if mediaitem.mediatype in mediatypes.audiotypes and get_kodi_version( ) < 18: continue self.update_progress(currentitem * 100 // len(medialist), mediaitem.label) info.add_additional_iteminfo(mediaitem, self.processed, None if self.localmode else search) currentitem += 1 try: services_hit = self._process_item(mediaitem, singleitem) except FileError as ex: services_hit = True mediaitem.error = ex.message log(ex.message, xbmc.LOGERROR) self.notify_warning(ex.message, None, True) reporting.report_item(mediaitem, singleitemlist or mediaitem.error) artcount += len(mediaitem.updatedart) if not services_hit: if self.monitor.abortRequested(): aborted = True break elif self.monitor.waitForAbort(THROTTLE_TIME): aborted = True break return aborted, currentitem, artcount
def remove_texture(textureid): json_request = get_base_json_request('Textures.RemoveTexture') json_request['params']['textureid'] = textureid json_result = pykodi.execute_jsonrpc(json_request) if not check_json_result(json_result, 'OK', json_request): log(json_result)
def set_arttypes(root, arttype_map): for key, artlist in arttype_map.items(): if key not in mediatype_map: if key not in unsupported_types: log( "Can't set arttypes for '{0}' in advancedsettings.xml". format(key), xbmc.LOGNOTICE) continue typemap = mediatype_map[key] library_elem = root.find(typemap[1]) if library_elem is None: library_elem = ET.SubElement(root, typemap[1]) mediatype_elem = library_elem.find(typemap[0]) if artlist: if mediatype_elem is None: mediatype_elem = ET.SubElement(library_elem, typemap[0]) else: mediatype_elem.clear() for arttype in artlist: if arttype in typemap[2]: continue arttype_elem = ET.SubElement(mediatype_elem, ARTTYPE_TAG) arttype_elem.text = arttype elif mediatype_elem is not None: library_elem.remove(mediatype_elem)
def read_xml(): if not xbmcvfs.exists(FILENAME): return ET.Element(ROOT_TAG) parser = ET.XMLParser(target=CommentedTreeBuilder()) with closing(xbmcvfs.File(FILENAME)) as as_xml: try: return ET.parse(as_xml, parser).getroot() except ET.ParseError: log("Can't parse advancedsettings.xml", xbmc.LOGWARNING)
def setlanguages(self): languages = [] if settings.language_override: languages.append(settings.language_override) if settings.language_fallback_kodi: newlang = pykodi.get_language(xbmc.ISO_639_1) if newlang not in languages: languages.append(newlang) if settings.language_fallback_en and 'en' not in languages: languages.append('en') self.autolanguages = languages log("Working language filter: " + str(languages))
def set_item_details(dbid, mediatype, **details): assert mediatype in typemap mapped = typemap[mediatype] basestr = 'VideoLibrary.Set{0}Details' if mediatype not in mediatypes.audiotypes else 'AudioLibrary.Set{0}Details' json_request = get_base_json_request(basestr.format(mapped[0])) json_request['params'] = details json_request['params'][mediatype + 'id'] = dbid json_result = pykodi.execute_jsonrpc(json_request) if not check_json_result(json_result, 'OK', json_request): log(json_result)
def save_backup(): if xbmcvfs.exists(FILENAME): xbmcvfs.copy(FILENAME, FILENAME_BAK) result = xbmcvfs.exists(FILENAME_BAK) if result: xbmcgui.Dialog().notification("Artwork Beef", L(BACKUP_SUCCESSFUL)) else: xbmcgui.Dialog().notification("Artwork Beef", L(BACKUP_UNSUCCESSFUL), xbmc.LOGWARNING) return result log("advancedsettings.xml doesn't exist, can't save backup", xbmc.LOGNOTICE) return True
def restore_backup(): if xbmcvfs.exists(FILENAME_BAK): xbmcvfs.copy(FILENAME_BAK, FILENAME) xbmcvfs.delete(FILENAME_BAK) result = xbmcvfs.exists(FILENAME) if result: xbmcgui.Dialog().notification("Artwork Beef", L(RESTORE_SUCCESSFUL)) else: xbmcgui.Dialog().notification("Artwork Beef", L(RESTORE_UNSUCCESSFUL), xbmc.LOGWARNING) return result log("advancedsettings.xml.beef.bak doesn't exist, can't restore backup", xbmc.LOGWARNING) return False
def downloadforitem(mediaitem): try: fileman.downloadfor(mediaitem) newart = dict((k, v) for k, v in mediaitem.downloadedart.iteritems() if not v or not v.startswith('http')) for arttype in newart: # remove old URL from texture cache if mediaitem.art.get(arttype, '').startswith('http'): quickjson.remove_texture_byurl(mediaitem.art[arttype]) return newart except FileError as ex: mediaitem.error = ex.message log(ex.message, xbmc.LOGERROR) xbmcgui.Dialog().notification("Artwork Beef", ex.message, xbmcgui.NOTIFICATION_ERROR) return {}
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 = '******'.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 get_movie_path_list(stackedpath): """Returns a list of filenames that can be used to find a movie's supporting files. The list includes the common base of all provided parts for stacked movies, and the parent directory of VIDEO_TS/BDMV. If neither applies, returns a list of one item, the original path. Check for the supporting files from each of these results. The first path can be used for creating files.""" result = [] if not stackedpath.startswith('stack://'): result = [stackedpath] else: firstpath, path2 = stackedpath[8:].split(' , ')[0:2] path, filename = split(firstpath) if filename: filename2 = basename(path2) for regex in moviestacking: offset = 0 while True: match = regex.match(filename, offset) match2 = regex.match(filename2, offset) if match is not None and match2 is not None and match.group(1).lower() == match2.group(1).lower(): if match.group(2).lower() == match2.group(2).lower(): offset = match.start(3) continue # DEPRECATED: Returning the path to part1 doesn't seem to work in Kodi. # Not sure where I got that idea, but it shouldn't be used. # Also, AB created the ones missing `group(3)` for awhile, but it is wrong. # Adding both here so that file scanning still finds them. pathbase = path + get_pathsep(path) + filename[:offset] + match.group(1) result = [ pathbase + match.group(3) + match.group(4), pathbase + match.group(4), firstpath] break else: # folder stacking pass # I can't even get Kodi to add stacked VIDEO_TS rips period if not result: log("Couldn't get an unstacked path from \"{0}\"".format(stackedpath), xbmc.LOGWARNING) result = [firstpath] if parent_dir(result[0]) in ('VIDEO_TS', 'BDMV'): result.append(dirname(dirname(result[0])) + get_pathsep(result[0]) + basename(result[0])) return result
def get_external_artwork(self, mediatype, seasons, uniqueids, missing=None): images = {} error = None for provider in providers.external.get(mediatype, ()): errcount = self.providererrors.get(provider.name, 0) if errcount == MAX_ERRORS: continue try: providerimages = provider.get_images(uniqueids, missing) self.providererrors[provider.name] = 0 except Exception as ex: # gross. need to split "getting" and "parsing" then catch and wrap exceptions # on the latter, then leave most of this code back for "ProviderError" only errcount += 1 self.providererrors[provider.name] = errcount if errcount != 1 and errcount != MAX_ERRORS: continue error = {'providername': provider.name.display} if errcount == 1: # notify on first error error['message'] = getattr(ex, 'message', repr(ex)) elif errcount == MAX_ERRORS: # and on last error when we're no longer going to try this provider error['message'] = L(TOO_MANY_ERRORS) if not isinstance(ex, ProviderError): log("Error parsing provider response", xbmc.LOGWARNING) log(traceback.format_exc(), xbmc.LOGWARNING) continue for arttype, artlist in providerimages.iteritems(): if arttype.startswith('season.'): season = arttype.rsplit('.', 2)[1] if int(season) not in seasons: # Don't add artwork for seasons we don't have continue if arttype not in images: images[arttype] = [] images[arttype].extend(artlist) if self.monitor.abortRequested(): break return images, error
def _rotate_file(): newdate = ndate = pykodi.get_infolabel('System.Date(yyyy-mm-dd)') count = 0 while _exists(newdate): count += 1 newdate = ndate + '.' + str(count) if not xbmcvfs.copy(_get_filepath(), _get_filepath(newdate)): log("Could not copy latest report to new filename", xbmc.LOGWARNING, 'reporting') return False if not xbmcvfs.delete(_get_filepath()): log("Could not delete old copy of latest report", xbmc.LOGWARNING, 'reporting') return False # delete old reports _, files = xbmcvfs.listdir(settings.datapath) reportfiles = sorted(f[:-4] for f in files if f.startswith(REPORT_NAME)) while len(reportfiles) > REPORT_COUNT: filename = reportfiles[0] + '.txt' if not xbmcvfs.delete(settings.datapath + filename): log("Could not delete old report '{0}'".format(filename), xbmc.LOGWARNING, 'reporting') return False del reportfiles[0] report_startup() return True
def process_item(self, mediatype, dbid, mode): if self.processor_busy: return if mode == MODE_DEBUG: mode = MODE_AUTO self.set_debug(True) if mode == MODE_GUI: busy = pykodi.get_busydialog() busy.create() if mediatype in mediatypes.artinfo and (mediatype not in mediatypes.audiotypes or get_kodi_version() >= 18): mediaitem = info.MediaItem( quickjson.get_item_details(dbid, mediatype)) log("Processing {0} '{1}' {2}.".format( mediatype, mediaitem.label, 'automatically' if mode == MODE_AUTO else 'manually')) else: if mode == MODE_GUI: busy.close() xbmcgui.Dialog().notification( "Artwork Beef", L(NOT_SUPPORTED_MESSAGE).format(mediatype), '-', 6500) return self.init_run() if mediatype == mediatypes.EPISODE: series = quickjson.get_item_details(mediaitem.tvshowid, mediatypes.TVSHOW) if not any(uniqueid in settings.autoadd_episodes for uniqueid in series['uniqueid'].itervalues()): mediaitem.skip_artwork = ['fanart'] info.add_additional_iteminfo(mediaitem, self.processed, search) if not mediaitem.uniqueids and not mediatypes.only_filesystem( mediaitem.mediatype): if mediatype in mediatypes.require_manualid: self.manual_id(mediaitem) if mode == MODE_GUI: self._manual_item_process(mediaitem, busy) else: medialist = [mediaitem] if mediatype == mediatypes.TVSHOW and not mediatypes.disabled( mediatypes.EPISODE): gen_epthumb = mediatypes.generatethumb(mediatypes.EPISODE) download_ep = mediatypes.downloadanyartwork(mediatypes.EPISODE) if mediaitem.uniqueids and any( x in mediaitem.uniqueids.values() for x in settings.autoadd_episodes): medialist.extend( info.MediaItem(ep) for ep in quickjson.get_episodes(dbid)) elif gen_epthumb or download_ep: for episode in quickjson.get_episodes(dbid): if gen_epthumb and not info.has_generated_thumbnail(episode) \ or download_ep and info.has_art_todownload(episode['art'], mediatypes.EPISODE): episode = info.MediaItem(episode) episode.skip_artwork = ['fanart'] medialist.append(episode) elif mediatype == mediatypes.ARTIST and not mediatypes.disabled( mediatypes.ALBUM): medialist.extend( info.MediaItem(album) for album in quickjson.get_albums( mediaitem.label, mediaitem.dbid)) if mediatype in (mediatypes.ALBUM, mediatypes.ARTIST) and not mediatypes.disabled(mediatypes.ALBUM) \ and not mediatypes.disabled(mediatypes.SONG): medialist.extend( info.MediaItem(song) for song in quickjson.get_songs( mediaitem.mediatype, mediaitem.dbid)) self.process_medialist(medialist, True)
self.processor.close_progress() def get_date(): return pykodi.get_infolabel('System.Date(yyyy-mm-dd)') def include_any_episode(): return not mediatypes.disabled(mediatypes.EPISODE) \ and (mediatypes.generatethumb(mediatypes.EPISODE) or mediatypes.downloadanyartwork(mediatypes.EPISODE) \ or settings.autoadd_episodes) def include_episode(episode): return mediatypes.generatethumb(mediatypes.EPISODE) and not info.item_has_generated_thumbnail(episode) \ or info.has_art_todownload(episode.art, mediatypes.EPISODE) def _buildsongs(albumgroup): result = {} if mediatypes.disabled(mediatypes.SONG): return result songfilter = {'field': 'album', 'operator': 'is', 'value': [album.label for album in albumgroup]} for song in quickjson.get_songs(songfilter=songfilter): if song['albumid'] not in result: result[song['albumid']] = [] result[song['albumid']].append(song) return result if __name__ == '__main__': log('Service started', xbmc.LOGINFO) ArtworkService().run() log('Service stopped', xbmc.LOGINFO)
def notify_count(message, count): countmessage = message.format(count) log(countmessage, xbmc.LOGINFO) xbmcgui.Dialog().notification('Artwork Beef', countmessage)
def log(self, message, level=xbmc.LOGDEBUG): if self.mediatype: log(message, level, tag='%s:%s' % (self.name.sort, self.mediatype)) else: log(message, level, tag='%s' % self.name.sort)
def _process_item(self, mediaitem, singleitem=False, auto=True): log("Processing {0} '{1}' automatically.".format( mediaitem.mediatype, mediaitem.label)) mediatype = mediaitem.mediatype onlyfs = self.localmode or mediatypes.only_filesystem( mediaitem.mediatype) if not mediaitem.uniqueids and not onlyfs: mediaitem.missingid = True if singleitem: header = L(NO_IDS_MESSAGE) message = "{0} '{1}'".format(mediatype, mediaitem.label) log(header + ": " + message, xbmc.LOGNOTICE) xbmcgui.Dialog().notification("Artwork Beef: " + header, message, xbmcgui.NOTIFICATION_INFO) if auto: cleaned = get_simpledict_updates(mediaitem.art, cleaner.clean_artwork(mediaitem)) if cleaned: if not self.debug: add_art_to_library(mediatype, mediaitem.seasons, mediaitem.dbid, cleaned) mediaitem.art.update(cleaned) mediaitem.art = dict(item for item in mediaitem.art.iteritems() if item[1]) mediaitem.missingart = list( info.iter_missing_arttypes(mediaitem, mediaitem.art)) services_hit, error = self.gatherer.getartwork(mediaitem, onlyfs, auto) if auto: existingart = dict(mediaitem.art) selectedart = dict( (key, image['url']) for key, image in mediaitem.forcedart.iteritems()) existingart.update(selectedart) # Then add the rest of the missing art selectedart.update( self.get_top_missing_art( info.iter_missing_arttypes(mediaitem, existingart), mediatype, existingart, mediaitem.availableart)) selectedart = get_simpledict_updates(mediaitem.art, selectedart) mediaitem.selectedart = selectedart toset = dict(selectedart) if not self.localmode and mediatypes.downloadanyartwork( mediaitem.mediatype): sh, er = self.downloader.downloadfor(mediaitem) services_hit = services_hit or sh error = error or er toset.update(mediaitem.downloadedart) if toset: mediaitem.updatedart = list( set(mediaitem.updatedart + toset.keys())) if not self.debug: add_art_to_library(mediatype, mediaitem.seasons, mediaitem.dbid, toset) self.cachelocal(mediaitem, toset) if error: if isinstance(error, dict): header = L(PROVIDER_ERROR_MESSAGE).format( error['providername']) error = '{0}: {1}'.format(header, error['message']) mediaitem.error = error log(error, xbmc.LOGWARNING) self.notify_warning(error) elif auto and not self.debug and not self.localmode: if not (mediatype == mediatypes.EPISODE and 'fanart' in mediaitem.skip_artwork) and \ mediatype != mediatypes.SONG: self.processed.set_nextdate( mediaitem.dbid, mediatype, mediaitem.label, datetime_now() + timedelta(days=self.get_nextcheckdelay(mediaitem))) if mediatype == mediatypes.TVSHOW: self.processed.set_data(mediaitem.dbid, mediatype, mediaitem.label, mediaitem.season) if mediaitem.borked_filename: msg = L(FILENAME_ENCODING_ERROR).format(mediaitem.file) if not mediaitem.error: mediaitem.error = msg log(msg, xbmc.LOGWARNING) if self.debug: log(mediaitem, xbmc.LOGNOTICE) return services_hit
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) if not full_basefilepath: continue if self.debug: mediaitem.downloadedart[arttype] = full_basefilepath + '.ext' continue result, err = self.doget(url) if err: error = err self.provider_errors[hostname] = self.provider_errors.get( hostname, 0) + 1 continue if not result: # 404 URL dead, wipe it so we can add another one later mediaitem.downloadedart[arttype] = None continue self.size += int(result.headers.get('content-length', 0)) services_hit = True ext = get_file_extension(result.headers.get('content-type'), url) if not ext: log("Can't determine extension for '{0}'\nfor image type '{1}'" .format(url, arttype)) continue full_basefilepath += '.' + ext if xbmcvfs.exists(full_basefilepath): if extrafanart_name_used(full_basefilepath, localfiles): # REVIEW: can this happen in any other circumstance? full_basefilepath = get_next_filename( full_basefilepath, localfiles) localfiles.append(full_basefilepath) if xbmcvfs.exists( full_basefilepath) and settings.recycle_removed: recyclefile(full_basefilepath) else: folder = os.path.dirname(full_basefilepath) if not xbmcvfs.exists(folder): xbmcvfs.mkdirs(folder) # For now this just downloads the whole thing in memory, then saves it to file. # Maybe chunking it will be better when GIFs are handled file_ = xbmcvfs.File(full_basefilepath, 'wb') with closing(file_): if not file_.write(result.content): self.fileerror_count += 1 raise FileError( L(CANT_WRITE_TO_FILE).format(full_basefilepath)) self.fileerror_count = 0 mediaitem.downloadedart[arttype] = full_basefilepath log("downloaded '{0}'\nto image file '{1}'".format( url, full_basefilepath)) return services_hit, error
def add_additional_iteminfo(mediaitem, processed, search=None): '''Get more data from the Kodi library, processed items, and look up web service IDs.''' if search and mediaitem.mediatype in search: search = search[mediaitem.mediatype] if mediaitem.mediatype == mediatypes.TVSHOW: # TODO: Split seasons out to separate items if not mediaitem.seasons: mediaitem.seasons, seasonart = _get_seasons_artwork( quickjson.get_seasons(mediaitem.dbid)) mediaitem.art.update(seasonart) if search and settings.default_tvidsource == 'tmdb' and 'tvdb' not in mediaitem.uniqueids: # TODO: Set to the Kodi library if found resultids = search.get_more_uniqueids(mediaitem.uniqueids, mediaitem.mediatype) if 'tvdb' in resultids: mediaitem.uniqueids['tvdb'] = resultids['tvdb'] elif mediaitem.mediatype == mediatypes.SEASON: tvshow = quickjson.get_item_details(mediaitem.tvshowid, mediatypes.TVSHOW) mediaitem.file = tvshow['file'] elif mediaitem.mediatype == mediatypes.EPISODE: if settings.default_tvidsource == 'tmdb': # TheMovieDB scraper sets episode IDs that can't be used in the API, but seriesID/season/episode works uniqueids = quickjson.get_item_details( mediaitem.tvshowid, mediatypes.TVSHOW)['uniqueid'] uniqueid = uniqueids.get('tmdb', uniqueids.get('unknown')) if uniqueid: mediaitem.uniqueids['tmdbse'] = '{0}/{1}/{2}'.format( uniqueid, mediaitem.season, mediaitem.episode) elif mediaitem.mediatype == mediatypes.MOVIESET: if not mediaitem.uniqueids.get('tmdb'): uniqueid = processed.get_data(mediaitem.dbid, mediaitem.mediatype, mediaitem.label) if search and not uniqueid and not mediatypes.only_filesystem( mediaitem.mediatype): searchresults = search.search(mediaitem.label, mediaitem.mediatype) if searchresults: for result in searchresults: if result['label'] == mediaitem.label: uniqueid = result['uniqueids']['tmdb'] break uniqueid = searchresults[0]['uniqueids']['tmdb'] if uniqueid: processed.set_data(mediaitem.dbid, mediatypes.MOVIESET, mediaitem.label, uniqueid) else: processed.set_data(mediaitem.dbid, mediatypes.MOVIESET, mediaitem.label, None) mediaitem.error = L(CANT_FIND_MOVIESET) log( "Could not find set '{0}' on TheMovieDB".format( mediaitem.label), xbmc.LOGNOTICE) mediaitem.uniqueids['tmdb'] = uniqueid if not processed.exists(mediaitem.dbid, mediaitem.mediatype, mediaitem.label): _remove_set_movieposters(mediaitem) if settings.setartwork_fromparent and not mediaitem.file: _identify_parent_movieset(mediaitem) elif mediaitem.mediatype == mediatypes.MUSICVIDEO: if not mediaitem.uniqueids.get('mbtrack') or not mediaitem.uniqueids.get('mbgroup') \ or not mediaitem.uniqueids.get('mbartist'): newdata = processed.get_data(mediaitem.dbid, mediaitem.mediatype, mediaitem.label) if newdata: mb_t, mb_al, mb_ar = newdata.split('/') mediaitem.uniqueids = { 'mbtrack': mb_t, 'mbgroup': mb_al, 'mbartist': mb_ar } elif search and not mediatypes.only_filesystem( mediaitem.mediatype): results = search.search(mediaitem.label, mediatypes.MUSICVIDEO) uq = results and results[0].get('uniqueids') if uq and uq.get('mbtrack') and uq.get('mbgroup') and uq.get( 'mbartist'): mediaitem.uniqueids = uq processed.set_data( mediaitem.dbid, mediatypes.MUSICVIDEO, mediaitem.label, uq['mbtrack'] + '/' + uq['mbgroup'] + '/' + uq['mbartist']) else: processed.set_data(mediaitem.dbid, mediatypes.MUSICVIDEO, mediaitem.label, None) mediaitem.error = L(CANT_FIND_MUSICVIDEO) log( "Could not find music video '{0}' on TheAudioDB". format(mediaitem.label), xbmc.LOGNOTICE) elif mediaitem.mediatype == mediatypes.ALBUM: folders = _identify_album_folders(mediaitem) if folders: mediaitem.file, mediaitem.discfolders = folders
def _manual_item_process(self, mediaitem, busy): self._process_item(mediaitem, True, False) busy.close() if mediaitem.availableart or mediaitem.forcedart: availableart = dict(mediaitem.availableart) if mediaitem.mediatype == mediatypes.TVSHOW and 'fanart' in availableart: # add unseasoned backdrops as manual-only options for each season fanart unseasoned_backdrops = [ dict(art) for art in availableart['fanart'] if not art.get('hasseason') ] if unseasoned_backdrops: for season in mediaitem.seasons.keys(): key = 'season.{0}.fanart'.format(season) if key in availableart: availableart[key].extend(unseasoned_backdrops) else: availableart[key] = list(unseasoned_backdrops) if mediaitem.mediatype in (mediatypes.MOVIE, mediatypes.MOVIESET ) and 'poster' in availableart: # add no-language posters from TMDB as manual-only options for 'keyart' nolang_posters = [ dict(art) for art in availableart['poster'] if not art['language'] ] for art in nolang_posters: if art['provider'].sort == 'themoviedb.org': if 'keyart' not in availableart: availableart['keyart'] = [] availableart['keyart'].append(art) tag_forcedandexisting_art(availableart, mediaitem.forcedart, mediaitem.art) selectedarttype, selectedart = prompt_for_artwork( mediaitem.mediatype, mediaitem.label, availableart, self.monitor) if selectedarttype and selectedarttype not in availableart: self.manual_id(mediaitem) return if selectedarttype and selectedart: if mediatypes.get_artinfo(mediaitem.mediatype, selectedarttype)['multiselect']: selectedart = info.fill_multiart(mediaitem.art, selectedarttype, selectedart) else: selectedart = {selectedarttype: selectedart} selectedart = get_simpledict_updates(mediaitem.art, selectedart) mediaitem.selectedart = selectedart toset = dict(selectedart) if settings.remove_deselected_files: self.downloader.remove_deselected_files(mediaitem) if mediatypes.downloadanyartwork(mediaitem.mediatype): try: self.downloader.downloadfor(mediaitem, False) except FileError as ex: mediaitem.error = ex.message log(ex.message, xbmc.LOGERROR) xbmcgui.Dialog().notification( "Artwork Beef", ex.message, xbmcgui.NOTIFICATION_ERROR) toset.update(mediaitem.downloadedart) if toset: mediaitem.updatedart = toset.keys() add_art_to_library(mediaitem.mediatype, mediaitem.seasons, mediaitem.dbid, toset) self.cachelocal(mediaitem, toset) reporting.report_item(mediaitem, True, True, self.downloader.size) if not mediaitem.error: notifycount(len(toset)) else: xbmcgui.Dialog().notification( L(NOT_AVAILABLE_MESSAGE), L(SOMETHING_MISSING) + ' ' + L(FINAL_MESSAGE), '-', 8000) self.finish_run()