def identify_unmatched(mediatype): busy = pykodi.get_busydialog() busy.create() processed = ProcessedItems() ulist = quickjson.get_item_list(mediatype) if mediatype == mediatypes.MUSICVIDEO: for item in ulist: item['label'] = info.build_music_label(item) unmatched = [ item for item in ulist if not processed.get_data(item[mediatype + 'id'], mediatype, item['label']) ] busy.close() if unmatched: selected = xbmcgui.Dialog().select(L( M.UNMATCHED_ITEMS), [item['label'] for item in unmatched]) if selected < 0: return # Cancelled mediaitem = info.MediaItem(unmatched[selected]) info.add_additional_iteminfo(mediaitem, processed, search) processor = ArtworkProcessor() if processor.manual_id(mediaitem): processor.process_item(mediatype, mediaitem.dbid, 'auto') else: xbmcgui.Dialog().notification("Artwork Beef", L(M.NO_UNMATCHED_ITEMS), xbmcgui.NOTIFICATION_INFO)
def doget(self, url, **kwargs): try: return self.getter(url, **kwargs) except GetterError as ex: message = L(CANT_CONTACT_PROVIDER) if ex.connection_error else L( HTTP_ERROR).format(ex.message) raise ProviderError(message, ex.cause)
def onInit(self): # This is called every time the window is shown if not self.selected: self.getControl( 1).setLabel("Artwork Beef: " + L(CHOOSE_TYPE_HEADER).format(self.medialabel)) self.getControl(3).setVisible(False) self.getControl(5).setVisible(self.show_refresh) self.getControl(5).setLabel(L(REFRESH_ITEM)) self.guilist = self.getControl(6) for arttype in self.arttypes: listitem = xbmcgui.ListItem(arttype['label']) summary = L(AVAILABLE_COUNT).format(arttype['count']) listitem.setLabel2(summary) # DEPRECATED: Above Krypton and higher (only), below Jarvis and lower (only) listitem.setProperty('Addon.Summary', summary) listitem.setPath(arttype['arttype']) if arttype.get('url'): listitem.setIconImage(arttype['url']) # DEPRECATED: Above is deprecated in Jarvis, but still works through Krypton (at least) # listitem.setArt({'icon': arttype.get('url')}) self.guilist.addItem(listitem) else: self.selected = None self.setFocus(self.guilist)
def make_local(): fileman = FileManager() 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 {} downloaded = runon_medialist(downloadforitem, L(M.MAKE_LOCAL), fg=True) xbmcgui.Dialog().ok( "Artwork Beef", L(M.DOWNLOAD_COUNT).format(downloaded) + ' - {0:0.2f}MB'.format(fileman.size / 1000000.00))
def show_artwork_log(): if pykodi.get_kodi_version() < 16: xbmcgui.Dialog().notification("Artwork Beef", L(M.VERSION_REQUIRED).format("Kodi 16"), xbmcgui.NOTIFICATION_INFO) return xbmcgui.Dialog().textviewer("Artwork Beef: " + L(M.REPORT_TITLE), reporting.get_latest_report())
def onInit(self): self.getControl(1).setLabel( "Artwork Beef: " + L(CHOOSE_ART_HEADER).format(self.arttype, self.medialabel)) self.getControl(3).setVisible(False) self.getControl(5).setVisible(self.multi) self.getControl(5).setLabel('$LOCALIZE[186]') self.guilist = self.getControl(6) for image in self.artlist: provider = image['provider'].display if isinstance(provider, int): provider = L(provider) secondprovider = image.get('second provider') if secondprovider: if isinstance(secondprovider, int): secondprovider = L(secondprovider) provider = '{0}, {1}'.format(provider, secondprovider) title = image.get('title') if not title and 'subtype' in image: title = image['subtype'].display language = xbmc.convertLanguage( image['language'], xbmc.ENGLISH_NAME) if image.get('language') else None if not title: title = language if title and len(title) < 20 and not secondprovider: label = '{0} from {1}'.format(title, provider) summary = language if language and language != title else '' else: label = provider if language and language != title: title = language + ' ' + title summary = title if title else '' rating = image.get('rating') size = image.get('size') if (rating or size) and summary: summary += '\n' if size: summary += image['size'].display if rating and size: summary += ' ' if rating: summary += image['rating'].display listitem = xbmcgui.ListItem(label) listitem.setLabel2(summary) # DEPRECATED: Above Krypton and higher (only), below Jarvis and lower (only) listitem.setProperty('Addon.Summary', summary) listitem.setIconImage(image['preview']) # DEPRECATED: Above is deprecated in Jarvis, but still works through Krypton (at least) # listitem.setArt({'icon': image['preview']}) listitem.setPath(image['url']) if image.get('existing'): listitem.select(True) self.guilist.addItem(listitem) self.setFocus(self.guilist)
def save_arttypes(arttype_map): root = read_xml() if root is None: xbmcgui.Dialog().notification("Artwork Beef", L(MALFORMED), xbmcgui.NOTIFICATION_WARNING) return False set_arttypes(root, arttype_map) if save_backup(): save_xml(root) xbmcgui.Dialog().ok("Artwork Beef", L(RESTART_KODI))
def doget(self, url, **kwargs): try: result = self.getter(url, **kwargs) if not result and url.startswith('http://'): # Try https, the browser "that totally shows this image" probably is, even if no redirect result, err = self.doget('https://' + url[7:]) if err or not result: result = None return result, None except GetterError as ex: message = L(CANT_CONTACT_PROVIDER) if ex.connection_error \ else L(HTTP_ERROR).format(ex.message) + '\n' + url return None, message
def cache_artwork(librarytype='videos'): fileman = FileManager(False, True) if not fileman.imagecachebase: xbmcgui.Dialog().notification("Artwork Beef", L(M.REMOTE_CONTROL_REQUIRED), xbmcgui.NOTIFICATION_WARNING) return heading = L(M.CACHE_VIDEO_ARTWORK if librarytype == 'videos' else M.CACHE_MUSIC_ARTWORK) cached = runon_medialist(lambda mi: fileman.cachefor(mi.art, True), heading, librarytype, fg=True) xbmcgui.Dialog().ok("Artwork Beef", L(M.CACHED_COUNT).format(cached))
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 report_item(mediaitem, forcedreport=False, manual=False, downloaded_size=0): if debug or not forcedreport and not settings.report_peritem: return with _get_file() as reportfile: itemtitle = "{0} '{1}'".format(mediaitem.mediatype, mediaitem.label) if mediaitem.mediatype != mediatypes.EPISODE \ else "{0} '{1}' of '{2}'".format(mediaitem.mediatype, mediaitem.label, mediaitem.showtitle) message = "== {0}: ".format(get_datetime()) if forcedreport else "" message += L(PROCESSING_MANUALLY if manual else PROCESSING) write(reportfile, message.format(itemtitle)) message = L(NO_IDS_MESSAGE) if mediaitem.missingid \ else L(MISSING_LABEL).format(', '.join(mediaitem.missingart)) if mediaitem.missingart \ else L(NO_MISSING_ARTWORK) write(reportfile, message) if mediaitem.downloadedart: message = L(DOWNLOAD_COUNT).format(len(mediaitem.downloadedart)) if downloaded_size: message += ' - {0:0.2f}MB'.format(downloaded_size / 1000000.00) write(reportfile, message) if mediaitem.updatedart: write(reportfile, L(UPDATED_LABEL).format(', '.join(mediaitem.updatedart))) elif manual or mediaitem.missingart: write(reportfile, L(NO_UPDATES)) if mediaitem.error: write(reportfile, L(ERROR_MESSAGE).format(pykodi.scrub_message(mediaitem.error))) if forcedreport: finish_chunk(reportfile) if forcedreport and _should_rotate(): _rotate_file()
def report_start(medialist): if debug: return with _get_file() as reportfile: write(reportfile, "== {0}: ".format(get_datetime()) + L(PROCESSING_AUTOMATICALLY)) if not medialist: write(reportfile, L(NO_ITEMS)) write(reportfile, '') return mediatypecount = {} for item in medialist: mediatypecount[item.mediatype] = mediatypecount.get(item.mediatype, 0) + 1 write(reportfile, L(ITEM_COUNT).format(len(medialist)) + " - " + ', '.join('{0}: {1}'.format(mt, mediatypecount[mt]) for mt in mediatypecount))
def doget(self, url, **kwargs): try: result = self.getter(url, **kwargs) if not result and url.startswith('http://'): # Try https, the browser "that totally shows this image" probably is, even if no redirect result, err = self.doget('https://' + url[7:]) if err or not result: result = None return result, None except GetterError as ex: if ex.response is not None and ex.response.status_code == 403: # TVDB returns Forbidden for certain images. Don't show an error message, replace it return None, None message = L(CANT_CONTACT_PROVIDER) if ex.connection_error \ else L(HTTP_ERROR).format(ex.message) + '\n' + url return None, message
def onInit(self): self.getControl(1).setLabel(L(ACTION_SELECTSERIES)) self.getControl(3).setVisible(False) self.getControl(5).setVisible(True) self.getControl(5).setLabel('$LOCALIZE[186]') self.guilist = self.getControl(6) self.guilist.setVisible(True) for series in self.serieslist: listitem = xbmcgui.ListItem(series['label']) summary = str(series['year']) + ' - ' + series['plot'] listitem.setLabel2(summary) # DEPRECATED: Above Krypton and higher (only), below Jarvis and lower (only) listitem.setProperty('Addon.Summary', summary) listitem.setProperty('imdbnumber', series['imdbnumber']) art = series['art'] if 'poster' in series['art']: art['thumb'] = series['art']['poster'] listitem.setArt(art) if series['imdbnumber'] in self.originalselected: listitem.select(True) self.selected.append(series['imdbnumber']) self.guilist.addItem(listitem) self.originalselected = list(self.selected) self.setFocus(self.guilist)
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 save_arttypes_to_asxml(): can_continue = xbmcgui.Dialog().ok("Artwork Beef", L(M.INTRO_TEXT)) if not can_continue: return include_multiple = xbmcgui.Dialog().yesno("Artwork Beef", L(M.INCLUDE_MULTIPLES)) art_toset = {} for mediatype in mediatypes.artinfo: if include_multiple: art_toset[mediatype] = list( mediatypes.iter_every_arttype(mediatype)) else: art_toset[mediatype] = [ a for a in mediatypes.iter_every_arttype(mediatype) if not a[-1].isdigit() ] advancedsettings.save_arttypes(art_toset)
def set_autoaddepisodes(): busy = pykodi.get_busydialog() busy.create() serieslist = [ series for series in quickjson.get_tvshows(True) if series.get('imdbnumber') ] busy.close() selected = SeriesSelector('DialogSelect.xml', settings.addon_path, serieslist=serieslist, selected=settings.autoadd_episodes).prompt() if selected != settings.autoadd_episodes: settings.autoadd_episodes = selected if xbmcgui.Dialog().yesno(L(M.ADD_MISSING_HEADER), L(M.ADD_MISSING_MESSAGE)): pykodi.execute_builtin( 'NotifyAll(script.artwork.beef:control, ProcessAfterSettings)')
def report_startup(): if not xbmcvfs.exists(settings.datapath): xbmcvfs.mkdir(settings.datapath) with _get_file() as reportfile: reportfile.seek(0, os.SEEK_SET) newline = "= " + L(RUNNING_VERSION).format(settings.useragent) line_exists = any(newline in line for line in reportfile) if not line_exists: reportfile.seek(0, os.SEEK_END) write(reportfile, newline) finish_chunk(reportfile)
def remove_art(): # TODO: seasons and episodes and whatever like "add missing artwork" does listitem = sys.listitem mediatype = get_mediatype(listitem) dbid = get_dbid(listitem) if not (dbid or mediatype): return if not xbmcgui.Dialog().yesno("Artwork Beef: " + L(32427), L(750)): return remove_localfiles = xbmcgui.Dialog().yesno("Artwork Beef", L(32062)) mediaitem = info.MediaItem(quickjson.get_item_details(dbid, mediatype)) mediaitem.selectedart = cleaner.remove_specific_arttype(mediaitem, '* all') if remove_localfiles: FileManager().remove_deselected_files(mediaitem, True) info.update_art_in_library(mediatype, dbid, mediaitem.selectedart) info.remove_local_from_texturecache(mediaitem.art.values(), True) xbmcgui.Dialog().notification("Artwork Beef", L(32027).format(len(mediaitem.selectedart)))
def recyclefile(filename): firstdir = utils.parent_dir(filename) directory = TEMP_DIR pathsep = utils.get_pathsep(directory) if firstdir in ('extrafanart', 'extrathumbs'): directory += utils.parent_dir(os.path.dirname(filename)) + pathsep directory += firstdir if not xbmcvfs.exists(directory): xbmcvfs.mkdirs(directory) recycled_filename = directory + pathsep + os.path.basename(filename) if not xbmcvfs.copy(filename, recycled_filename): raise FileError(L(CANT_WRITE_TO_FILE).format(recycled_filename))
def add_missing_for(): options = [(L(M.FOR_NEW_VIDEOS), 'ProcessNewVideos'), (L(M.FOR_OLD_VIDEOS), 'ProcessNewAndOldVideos'), (L(M.FOR_ALL_VIDEOS), 'ProcessAllVideos')] if get_kodi_version() >= 18: options.extend(((L(M.FOR_NEW_AUDIO), 'ProcessNewMusic'), (L(M.FOR_OLD_AUDIO), 'ProcessNewAndOldMusic'), (L(M.FOR_ALL_AUDIO), 'ProcessAllMusic'))) selected = xbmcgui.Dialog().select(L(M.ADD_MISSING_FOR), [option[0] for option in options]) if selected >= 0 and selected < len(options): pykodi.execute_builtin( 'NotifyAll(script.artwork.beef:control, {0})'.format( options[selected][1]))
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_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 manual_id(self, mediaitem): label = ENTER_COLLECTION_NAME if mediaitem.mediatype == mediatypes.MOVIESET else ENTER_ARTIST_TRACK_NAMES result = xbmcgui.Dialog().input(L(label), mediaitem.label) if not result: return False # Cancelled options = search[mediaitem.mediatype].search(result, mediaitem.mediatype) selected = xbmcgui.Dialog().select( mediaitem.label, [option['label'] for option in options]) if selected < 0: return False # Cancelled uq = options[selected]['uniqueids'] toset = uq.get('tmdb') if not toset and 'mbtrack' in uq and 'mbgroup' in uq and 'mbartist' in uq: toset = '{0}/{1}/{2}'.format(uq['mbtrack'], uq['mbgroup'], uq['mbartist']) if toset: self.processed.set_data(mediaitem.dbid, mediaitem.mediatype, mediaitem.label, toset) return bool(toset)
def tag_forcedandexisting_art(availableart, forcedart, existingart): typeinsert = {} for exacttype, artlist in sorted( forcedart.iteritems(), key=lambda arttype: natural_sort(arttype[0])): arttype = info.get_basetype(exacttype) if arttype not in availableart: availableart[arttype] = artlist else: for image in artlist: match = next((available for available in availableart[arttype] if available['url'] == image['url']), None) if match: if 'title' in image and 'title' not in match: match['title'] = image['title'] match['second provider'] = image['provider'].display else: typeinsert[arttype] = typeinsert[ arttype] + 1 if arttype in typeinsert else 0 availableart[arttype].insert(typeinsert[arttype], image) typeinsert = {} for exacttype, existingurl in existingart.iteritems(): arttype = info.get_basetype(exacttype) if arttype in availableart: match = next((available for available in availableart[arttype] if available['url'] == existingurl), None) if match: match['preview'] = existingurl match['existing'] = True else: typeinsert[arttype] = typeinsert[ arttype] + 1 if arttype in typeinsert else 0 image = { 'url': existingurl, 'preview': existingurl, 'title': exacttype, 'existing': True, 'provider': SortedDisplay('current', L(CURRENT_ART)) } availableart[arttype].insert(typeinsert[arttype], image)
def report_end(medialist, abortedcount, downloaded_size): if debug: return if len(medialist) <= 1: if _should_rotate(): _rotate_file() return with _get_file() as reportfile: write(reportfile, get_datetime() + ": " + L(PROCESSING_ABORTED if abortedcount else PROCESSING_FINISHED)) if abortedcount: write(reportfile, L(PROCESSED_COUNT).format(abortedcount)) arttypecount = {} total = 0 downloaded = 0 errorcount = 0 for item in medialist: for arttype in item.updatedart: arttype = get_basetype(arttype) arttypecount[arttype] = arttypecount.get(arttype, 0) + 1 total += 1 if item.error: errorcount += 1 downloaded += len(item.downloadedart) if downloaded: # TODO: include # of existing items downloaded, or the # of items added and downloaded write(reportfile, L(DOWNLOAD_COUNT).format(downloaded) + ' - {0:0.2f}MB'.format(downloaded_size / 1000000.00)) if not total: write(reportfile, L(NO_UPDATES)) else: write(reportfile, L(UPDATE_COUNT).format(total) + " - " + ', '.join('{0}: {1}'.format(at, arttypecount[at]) for at in arttypecount)) if errorcount: write(reportfile, L(ERRORS_MESSAGE)) finish_chunk(reportfile) if _should_rotate(): _rotate_file()
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 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 main(): settings.update_settings() mediatypes.update_settings() command = get_command() if command.get('dbid') and command.get('mediatype'): processor = ArtworkProcessor() processor.process_item(command['mediatype'], int(command['dbid']), command.get('mode', 'auto')) elif 'command' in command: if command['command'] == 'set_autoaddepisodes': set_autoaddepisodes() elif command['command'] == 'show_artwork_log': show_artwork_log() elif command['command'] == 'set_download_artwork' and command.get( 'mediatype'): set_download_artwork(command['mediatype']) else: processor = ArtworkProcessor() if processor.processor_busy: options = [ (L(M.STOP), 'NotifyAll(script.artwork.beef:control, CancelCurrent)') ] else: options = [ (L(M.ADD_MISSING_FOR), add_missing_for), (L(M.NEW_LOCAL_FILES), 'NotifyAll(script.artwork.beef:control, ProcessLocalVideos)'), (L(M.IDENTIFY_UNMATCHED_SETS), lambda: identify_unmatched(mediatypes.MOVIESET)), (L(M.IDENTIFY_UNMATCHED_MVIDS), lambda: identify_unmatched(mediatypes.MUSICVIDEO)), (L(M.REMOVE_SPECIFIC_TYPES), remove_specific_arttypes), (L(M.MAKE_LOCAL), make_local), (L(M.CACHE_VIDEO_ARTWORK), cache_artwork) ] if get_kodi_version() >= 18: options.append( (L(M.CACHE_MUSIC_ARTWORK), lambda: cache_artwork('music'))) options.append( (L(M.SAVE_ARTTYPES_TO_ASXML), save_arttypes_to_asxml)) if advancedsettings.has_backup(): options.append((L(M.RESTORE_ASXML_BACKUP), advancedsettings.restore_backup)) selected = xbmcgui.Dialog().select('Artwork Beef', [option[0] for option in options]) if selected >= 0 and selected < len(options): action = options[selected][1] if isinstance(action, basestring): pykodi.execute_builtin(action) else: action()
def runon_medialist(function, heading, medialist='videos', typelabel=None, fg=False): progress = xbmcgui.DialogProgress() if fg else xbmcgui.DialogProgressBG() progress.create(heading) monitor = xbmc.Monitor() if medialist == 'videos': steps_to_run = [ (lambda: quickjson.get_item_list(mediatypes.MOVIE), L(M.MOVIES)), (info.get_cached_tvshows, L(M.SERIES)), (quickjson.get_seasons, L(M.SEASONS)), (lambda: quickjson.get_item_list(mediatypes.MOVIESET), L(M.MOVIESETS)), (quickjson.get_episodes, L(M.EPISODES)), (lambda: quickjson.get_item_list(mediatypes.MUSICVIDEO), L(M.MUSICVIDEOS)) ] elif medialist == 'music' and get_kodi_version() >= 18: steps_to_run = [ (lambda: quickjson.get_item_list(mediatypes.ARTIST), L(M.ARTISTS)), (lambda: quickjson.get_item_list(mediatypes.ALBUM), L(M.ALBUMS)), (lambda: quickjson.get_item_list(mediatypes.SONG), L(M.SONGS)) ] else: steps_to_run = ((lambda: medialist, typelabel), ) stepsize = 100 // len(steps_to_run) def update_art_for_items(items, start): changedcount = 0 for i, item in enumerate(items): if fg: progress.update(start + i * stepsize // len(items), item['label']) else: progress.update(start + i * stepsize // len(items)) item = info.MediaItem(item) if item.mediatype == mediatypes.SEASON: item.file = info.get_cached_tvshow(item.tvshowid)['file'] updates = function(item) if isinstance(updates, int): changedcount += updates else: processed = utils.get_simpledict_updates(item.art, updates) if processed: info.update_art_in_library(item.mediatype, item.dbid, processed) changedcount += len(processed) if monitor.abortRequested() or fg and progress.iscanceled(): break return changedcount fixcount = 0 for i, (list_fn, listtype) in enumerate(steps_to_run): start = i * stepsize if fg: progress.update(start, line1=L(M.LISTING_ALL).format(listtype)) else: progress.update(start, message=L(M.LISTING_ALL).format(listtype)) fixcount += update_art_for_items(list_fn(), start) if monitor.abortRequested() or fg and progress.iscanceled(): break info.clear_cache() progress.close() return fixcount