some shoutcast update changes

  Thread Rating:
  • 0 Votes - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Post Reply
IndieRockSteve Offline
Senior Member
Posts: 195
Joined: Oct 2003
Reputation: 0
Post: #1
i've made a couple changes to the script, this is my first time using python, so i didn't delve far in, but what i did was cause the numbers placed at the beginning of the filenames to show a leading zero to keep them listed in order on the screen and i also had it place the bitrate right after the count number so it was easy to see what bitrate the stream was at.

if i feel like teaching myself enough about python i'll set it up so the output file name follows a user-definable string. i'll also see if i can do some better parsing to get the station name out of the retreived field and seperate it from the description, but this is obviously a more difficult algorithm to implement, so it will prbly take me some time.

anyway, here's what i changed so someone can change the cvs copy if they want...

line 138 becomes:
filename = '%sk ' % bitrate + strip(filename, 0, xfs_file_length - 4 - 3 - 5 - 1) + '.pls'

and line 237 becomes:
filename = '%02d ' + get_filename(plsfilecontent, station.get_bitrate())

so now it looks like:
01 128k somafm....
find quote
IndieRockSteve Offline
Senior Member
Posts: 195
Joined: Oct 2003
Reputation: 0
Post: #2
ok, i've decided to get it to sort by bitrate too...
i'm working on that right now and will post the change to get that to work...
find quote
IndieRockSteve Offline
Senior Member
Posts: 195
Joined: Oct 2003
Reputation: 0
Post: #3
hehe, ok, i have it sorting by bitrate now, wasn't that hard...
but, in doing so i discovered a parseing flaw in the script that prevented stations that don't have a "now playing" line from being grabbed, so i fixed the function responsible for this and now it seems to work...

here's the function i changed:
Quote:# 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:
if line.find('now playing') !=-1:
line_count = 7
else:
line_count = 6
#always seven lines later its the html content with the bitrate
bitrate = get_bitrate(htmlcontent[htmlcontent.index(line) + line_count])
if min_bitrate <= bitrate <= max_bitrate:
stations.append(plsdescriptor(line, bitrate))
if len(stations) == max_number_of_stations:
break
return stations

so this will make sure all the stations get added.

and line 79 becomes:
shoutcast_genre_path = 'http://www.shoutcast.com/directory/?sgen...y=bitrate'

this will sort the list with highest bitrate first.
find quote
IndieRockSteve Offline
Senior Member
Posts: 195
Joined: Oct 2003
Reputation: 0
Post: #4
anyone know why the dialog.yesno doesn't autosize correctly?
the dialog.create seems to autosize correctly

thanks!
find quote
Zinger Offline
Junior Member
Posts: 25
Joined: Feb 2004
Reputation: 0
Post: #5
i found this issue also.   i rewrote with regex.  no more counting lines.  grouping makes it easy to pull out the info from the html line without needing external functions to split strings.

Quote:title = re.compile('target="_scurl" href=.*>(.*)</a>')
bitrate = re.compile('>(\d+)<')
rn_number = re.compile('rn=(\d+)&file=filename.pls')

for line in htmlcontent:
# search for rn number of shoutcast playlist file
m = rn_number.search(line)
if m:
rn_number = m.group(1)
if line.find('/tr>') != -1 and rn_number != 0:
genres[genre].append([rn_number,bitrate,title])
rn_number = 0    
if rn_number != 0:  
m = title.search(line)
if m:
title = m.group(1)
m = bitrate.search(line)
if m:
bitrate = m.group(1)
find quote
IndieRockSteve Offline
Senior Member
Posts: 195
Joined: Oct 2003
Reputation: 0
Post: #6
zinger, awesome, that was the next peice of code i wanted to rewrite.

i'm gunna merge this with my test tree and test it out.
find quote
IndieRockSteve Offline
Senior Member
Posts: 195
Joined: Oct 2003
Reputation: 0
Post: #7
zinger, i get a line error about
"for line in htmlcontent"
as invalid syntax

any ideas? maybe i did something wrong in pasting it?
find quote
Zinger Offline
Junior Member
Posts: 25
Joined: Feb 2004
Reputation: 0
Post: #8
sorry, thats from a script i'm working on. not for shoutcast.py. i never fixed shoutcast_update after finding the issue. here is my modified version though. it adds the ability to specify bitrate min/max and channel number by genre. also creates pls with only one stream so xbmc will just play the stream instead of opening pls first.

Quote:#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 ~24h.
#feel free to edit, bugfix, enhance and so on... :-)
#initial version by nei

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


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

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

#genres [number of channels, minimum bitrate,maximum bitrate], leave [] empty for defaults.
genres = {'topten':[20,128,256], 'alternative':[20,128,256], 'metal':[],'rock':[],'rnb':[5,128,256],'hip hop':[4,128,256], 'comedy':[2,1,256], '70s':[10,96,256], '80s':[]}

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

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

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

#number of stations to retrieve.
max_number_of_stations = 10


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


#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
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'

