text: from ioUtils import getFile
  from fsUtils import isFile
  from webUtils import getHTML, isBS4
  from strUtils import fixName
  from math import ceil, floor
  from hashlib import md5
  
  from dbBase import dbBase
  
  class artistMSIDClass:
      def __init__(self, ID=None, err=None):
          self.ID=ID
          self.err=err
          
      def get(self):
          return self.__dict__
      
              
  class artistMSURLClass:
      def __init__(self, url=None, err=None):
          self.url = url
          self.err = err
          
      def get(self):
          return self.__dict__
          
          
  class artistMSNameClass:
      def __init__(self, name=None, err=None):
          self.name = name
          self.err  = err
          
      def get(self):
          return self.__dict__
      
  
  class artistMSMediaClass:
      def __init__(self, err=None):
          self.media = {}
          self.err   = err
          
      def get(self):
          return self.__dict__
      
  
  class artistMSMediaDataClass:
      def __init__(self, album=None, url=None, aclass=None, aformat=None, artist=None, code=None, year=None, err=None):
          self.album   = album
          self.url     = url
          self.aclass  = aclass
          self.aformat = aformat
          self.artist  = artist
          self.code    = code
          self.year    = year
          self.err     = err
          
      def get(self):
          return self.__dict__
      
  
  class artistMSMediaAlbumClass:
      def __init__(self, url=None, album=None, aformat=None, err=None):
          self.url     = url
          self.album   = album
          self.aformat = aformat
          self.err     = err        
          
      def get(self):
          return self.__dict__
  
      
  class artistMSMediaCountsClass:
      def __init__(self, err=None):
          self.counts = {}
          self.err    = err
          
      def get(self):
          return self.__dict__
      
  
  class artistMSPageClass:
      def __init__(self, ppp = None, tot = None, more=None, redo=None, err=None):
          self.ppp   = ppp
          self.tot   = tot
          if isinstance(ppp, int) and isinstance(tot, int):
              self.pages = int(ceil(tot/ppp))
          else:
              self.pages = None
  
          self.err   = err
  
          self.more  = more
          self.redo  = redo
          
      def get(self):
          return self.__dict__
      
  
  class artistMSProfileClass:
      def __init__(self, profile=None, aliases=None, members=None, sites=None, groups=None, variations=None, err=None):
          self.profile    = profile
          self.aliases    = aliases
          self.members    = members
          self.sites      = sites
          self.groups     = groups
          self.variations = variations
          self.err        = err
          
      def get(self):
          return self.__dict__
      
  
  class artistMSURLInfo:
      def __init__(self, name=None, url=None, ID=None, err=None):
          self.name = name
          self.url  = url
          self.ID   = ID
          self.err  = err
          
      def get(self):
          return self.__dict__
          
  
  class artistMSDataClass:
      def __init__(self, artist=None, url=None, ID=None, pages=None, profile=None, media=None, mediaCounts=None, err=None):
          self.artist      = artist
          self.url         = url
          self.ID          = ID
          self.pages       = pages
          self.profile     = profile
          self.media       = media
          self.mediaCounts = mediaCounts
          self.err         = err
          
      def get(self):
          return self.__dict__
  
  
          
  class artistMS(dbBase):
      def __init__(self, debug=False):
          self.debug = debug
          self.data  = None
          
      def getData(self, inputdata):
          if isinstance(inputdata, str):
              if isFile(inputdata):
                  try:
                      bsdata = getHTML(getFile(inputdata))
                  except:
                      try:
                          bsdata = getHTML(getFile(inputdata, version=2))
                      except:
                          raise ValueError("Cannot read artist file: {0}".format(inputdata))
              else:
                  try:
                      bsdata = getHTML(inputdata)
                  except:
                      raise ValueError("Not sure about string input: {0} . It is not a file".format(inputdata))
          elif isBS4(inputdata):
              bsdata = inputdata
              pass
          else:
              raise ValueError("Not sure about input type: {0}".format(type(inputdata)))
  
          self.bsdata = bsdata
          
          return self.parse()
          
          
      def show(self):
          print("MusicStack Artist Data Class")
          print("-------------------------")
          print("Artist: {0}".format(self.artist.name))
          print("URL:    {0}".format(self.url.url))
          print("ID:     {0}".format(self.ID.ID))
          print("Pages:  {0}".format(self.pages.get()))
          print("Media:  {0}".format(self.mediaCounts.get()))
          for mediaType,mediaTypeAlbums in self.media.media.items():
              print("   {0}".format(mediaType))
              for album in mediaTypeAlbums:
                  print("      {0}".format(album.album))     
          
      def get(self):
          return self.__dict__
          
          
              
          
      def getNamesAndURLs(self, content):
          data = []
          if content is not None:
              for ref in content.findAll("a"):
                  url    = ref.attrs['href']
                  name   = ref.text
  
                  ID = None
                  data.append(artistMSURLInfo(name=name, url=url, ID=ID))
          return data
  
  
  
  
  
      #######################################################################################################################################
      ## Artist URL
      #######################################################################################################################################
      def getartistMSURL(self):
          auc = artistMSURLClass(url=None, err=None)
          return auc
  
      
  
      #######################################################################################################################################
      ## Artist ID
      #######################################################################################################################################
      def getartistMSDiscID(self):
          aic = artistMSIDClass(ID=self.data["ID"], err=None)
          return aic
      
      
  
      #######################################################################################################################################
      ## Artist Name
      #######################################################################################################################################
      def getartistMSName(self):
          anc = artistMSNameClass(name=self.data["Name"], err=None)
          return anc
      
      
  
      #######################################################################################################################################
      ## Artist Media
      #######################################################################################################################################
      def getartistMSMediaAlbum(self, td):
          amac = artistMSMediaAlbumClass()
          for span in td.findAll("span"):
              attrs = span.attrs
              if attrs.get("class"):
                  if 'format' in attrs["class"]:
                      albumformat = span.text
                      albumformat = albumformat.replace("(", "")
                      albumformat = albumformat.replace(")", "")
                      amac.format = albumformat
                      continue
              span.replaceWith("")
  
          ref = td.find("a")
          if ref:
              amac.url   = ref.attrs['href']
              amac.album = ref.text
          else:
              amac.err = "NoText"
  
          return amac
      
      
      def getartistMSMedia(self):
          amc  = artistMSMediaClass()
          mediaType = "MixTape"
          media = self.data["Media"]
          for album in media:
              amdc = artistMSMediaDataClass(album=album["Name"], url=album["URL"], aclass=None, aformat=None, artist=album["Artists"], code=album["Code"], year=None)
              if amc.media.get(mediaType) is None:
                  amc.media[mediaType] = []
              amc.media[mediaType].append(amdc)
          return amc
      
      
  
      #######################################################################################################################################
      ## Artist Media Counts
      #######################################################################################################################################        
      def getartistMSMediaCounts(self, media):
          
          amcc = artistMSMediaCountsClass()
          
          credittype = "Releases"
          if amcc.counts.get(credittype) == None:
              amcc.counts[credittype] = {}
          for creditsubtype in media.media.keys():
              amcc.counts[credittype][creditsubtype] = int(len(media.media[creditsubtype]))
              
          return amcc
          
      
  
      #######################################################################################################################################
      ## Artist Variations
      #######################################################################################################################################
      def getartistMSProfile(self):
          data = {}        
          apc  = artistMSProfileClass(profile=data.get("Formed"), aliases=data.get("Aliases"),
                                      members=data.get("Members"), groups=data.get("In Groups"),
                                      sites=data.get("Sites"), variations=data.get("Variations"))
          return apc
  
  
      
      #######################################################################################################################################
      ## Artist Pages
      #######################################################################################################################################
      def getartistMSPages(self):
          apc   = artistMSPageClass(ppp=1, tot=1, redo=False, more=False)
          return apc
              
          pageData = bsdata.find("div", {"class": "pagination bottom"})
          if pageData is None:
              err = "pagination bottom"
              apc = artistMSPageClass(err=err)
              return apc
          else:
              x = pageData.find("strong", {"class": "pagination_total"})
              if x is None:
                  err = "pagination_total"
                  apc = artistMSPageClass(err=err)
                  return apc
              else:
                  txt = x.text
                  txt = txt.strip()
                  txt = txt.replace("\n", "")
                  retval = [tmp.strip() for tmp in txt.split('of')]
  
                  try:
                      ppp   = int(retval[0].split('–')[-1])
                      tot   = int(retval[1].replace(",", ""))
                  except:
                      err   = "int"
                      apc   = artistMSPageClass(err=err)
                      return apc
  
                  if ppp < 500:
                      if tot < 25 or ppp == tot:
                          apc   = artistMSPageClass(ppp=ppp, tot=tot, redo=False, more=False)
                      else:
                          apc   = artistMSPageClass(ppp=ppp, tot=tot, redo=True, more=False)
                  else:
                      if tot < 500:
                          apc   = artistMSPageClass(ppp=ppp, tot=tot, redo=False, more=False)
                      else:
                          apc   = artistMSPageClass(ppp=ppp, tot=tot, redo=False, more=True)
                          
                  return apc
              
          return artistMSPageClass()
  
      
      
      def setData(self, data):
          self.data = data
  
  
      def parse(self):
          if self.data is None:
              print("Must call setData(data)")
              return
          
          artist      = self.getartistMSName()
          url         = self.getartistMSURL()
          ID          = self.getartistMSDiscID()
          pages       = self.getartistMSPages()
          profile     = self.getartistMSProfile()
          media       = self.getartistMSMedia()
          mediaCounts = self.getartistMSMediaCounts(media)
          
          err = [artist.err, url.err, ID.err, pages.err, profile.err, mediaCounts.err, media.err]
          adc = artistMSDataClass(artist=artist, url=url, ID=ID, pages=pages, profile=profile, mediaCounts=mediaCounts, media=media, err=err)
          
          return adc