Shoutcast playlist script update
#1
new version of the original shoutcast_update.py script by nei (downloads playlists from shoutcast site for specified genres and bitrates and stores them in f:\music\webradio\genrename\Wink

# fixes / enhancments by belle:
# -(phew a lot)-
#
# -option to show (or not show) the index number in the filename
# -index number always formatted as "###"
# -max downloaded stations is now number of stations per genre instead of overall
# -download/parse all pages from selected genre instead of only the 1st page
# -retry playlist download 3 times if something is wrong with the connection
# -get the stationname from the shoutcast page instead of from the playlist file (saves downloads!Wink
# -fixed parsing of bitrate
# -get number of listeners
#
# -enhanced download/update/clean mechanism!! (see options below)
# - option to only add new files / or overwrite all
# - option to don't get / delete stations with 0 listeners (probably bad station)
# - option to delete existing stations (playlists) that are no longer listed on shoutcast
# - continue cleaning up also if playlist download limit reached (show error at end of script)
# - eh....? other fixes....
#
# -other fixes

copy & paste code below to new textfile (e.g. shoutcastplsdownloader.py) and copy file to your xbmc scripts directory

limit the number of requests to the shoutcast site, or you will get banned for a while

enjoy!

belle -=desire pc=-

Quote:# v1.1
#shoutcast .pls updater for xbmc. use at your own risk!
#edit config part in script, copy script to $home\script\ directory,
#start and enjoy.
#keep in mind, that updating too frequently or with too many
#genres and/or stations will "bann" you from shoutcast for a while.
#feel free to edit, bugfix, enhance and so on... :-)
#initial version by nei
#modified / enhanced by belle -=desirepc=- not my best coding, maybe fix that sometimes, maybe not :)


# known issues:
#  -none for the moment;-)

# fixes / enhancments by belle:
# -(phew a lot)-
#
# -option to show (or not show) the index number in the filename
# -index number always formatted as "###"
# -max downloaded stations is now number of stations per genre instead of overall
# -download/parse all pages from selected genre instead of only the 1st page
# -retry playlist download 3 times if something is wrong with the connection
# -get the stationname from the shoutcast page instead of from the playlist file (saves downloads!)
# -fixed parsing of bitrate
# -get number of listeners
#
# -enhanced download/update/clean mechanism!! (see options below)
#    - option to only add new files / or overwrite all
#    - option to don't get / delete stations with 0 listeners (probably bad station)
#    - option to delete existing stations (playlists) that are no longer listed on shoutcast
#    - continue cleaning up also if playlist download limit reached (show error at end of script)
#    - eh....? other fixes....
#
# -other fixes :)
#
# change your genres list in the config below... be advised that shoutcast only allows a limited number of downloads
# of playlists (genre page views are not counted) and "bans" you for a while if you get too many playlists at once.
# so limit your genrelist or set max_number_of_stations to a low value!
#
# belle


####################### config ########################

#directory where you want to store your radio stations
shoutcast_dir = 'f:\\music\\webradio\\'

#genres
genres = ['house', 'dance', 'techno', 'trance', 'electronic', 'old school', 'hardcore', 'downtempo', 'ambient', 'acid jazz', 'breakbeat', 'drum and bass', 'reggae']

#minimum bitrates for stations (0 -> all)
min_bitrate = 128

#maximum bitrate for stations (999 -> all)
max_bitrate = 384

#(0 or 1) to enable or disable the bitrate in the filename
show_bitrate_in_filename = 1

#(0 or 1) to enable or disable the stationindex in the filename
show_index_in_filename = 0

#number of stations to retrieve per genre max (999).
max_number_of_stations = 999

#(0 or 1) to enable or disable clean of old (no longer listed) stations
allow_clean = 1

#(0 or 1) to enable or disable clean of stations with 0 listeners
allow_clean_emptystations = 1

#(0 or 1) only add (new) nonexisting stations (keep previous)
only_add_stations = 1

#######################################################


#do not edit from here on (if you don't know what you're doing, of course;-)
import xbmc, xbmcgui
import urllib2, os, sys, traceback, shutil
from os import makedirs
from os.path import normpath,dirname,exists,abspath,join

xfs_file_length = 42

shoutcast_genre_path = 'http://www.shoutcast.com/directory/?sgenre=%s&numresult=100&startat=%s'

#chars accepted for filenames on xfs
legal_chars = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz1234567890!#$%&'()-@[]^_`{}~."

#class for storing pls stuff.
class plsdescriptor:
  #converts the line with the filename.pls in an url
  def (self, fileline, bitrate, stationname, listeners):
      startindex = fileline.find('/sbin')
      endindex = fileline.find('filename.pls') + 11
      self.fileurl = 'http://www.shoutcast.com' + strip(fileline, startindex, endindex)
      self.bitrate = bitrate
      self.stationname = stationname
      self.listeners = listeners
 
  #returns the url to be used for getting the pls file content.
  def get_fileurl(self):
      return self.fileurl
     
  #returns the bitrate for this plsdescriptor.
  def get_bitrate(self):
      return self.bitrate

  #returns the listeners for this plsdescriptor.
  def get_listeners(self):
      return self.listeners

  #returns the stationname for this plsdescriptor.
  def get_stationname(self):
      return self.stationname

# removes all chars at the end, that are loner than length.
# stupid to implement this myself, but couldn't find an
# approriate function for it...
def strip(stringtostrip, startindex, endindex):
  if endindex >= len(stringtostrip):
      endindex = len(stringtostrip) - 1
  strippedstring = ''
  i = startindex
  while i <= endindex:
      strippedstring = strippedstring + stringtostrip[i]
      i += 1    
  return strippedstring.rstrip() #remove newline character (and any ending whitespaces)

#substitutes all illegal chars for xfs with ' '
def subst_illegal_chars(stringtocheck):
  for char in stringtocheck:
      if legal_chars.find(char) == -1:
          stringtocheck = stringtocheck.replace(char, ' ')
  return stringtocheck

def get_filename(stationname, bitrate):
  filename = subst_illegal_chars(stationname)
  #remove double spaces
  filename = filename.replace('  ', ' ')
  filename = filename.replace('  ', ' ')
  filename = filename.rstrip()
  filename = filename.lstrip()
 
  indexlength = 0
  if show_index_in_filename == 1:
      # -4 -> i.e.: "019 "
      indexlength = 4
  else:
      indexlength = 0

  if show_bitrate_in_filename == 1:
      # -4 -> ".pls", -indexlength, -5 -> " 999k" -1 -> index
      filename = strip(filename, 0, xfs_file_length - 4 - indexlength - 5 - 1) + ' %sk.pls' % bitrate
  else:
      # -4 -> ".pls", -indexlength, -1 -> index
      filename = strip(filename, 0, xfs_file_length - 4 - indexlength - 1) + '.pls'
  return filename

#converts non http chars to them.
#enhance if necessary.
def get_genre_url(genre):
  genreurl = genre.replace(' ', '\%20')
  genreurl = genreurl.replace('&', '\%26')
  return genreurl


#creates missing directories for the given path
def make_path(path):
  dpath = normpath(dirname(path))
  if not exists(dpath): makedirs(dpath)
  return normpath(abspath(path))


# saves the content under the given path. (i.e. f:\music\shoutcast\blabla.pls)
def save_file(dir, filename, content):
  dir = make_path(dir)
  file = open(dir + '\\' + filename, 'w')
  file.writelines(content)
  file.close


#just strips the line at the right indices. line will always look something like this:
#      <td nowrap align="center" bgcolor="#001e5a"><font face="arial, helvetica" size="2" color="#ffffff">128</font></td>
#returns an int representing the bitrate or -1, if conversion failed
def get_bitrate(bitrateline):
  bitrate = -1
  bitrateend = bitrateline.rfind("</font>")
  if bitrateend != -1:
      bitratestart = bitrateline.rfind('">', 0, bitrateend)
      if bitratestart != -1:
          try:
              bitratestring = strip(bitrateline, bitratestart + 2, bitrateend - 1)
              bitratestring = bitratestring.lstrip()
              bitratestring = bitratestring.rstrip()
              bitrate = int(bitratestring)
          except:
              print "bitrate not found: \n" + bitrateline
  return bitrate

#just strips the line at the right indices. line will always look something like this:
#      <td nowrap align="center" bgcolor="#001e32"><font face="arial, helvetica" size="2" color="#ffffff">1800/117841</font></td>
#returns an int representing the number of listeners or -1, if conversion failed
def get_listeners(listenerline):
  listeners = -1
  listenerend = listenerline.rfind("</font>")
  if listenerend != -1:
      listenerend = listenerline.rfind('/', 0, listenerend)
      if listenerend != -1:
          listenerstart = listenerline.rfind('">', 0, listenerend)
          if listenerstart != -1:
              try:
                  listenerstring = strip(listenerline, listenerstart + 2, listenerend - 1)
                  listenerstring = listenerstring.lstrip()
                  listenerstring = listenerstring.rstrip()
                  listeners = int(listenerstring)
              except:
                  print "listeners not found: \n" + listeners
  return listeners

#just strips the line at the right indices. line will always look something like this:
#      <td width="100%" align="left" bgcolor="#001e5a"><font face="arial" size="2" color="#ffffff"><font size="1"><b>[dance]</font></b> <a id="listlinks" target="_scurl" href="http://www.shoutcast.com">http://www.dancefm.biz</a><br>
#returns a string containing the stationname or "", if conversion failed
def get_stationname(stationline):
  stationname = ''
  stationend = stationline.rfind("</a>")
  if stationend != -1:
      stationstart = stationline.rfind('">', 0, stationend)
      if stationstart != -1:
          try:
              stationname = strip(stationline, stationstart + 2, stationend - 1)
              stationname = stationname.lstrip()
              stationline = stationname.rstrip()
          except:
              print "stationname not found: \n" + stationline
  return stationline

# gets the stations out of the html page by looking for a 'filename.pls' string.
def get_stations(htmlcontent):
  stations = []
  for line in htmlcontent:
      if line.find('filename.pls') != -1:
          #always six or seven lines later its the html content with the bitrate
          #two lines before the bitrate is the number of current listeners
          #first try line six
          bitrate = get_bitrate(htmlcontent[htmlcontent.index(line) + 6])
          listeners = get_listeners(htmlcontent[htmlcontent.index(line) + 4])
          if bitrate == -1:
              #then try line seven
              bitrate = get_bitrate(htmlcontent[htmlcontent.index(line) + 7])
              listeners = get_listeners(htmlcontent[htmlcontent.index(line) + 5])                
          if bitrate == -1:
              print "parsing of bitrate failed\n"
          if min_bitrate <= bitrate <= max_bitrate:
              stationname = get_stationname(htmlcontent[htmlcontent.index(line) + 2])
              stations.append(plsdescriptor(line, bitrate, stationname, listeners))
  return stations

#delete files in directory root
def clean_dir(dirname):
  for filename in os.listdir(dirname):
      if os.path.isfile(dirname + filename):
          os.remove(dirname + filename)

#copy files in directory root (keeps filedates)
def copy_dir(sourcedirname, targetdirname):
  for filename in os.listdir(sourcedirname):
      if os.path.isfile(sourcedirname + filename):
          shutil.copy2(sourcedirname + filename, targetdirname + filename)

#dialog asking user if he wants to start the update
def start_update():
  dialog = xbmcgui.dialog()
  title = "shoutcast update"
  question =  "do you want to update %s?" % shoutcast_dir
  config1 = "number of stations/genre: %s, bitrate: %sk - %sk" % (max_number_of_stations, min_bitrate, max_bitrate)

  config2 = ""
  if show_bitrate_in_filename == 1:
      config2 = "show bitrate"

  if show_index_in_filename == 1:
      if config2 == "":
          config2 = "show index"
      else:
          config2 += " and index"
         
  config2 +=  " in filename\n"

  config3 = ""
  if allow_clean == 1:
      config3 = "removing unlisted"

  if allow_clean_emptystations == 1:
      if config3 == "":
         config3 = "removing zero listener"
      else:
         config3 += " and zero listener"

  config3 += " stations\n"

  if only_add_stations == 1:
      config3 += "adding only new stations (keeping old files)"

  config2 = config2 + config3

  selected = dialog.yesno(title, question, config1, config2)
  return selected

##### main() #####
if start_update():
  try:
      dialog = xbmcgui.dialogprogress()
      genreindex = 1
      cancelrequests = 0
      for genre in genres:
          pagestart = 0
          pagescontent = []
          maxpage = 1
          genrestatus = 'updating ' + genre + ' (%s/%s)' % (genreindex, len(genres))
          dialog.create('shoutcast update', genrestatus + "\ngetting station list...")
          dialog.update(0)
          genrepage = urllib2.urlopen(shoutcast_genre_path % (get_genre_url(genre), pagestart))
          pagescontent = genrepage.readlines()
          genrepage.close()      
         
          for line in pagescontent:
              maxpagestart = line.find("page ")
              if maxpagestart != -1:
                  maxpageend = line.find("&nbsp;", maxpagestart)
                  if maxpageend != -1:
                      maxpagestart = line.find("of", maxpagestart, maxpageend)
                      try:
                          maxpagestring = strip(line, maxpagestart + 3, maxpageend - 1)
                          maxpagestring = maxpagestring.lstrip()
                          maxpagestring = maxpagestring.rstrip()
                          maxpage = int(maxpagestring)
                          break
                      except:
                          maxpage = 1
                          print "maxpage not found: \n"
                          break

          pagestart = 1
          dialog.update((pagestart * 100)/ maxpage)                            
          if dialog.iscanceled():
              break
         
          while pagestart < maxpage:
              genrepage = urllib2.urlopen(shoutcast_genre_path % (get_genre_url(genre), (pagestart * 100)))
              pagescontent.extend(genrepage.readlines())
              genrepage.close()
              pagestart += 1
              dialog.update((pagestart * 100)/ maxpage)                            
              if dialog.iscanceled():
                  break

          if dialog.iscanceled():
              break
         
          stations = get_stations(pagescontent)
          pagescontent = []
          directory = shoutcast_dir + subst_illegal_chars(genre) + '\\'
          tempdirectory = shoutcast_dir + 'temp\\'
          numberofstations = len(stations)
          stationindex = 1
         
          if exists(tempdirectory):
              clean_dir(tempdirectory)
          else:
              dir = make_path(tempdirectory)
             
          if allow_clean == 1:
              dialog.close()
              dialog.create('shoutcast update', genrestatus + "\ncleaning no longer listed stations...")
              dialog.update(0)
         
              for station in stations:
                  if dialog.iscanceled():
                      break
                  dialog.update((stationindex * 100)/ numberofstations)
             
                  if show_index_in_filename == 1:
                      filename = '%03d ' + get_filename(station.get_stationname(), station.get_bitrate())
                      filename = filename % stationindex
                  else:
                      filename = get_filename(station.get_stationname(), station.get_bitrate())
                     
                  if exists(directory + filename):
                      shutil.copy2(directory + filename, tempdirectory + filename)
                 
                  stationindex += 1
                 
              if dialog.iscanceled():
                  break
                 
              if exists(directory):
                  clean_dir(directory)
              else:
                  dir = make_path(directory)
              copy_dir(tempdirectory, directory)
         
          stationindex = 1
         
          if exists(tempdirectory):
              clean_dir(tempdirectory)
          else:
              dir = make_path(tempdirectory)
             
          if allow_clean_emptystations == 1:
              dialog.close()
              dialog.create('shoutcast update', genrestatus + "\ncleaning stations with 0 listeners...")
              dialog.update(0)
         
              for station in stations:
                  if dialog.iscanceled():
                      break
                  dialog.update((stationindex * 100)/ numberofstations)

                  if station.get_listeners() > 0:
                      if show_index_in_filename == 1:
                          filename = '%03d ' + get_filename(station.get_stationname(), station.get_bitrate())
                          filename = filename % stationindex
                      else:
                          filename = get_filename(station.get_stationname(), station.get_bitrate())
                     
                      if exists(directory + filename):
                          shutil.copy2(directory + filename, tempdirectory + filename)
                 
                  stationindex += 1
                 
              if dialog.iscanceled():
                  break
                 
              if exists(directory):
                  clean_dir(directory)
              else:
                  dir = make_path(directory)
              copy_dir(tempdirectory, directory)
         
          dialog.close()
          dialog.create('shoutcast update', genrestatus + "\nprocessing stations...")
          dialog.update(0)
         
          stationindex = 1
          requestedplaylists = 0

          if exists(tempdirectory):
              clean_dir(tempdirectory)
         
          for station in stations:
              if dialog.iscanceled():
                  break
              dialog.update((stationindex * 100)/ numberofstations)

              if cancelrequests == 1:
                  break

              if requestedplaylists >= max_number_of_stations:
                  break
             
              i = 0
              if show_index_in_filename == 1:
                  filename = '%03d ' + get_filename(station.get_stationname(), station.get_bitrate())
                  filename = filename % stationindex
              else:
                  filename = get_filename(station.get_stationname(), station.get_bitrate())
              while i < 3:
                  try:
                      if only_add_stations == 1:
                          if not exists(directory + filename):
                              plsfile = urllib2.urlopen(station.get_fileurl())
                              plsfilecontent = plsfile.readlines()
                              plsfile.close()

                              if (plsfilecontent[0].find('[playlist]') == -1):
                                  cancelrequests = 1
                                  break

                              if (plsfilecontent[0].find('numberofentries=0') == -1):
                                  if len(plsfilecontent) > 1:
                                      if (plsfilecontent[1].find('numberofentries=0') == -1):
                                          if allow_clean_emptystations == 1:
                                              if station.get_listeners() > 0:
                                                  save_file(directory, filename, plsfilecontent)
                                                  requestedplaylists += 1
                                          else:
                                              save_file(directory, filename, plsfilecontent)
                                              requestedplaylists += 1
                                  else:
                                      if allow_clean_emptystations == 1:
                                          if station.get_listeners() > 0:
                                              save_file(directory, filename, plsfilecontent)
                                              requestedplaylists += 1
                                      else:
                                          save_file(directory, filename, plsfilecontent)
                                          requestedplaylists += 1
                      else:
                          plsfile = urllib2.urlopen(station.get_fileurl())
                          plsfilecontent = plsfile.readlines()
                          plsfile.close()

                          if (plsfilecontent[0].find('[playlist]') == -1):
                              cancelrequests = 1
                              break

                          if (plsfilecontent[0].find('numberofentries=0') == -1):
                              if len(plsfilecontent) > 1:
                                  if (plsfilecontent[1].find('numberofentries=0') == -1):
                                      if allow_clean_emptystations == 1:
                                          if station.get_listeners() > 0:
                                              save_file(directory, filename, plsfilecontent)
                                              requestedplaylists += 1
                                      else:
                                          save_file(directory, filename, plsfilecontent)
                                          requestedplaylists += 1
                              else:
                                  if allow_clean_emptystations == 1:
                                      if station.get_listeners() > 0:
                                          save_file(directory, filename, plsfilecontent)
                                          requestedplaylists += 1
                                  else:
                                      save_file(directory, filename, plsfilecontent)
                                      requestedplaylists += 1
                     
                  except urllib2.httperror, error:
                      try:
                          print "parsing of playlist file failed: %s\n%s\n" % (error, station.get_fileurl())
                          plsfile.close()
                      except urllib2.httperror, error:
                          print "urllib2.close() failed: %s\n%s\n" % (error, station.get_fileurl())
                  else:
                      i = 3
                  i += 1
              stationindex += 1
          if dialog.iscanceled():
              break
          dialog.close()
          stations = []
          genreindex += 1

      if exists(shoutcast_dir + 'temp\\'):
          clean_dir(shoutcast_dir + 'temp\\')
          os.removedirs(shoutcast_dir + 'temp\\')
         
      if dialog.iscanceled():
          dialog.close()

      if cancelrequests == 1:
          dialog = xbmcgui.dialog()
          result = dialog.ok("shoutcast update partially completed", "too many requests, try again in a few hours...")

  except exception, error:
      if exists(shoutcast_dir + 'temp\\'):
          clean_dir(shoutcast_dir + 'temp\\')
          os.removedirs(shoutcast_dir + 'temp\\')
         
      dialog.close()
      pagescontent = []
      stations = []
      print "%s\n" % error
      dialog = xbmcgui.dialog()
      result = dialog.ok("shoutcast update aborted", "%s\n" % error)
Reply
#2
hmmmm, the forum doesn't let me post certain chars, so the script is not ok....
download the working file here
Reply

Logout Mark Read Team Forum Stats Members Help
Shoutcast playlist script update0