#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):
startindex = fileline.find('/sbin')
endindex = fileline.find('filename.pls') + 12
self.fileurl = 'http://www.shoutcast.com' + strip(fileline, startindex, endindex)
self.bitrate = bitrate

#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


# 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


# gets the filename out of the first title in the pls file.
# removes the (#2 - 187/750) and strips it to Confused characters.
def get_filename(plsfilecontent, bitrate):
filename = ''
for line in plsfilecontent:
if line.startswith('title1='):
filename = line
break
if filename == '': #happens, if there were too many requests
raise exception('too many requests, try again tomorrow...')
splittedname = filename.split(') ', 1)
filename = splittedname[1]
if show_bitrate_in_filename == 1:
# -4 -> ".pls", -3 -> i.e.: "19 ", -5 -> 999k -1 -> index
filename = strip(filename, 0, xfs_file_length - 4 - 3 - 5 - 1) + ' %sk.pls' % bitrate
else:
# -4 -> ".pls", -3 -> i.e.: "19 ", -1 -> index
filename = strip(filename, 0, xfs_file_length - 4 - 3 - 1) + '.pls'
filename = subst_illegal_chars(filename)
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.write("[playlist]\n")
file.write("numberofentries=1\n")
for line in content:
if line.startswith('file1=') or line.startswith('title1=') or line.startswith('length1=') or line.startswith('version='):
file.write(line)
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 reprecenting the bitrate or -1, if conversion failed
def get_bitrate(bitrateline):
bitratestring = strip(bitrateline, 105, len(bitrateline) - 14)
bitrate = -1
try:
bitrate = int(bitratestring)
except:
print "parsing of bitrate failed: \n" + bitrateline
return bitrate


# gets the stations out of the html page by looking for a 'filename.pls' string.
def get_stations(htmlcontent, genre):
stations = []
for line in htmlcontent:
if line.find('filename.pls') != -1:
#always seven lines later its the html content with the bitrate
bitrate = get_bitrate(htmlcontent[htmlcontent.index(line) + 7])
if genres[genre][1] <= bitrate <= genres[genre][2]:
stations.append(plsdescriptor(line, bitrate))
if len(stations) == genres[genre][0]:
break
return stations

#delete directory
def clean_dir(dirname):
for root, dirs, files in os.walk(dirname):
for name in files:
os.remove(join(root, name))

#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 genres to update: %s" % len(genres)
selected = dialog.yesno(title, question, config1)
return selected

##### main() #####
if start_update():
try:
dialog = xbmcgui.dialogprogress()
genreindex = 1
for genre in genres.keys():
if len(genres[genre]) == 0:
genres[genre]=[max_number_of_stations,min_bitrate,max_bitrate]
genrestatus = 'updating ' + genre + ' (%s/%s) numer of stations:%s\nmin bitrate:%s max bitrate:%s' % (genreindex, len(genres),genres[genre][0],genres[genre][1],genres[genre][2])
dialog.create('shoutcast update', genrestatus + "\ngetting station list...")
clean_dir(shoutcast_dir + subst_illegal_chars(genre))
genrepage = urllib2.urlopen(shoutcast_genre_path % get_genre_url(genre))
stations = get_stations(genrepage.readlines(),genre)
genrepage.close()
dialog.close()
dialog.create('shoutcast update', genrestatus + "\nprocessing stations...")
numberofstations = len(stations)
stationindex = 1
for station in stations:
if dialog.iscanceled():
break
dialog.update((stationindex * 100)/ numberofstations)
plsfile = urllib2.urlopen(station.get_fileurl())
plsfilecontent = plsfile.readlines()
plsfile.close()
directory = shoutcast_dir + subst_illegal_chars(genre) + '\\'
filename = '%s ' + get_filename(plsfilecontent, station.get_bitrate())
save_file(directory, filename % stationindex, plsfilecontent)
stationindex += 1
if dialog.iscanceled():
break
dialog.close()
genreindex += 1
if dialog.iscanceled():
dialog.close()
except:
dialog.close()
dialog = xbmcgui.dialog()
traceback.print_exc()
dialog.ok("unexpected error", "%s\npress white button after closing the dialog for more info." % sys.exc_info()[0])
find quote
Zinger Offline
Junior Member
Posts: 25
Joined: Feb 2004
Reputation: 0
Post: #9
you should be able to use the code. with some changes. it pulls the title from the page not the pls and only grabs the rn number to build the link to the pls file. i've been working on a live player (does not download anything to hd) and provides all genre/station stuff in gui dialogs. but maybe you can port it into the updater script. good luck.
find quote
IndieRockSteve Offline
Senior Member
Posts: 195
Joined: Oct 2003
Reputation: 0
Post: #10
ah, ok, so i have to add the other info.

so should i continue working on this if your writing a cooler version?
find quote
Zinger Offline
Junior Member
Posts: 25
Joined: Feb 2004
Reputation: 0
Post: #11
up to you. it may be forever and a day before i find time to finish. if you enjoy programming, as i do, then nothing you do has gone to waste. Smile
find quote