import os import xbmcvfs from abc import ABCMeta from lib.libs import mediatypes from lib.libs.addonsettings import settings from lib.libs.mediainfo import arttype_matches_base, format_arttype, find_central_infodir from lib.libs.utils import SortedDisplay, natural_sort, get_movie_path_list, get_pathsep, \ iter_possible_cleannames, parent_dir ARTWORK_EXTS = ('.jpg', '.png', '.gif') ARTIST_INFOFOLDER_PROVIDER = SortedDisplay('file:art', 20223) ARTTYPE_MAXLENGTH = 30 class ArtFilesAbstractProvider(object): __metaclass__ = ABCMeta # 13514 = Local art name = SortedDisplay('file:art', 13514) def buildimage(self, url, title, fromartistfolder=False): provider = ARTIST_INFOFOLDER_PROVIDER if fromartistfolder else self.name result = {'url': url, 'provider': provider, 'preview': url} result['title'] = title result['rating'] = SortedDisplay(0, '') result['size'] = SortedDisplay(0, '') result['language'] = 'xx' return result def getextra(self, path, exacttypes, thumbs=False): arttype = 'thumb' if thumbs else 'fanart' extradir = 'extrathumbs' if thumbs else 'extrafanart' sep = get_pathsep(path) missing, nextno = getopentypes(exacttypes, arttype) path += extradir + sep _, files = xbmcvfs.listdir(path) files.sort(key=natural_sort) result = {} for filename in files: check_filename = filename.lower() if not check_filename.endswith(ARTWORK_EXTS): continue popped = missing.pop(0) if missing else None nexttype = popped if popped else format_arttype(arttype, nextno) result[nexttype] = self.buildimage(path + filename, extradir + sep + filename) if not popped: nextno += 1 return result class ArtFilesSeriesProvider(ArtFilesAbstractProvider): mediatype = mediatypes.TVSHOW alttypes = {'logo': 'clearlogo', 'character': 'characterart'} def get_exact_images(self, mediaitem): path = mediaitem.file dirs, files = xbmcvfs.listdir(path) files.sort(key=natural_sort) result = {} for filename in files: check_filename = filename.lower() if not check_filename.endswith(ARTWORK_EXTS): continue basefile = os.path.splitext(check_filename)[0] if '.' in basefile or ' ' in basefile: continue if basefile.startswith('season') and basefile.count('-'): seasonsplit = basefile.split('-') if len(seasonsplit) == 3: if seasonsplit[1] not in ('specials', 'all'): continue number = 0 if seasonsplit[1] == 'specials' else -1 arttype = seasonsplit[2] elif len(seasonsplit) == 2: try: number = int(seasonsplit[0].replace('season', '')) except ValueError: continue arttype = seasonsplit[1] else: continue if not arttype.isalnum(): continue arttype = 'season.{0}.{1}'.format(number, arttype) else: if not basefile.isalnum() or len(basefile) > ARTTYPE_MAXLENGTH: continue arttype = basefile if settings.identify_alternatives and arttype in self.alttypes.keys(): arttype = self.alttypes[arttype] if arttype in result.keys(): continue result[arttype] = self.buildimage(path + filename, filename) if dirs and 'extrafanart' in dirs: result.update(self.getextra(path, result.keys())) return result class ArtFilesMovieProvider(ArtFilesAbstractProvider): mediatype = mediatypes.MOVIE alttypes = {'logo': 'clearlogo', 'disc': 'discart'} def get_exact_images(self, mediaitem): path = mediaitem.file paths = get_movie_path_list(path) result = {} sep = get_pathsep(path) path = os.path.dirname(paths[0]) + sep havespecific = [] for dirname, moviefile in (os.path.split(p) for p in paths): dirname += sep check_moviebase = os.path.splitext(moviefile)[0].lower() dirs, files = xbmcvfs.listdir(dirname) for filename in files: check_filename = filename.lower() if not check_filename.endswith(ARTWORK_EXTS): continue imagefile = os.path.splitext(check_filename)[0] specific = False if '-' in imagefile: firstbit, imagefile = imagefile.rsplit('-', 1) if firstbit != check_moviebase: continue specific = True if not imagefile.isalnum() or len(imagefile) > ARTTYPE_MAXLENGTH: continue arttype = imagefile if settings.identify_alternatives and arttype in self.alttypes.keys(): arttype = self.alttypes[arttype] if arttype in result.keys(): continue if specific or arttype not in havespecific: result[arttype] = self.buildimage(dirname + filename, filename) if specific: havespecific.append(arttype) if dirs: if 'extrafanart' in dirs: result.update(self.getextra(path, result.keys())) if 'extrathumbs' in dirs: result.update(self.getextra(path, result.keys(), True)) return result class ArtFilesMovieSetProvider(ArtFilesAbstractProvider): mediatype = mediatypes.MOVIESET alttypes = {'logo': 'clearlogo', 'folder': 'thumb'} def get_exact_images(self, mediaitem): path, inputfilename = os.path.split(mediaitem.file) sep = get_pathsep(path) path += sep dirs, files = xbmcvfs.listdir(path) check_inputbase = os.path.splitext(inputfilename)[0] if inputfilename else '' result = {} if inputfilename: dirname = next((name for name in dirs if name in iter_possible_cleannames(check_inputbase)), None) if dirname: # '[centraldir]/[set name]/[arttype].[ext]' dirname = path + dirname + sep _, dfiles = xbmcvfs.listdir(dirname) for filename in dfiles: if not filename.endswith(ARTWORK_EXTS): continue arttype = os.path.splitext(filename)[0] if not arttype.isalnum() or len(arttype) > ARTTYPE_MAXLENGTH: continue if settings.identify_alternatives and arttype in self.alttypes.keys(): arttype = self.alttypes[arttype] if arttype in result.keys(): continue result[arttype] = self.buildimage(dirname + filename, filename) if not result: for filename in files: if not filename.endswith(ARTWORK_EXTS): continue basefile = os.path.splitext(filename)[0] if check_inputbase: # '[centraldir]/[set name]-[arttype].[ext]' if '-' not in basefile: continue firstbit, arttype = basefile.rsplit('-', 1) if not arttype.isalnum() or firstbit not in iter_possible_cleannames(check_inputbase): continue else: # parent of movie directory if not basefile.isalnum() or len(basefile) > ARTTYPE_MAXLENGTH: continue arttype = basefile if settings.identify_alternatives and arttype in self.alttypes.keys(): arttype = self.alttypes[arttype] if arttype in result.keys(): continue result[arttype] = self.buildimage(path + filename, filename) return result class ArtFilesEpisodeProvider(ArtFilesAbstractProvider): mediatype = mediatypes.EPISODE def get_exact_images(self, mediaitem): path, inputfilename = os.path.split(mediaitem.file) path += get_pathsep(path) _, files = xbmcvfs.listdir(path) check_inputbase = os.path.splitext(inputfilename)[0].lower() result = {} for filename in files: check_filename = filename.lower() if not check_filename.endswith(ARTWORK_EXTS): continue basefile = os.path.splitext(check_filename)[0] if '-' not in basefile: continue firstbit, arttype = basefile.rsplit('-', 1) if firstbit != check_inputbase or not arttype.isalnum(): continue result[arttype] = self.buildimage(path + filename, filename) return result class ArtFilesMusicVideoProvider(ArtFilesAbstractProvider): mediatype = mediatypes.MUSICVIDEO alttypes = {'logo': 'clearlogo', 'disc': 'discart', 'cdart': 'discart'} def get_exact_images(self, mediaitem): path, inputfilename = os.path.split(mediaitem.file) path += get_pathsep(path) dirs, files = xbmcvfs.listdir(path) check_inputbase = os.path.splitext(inputfilename)[0].lower() paths = get_movie_path_list(path) result = {} sep = get_pathsep(path) path = os.path.dirname(paths[0]) + sep havespecific = [] for filename in files: check_filename = filename.lower() if not check_filename.endswith(ARTWORK_EXTS): continue basefile = os.path.splitext(check_filename)[0] specific = False if '-' in basefile: firstbit, basefile = basefile.rsplit('-', 1) if firstbit != check_inputbase: continue specific = True if not basefile.isalnum() or len(basefile) > ARTTYPE_MAXLENGTH: continue arttype = basefile if settings.identify_alternatives and arttype in self.alttypes.keys(): arttype = self.alttypes[arttype] if arttype in result.keys(): continue if specific or arttype not in havespecific: result[arttype] = self.buildimage(path + filename, filename) if specific: havespecific.append(arttype) if dirs: if 'extrafanart' in dirs: result.update(self.getextra(path, result.keys())) if 'extrathumbs' in dirs: result.update(self.getextra(path, result.keys(), True)) return result class ArtFilesArtistProvider(ArtFilesAbstractProvider): mediatype = mediatypes.ARTIST alttypes = {'folder': 'thumb', 'logo': 'clearlogo'} def get_exact_images(self, mediaitem): path = find_central_infodir(mediaitem) if not path: return {} _, files = xbmcvfs.listdir(path) result = {} for filename in files: check_filename = filename.lower() if not check_filename.endswith(ARTWORK_EXTS): continue arttype = os.path.splitext(check_filename)[0] if not arttype.isalnum() or len(arttype) > ARTTYPE_MAXLENGTH: continue if settings.identify_alternatives and arttype in self.alttypes.keys(): arttype = self.alttypes[arttype] if arttype in result.keys(): continue result[arttype] = self.buildimage(path + filename, filename, True) return result class ArtFilesAlbumProvider(ArtFilesArtistProvider): mediatype = mediatypes.ALBUM alttypes = {'folder': 'thumb', 'cover': 'thumb', 'disc': 'discart', 'cdart': 'discart'} def get_exact_images(self, mediaitem): paths = (mediaitem.file, find_central_infodir(mediaitem)) if settings.albumartwithmediafiles: paths = (paths[1], paths[0]) result = {} for path in paths: if not path: continue _, files = xbmcvfs.listdir(path) for filename in files: check_filename = filename.lower() if not check_filename.endswith(ARTWORK_EXTS): continue arttype = os.path.splitext(check_filename)[0] if not arttype.isalnum() or len(arttype) > ARTTYPE_MAXLENGTH: continue if settings.identify_alternatives and arttype in self.alttypes.keys(): arttype = self.alttypes[arttype] if arttype in result.keys(): continue result[arttype] = self.buildimage(path + filename, filename, path != mediaitem.file) for disc in sorted(mediaitem.discfolders.keys()): path = mediaitem.discfolders[disc] _, files = xbmcvfs.listdir(path) for filename in files: check_filename = filename.lower() if not check_filename.endswith(ARTWORK_EXTS): continue arttype = os.path.splitext(check_filename)[0] if not arttype.isalnum() or len(arttype) > ARTTYPE_MAXLENGTH: continue if settings.identify_alternatives and arttype in self.alttypes.keys(): arttype = self.alttypes[arttype] parentdir = parent_dir(path) + get_pathsep(path) if arttype == 'discart': if 'discart' not in result: result['discart'] = self.buildimage(path + filename, parentdir + filename) if not disc: continue arttype += str(disc) if arttype in result: continue result[arttype] = self.buildimage(path + filename, parentdir + filename) return result class ArtFilesSongProvider(ArtFilesAbstractProvider): mediatype = mediatypes.SONG def get_exact_images(self, mediaitem): paths = (mediaitem.file, find_central_infodir(mediaitem)) if not paths[0] and not paths[1]: return {} if settings.albumartwithmediafiles: paths = (paths[1], paths[0]) if mediaitem.file: check_inputbase = os.path.splitext(os.path.basename(mediaitem.file))[0].lower() result = {} for path in paths: if not path: continue centraldir = path != mediaitem.file path = os.path.dirname(path) + get_pathsep(path) _, files = xbmcvfs.listdir(path) for filename in files: check_filename = filename.lower() if not check_filename.endswith(ARTWORK_EXTS): continue basefile = os.path.splitext(check_filename)[0] if '-' not in basefile: continue firstbit, arttype = basefile.rsplit('-', 1) if not arttype.isalnum(): continue if not centraldir and firstbit != check_inputbase: continue if centraldir and firstbit not in (i.lower() for i in iter_possible_cleannames(mediaitem.label)): continue result[arttype] = self.buildimage(path + filename, filename, centraldir) return result def getopentypes(existingtypes, arttype): keys = [exact for exact in sorted(existingtypes, key=natural_sort) if arttype_matches_base(arttype, exact)] missing = [] nextstart = 0 for count in xrange(100): if not keys: nextstart = count break exact = format_arttype(arttype, count) if exact in keys: keys.remove(exact) else: missing.append(exact) return missing, nextstart