Index: README ================================================================== --- README +++ README @@ -104,11 +104,11 @@ - fix for https://bugzilla.redhat.com/show_bug.cgi?id=655596 array error - transitioned glade file to gtk.Builder, just a few things broke - made configuration window resizable - locked station list columns, no longer resortable (associatation mismatch) - applied Vincents fixed for Windows action.run and Shoutcast PQ parsing -- +- reading of live365 restored, but no .pls location so far 2.0.8 - configuration files use prettified json - fixed double quotation for %pls players and /local/file handling Index: _pack ================================================================== --- _pack +++ _pack @@ -1,35 +1,43 @@ -#"bsd" "osx" -# "rpm -a noarch" #-- meta data VERSION=$(get_version st2.py) epm_set_version _package.epm $VERSION perl -n -i -e 'if (/^(\s+a.set_version)[\d.(\")]+/m) { print "$1(\"'$VERSION'\")\n" } else { print }' st2.py -#-- linux -for pkg in "rpm" "deb DEP=deb" "slackware" "portable -s streamtuner2.png" -do - echo \#\#\#$pkg\#\#\# - sudo="" - if [ "$pkg" == "deb" ]; then sudo=fakeroot ; fi - $sudo epm -v -n -a all -f $pkg streamtuner2 _package.epm -done +echo "------------------ source .txz ------------------" +cd .. +pax -wvJf streamtuner2-$VERSION.src.txz \ + streamtuner2/*.py streamtuner2/*.xml streamtuner2/channels/*.{py,png} \ + streamtuner2/*.png streamtuner2/*.svg streamtuner2/*.desktop \ + streamtuner2/README streamtuner2/help/* streamtuner2/contrib/* \ + streamtuner2/PKG-INFO streamtuner2/version streamtuner2/_pack streamtuner2/*.epm +# streamtuner2/scripts +cd - + + +echo "-------------------- .deb -----------------------" +fakeroot epm -vvv -n -a all -f deb DEP=deb streamtuner2 _package.epm + + +echo "------------------ slackware --------------------" +epm -vvv -n -a all -f slackware streamtuner2 _package.epm + + +echo "-------------- .tar.gz installer ---------------" +epm -vvv -n -a all -f portable -s streamtuner2.png streamtuner2 _package.epm + + +echo "-------------------- .rpm -----------------------" +epm -vvv -n -a all -f rpm streamtuner2 _package.epm +/usr/bin/rpmbuild -bb --buildroot "/home/mario/projects/streamtuner2/linux-3.0-all/buildroot" --target all linux-3.0-all/streamtuner2.spec +mv linux-3.0-all/RPMS/all/*.rpm linux-3.0-all -#-- win32 +echo "-------------------- win32 -----------------------" for pkg in "win32" do epm-win32sfx -v streamtuner2 _package.epm done - -#-- src.tgz -cd .. -pax -wzf streamtuner2-$VERSION.src.tgz \ - streamtuner2/*.py streamtuner2/*.xml streamtuner2/channels/*.{py,png} \ - streamtuner2/*.png streamtuner2/*.svg streamtuner2/*.desktop \ - streamtuner2/README streamtuner2/help/* streamtuner2/contrib/* \ - streamtuner2/PKG-INFO streamtuner2/version streamtuner2/_pack streamtuner2/*.epm -# streamtuner2/scripts Index: _package.epm ================================================================== --- _package.epm +++ _package.epm @@ -1,25 +1,24 @@ %product streamtuner2 - internet radio browser -%version 2.0.8.5 +%version 2.0.9 %vendor Mario Salzer %license -%copyright Placed into the Public Domain, 2009/2010 +%copyright Placed into the Public Domain, 2009-2014 %readme README -%description %description Browser for Internet Radio Stations -%description +%description . %description streamtuner2 is a browser for radio station directories. %description It can fetch lists from SHOUTcast, Xiph.org, Live365, %description Jamendo, DMOZ, basic.ch, Punkcast. And it lists stream %description entries by category or genre. It reuses existing audio %description players, and recording is delegated to streamripper. -%description +%description . %description It mimics the original streamtuner 0.99.99, but is easier %description to extend because it's written entirely in Python. It's %description already in a stable and useable form. -%description +%description . %description There is no license to accept. Streamtuner2 is open source %description and released into the Public Domain. %system all Index: channels/live365.py ================================================================== --- channels/live365.py +++ channels/live365.py @@ -1,5 +1,13 @@ +# +# api: streamtuner2 +# title: live365 +# state: deprecated +# +# Live365 probably won't be made working anymore. Too many Javascript+XML blobs. +# Pretty cumbersome/sluggish to use the actual website nowadays. +# @@ -56,92 +64,67 @@ ChannelPlugin.__init__(self, parent) # read category thread from /listen/browse.live def update_categories(self): - self.categories = [] - - # fetch page - html = http.get("http://www.live365.com/index.live", feedback=self.parent.status); - rx_genre = re.compile(""" - href='/genres/([\w\d%+]+)'[^>]*> - ( (?:)? ) - ( \w[-\w\ /'.&]+ ) - ( (?:)? ) - """, re.X|re.S) - - # collect - last = [] - for uu in rx_genre.findall(html): - (link, sub, title, main) = uu - - # main - if main and not sub: - self.categories.append(title) - self.categories.append(last) - last = [] - # subcat - else: - last.append(title) - - # don't forget last entries - self.categories.append(last) + return # extract stream infos def update_streams(self, cat, search=""): # search / url if (not search): - url = "http://www.live365.com/cgi-bin/directory.cgi?genre=" + self.cat2tag(cat) + "&rows=200" #+"&first=1" + url = "http://www.live365.com/genres/" + self.cat2tag(cat) else: - url = "http://www.live365.com/cgi-bin/directory.cgi?site=..&searchdesc=" + urllib.quote(search) + "&searchgenre=" + self.cat2tag(cat) + "&x=0&y=0" + url = "http://www.live365.com/cgi-bin/directory.cgi?mode=2&site=web&searchdesc=" + urllib.quote(search) html = http.get(url, feedback=self.parent.status) # we only need to download one page, because live365 always only gives 200 results - # terse format + # extract JS calls rx = re.compile(r""" - ['"]Launch\((\d+).*? - ['"](OK|PM_ONLY|SUBSCRIPTION).*? - href=['"](http://www.live365.com/stations/\w+)['"].*? - page['"]>([^<>]*).*? - CLASS="genre"[^>]*>(.+?).+? - =["']audioQuality.+?>\w+\s+(\d+)\w<.+? - >DrawListenerStars\((\d+),.+? - >DrawRatingStars\((\d+),\s+(\d+),.*? - ["']station_id=(\d+).+? - class=["']?desc-link[^>]+>([^<>]*)< + new\s+top\.Station; + \s+ stn.set\("stationName", \s+ "(\w+)"\); + \s+ stn.set\("title", \s+ "([^"]+)"\); + \s+ stn.set\("id", \s+ "(\d+)"\); + \s+ stn.set\("listenerAccess", \s+ "(\w+)"\); + \s+ stn.set\("status", \s+ "(\w+)"\); + \s+ stn.set\("serverMode", \s+ "(\w+)"\); + \s+ stn.set\("rating", \s+ "(\d+)"\); + \s+ stn.set\("ratingCount", \s+ "(\d+)"\); + \s+ stn.set\("tlh", \s+ "(\d+)"\); + \s+ stn.set\("imgUrl", \s+ "([^"]+)"\); + \s+ stn.set\("location", \s+ "([^"]+)"\); """, re.X|re.I|re.S|re.M) -# src="(http://www.live365.com/.+?/stationlogo\w+.jpg)".+? + +#('jtava', 'ANRLIVE.NET', '293643', 'PUBLIC', 'OK', 'OR', '298', '31', +#'98027', 'http://www.live365.com/userdata/37/15/1371537/stationlogo80x45.jpg', 'n/a') + # append entries to result list __print__( html ) ls = [] for row in rx.findall(html): __print__( row ) - points = int(row[7]) - count = int(row[8]) + ls.append({ "launch_id": row[0], - "sofo": row[1], # subscribe-or-fuck-off status flags - "state": ("" if row[1]=="OK" else gtk.STOCK_STOP), - "homepage": entity_decode(row[2]), - "title": entity_decode(row[3]), - "genre": self.strip_tags(row[4]), - "bitrate": int(row[5]), - "listeners": int(row[6]), + "title": entity_decode(row[1]), + "station_id": row[2], + "sofo": row[3], + "state": ("" if row[4]=="OK" else gtk.STOCK_STOP), + "rating": int(row[6]), + "listeners": int(row[8]), + "img_": row[9], + "description": entity_decode(row[10]), "max": 0, - "rating": (points + count**0.4) / (count - 0.001*(count-0.1)), # prevents division by null, and slightly weights (more votes are higher scored than single votes) - "rating_points": points, - "rating_count": count, - # id for URL: - "station_id": row[9], - "url": self.base_url + "play/" + row[9], - "description": entity_decode(row[10]), - #"playing": row[10], - # "deleted": row[0] != "OK", + "genre": cat, + "bitrate": 128, + "playing": "", + "url": "http://www.live365.com/cgi-bin/mini.cgi?version=3&templateid=xml&from=web&site=web&caller=&tag=web&station_name="+row[0]+"&_=1388870321828", + "format": "application/xml", }) return ls # faster if we do it in _update() prematurely #def prepare(self, ls): Index: st2.py ================================================================== --- st2.py +++ st2.py @@ -3,11 +3,11 @@ # api: python # type: application # title: streamtuner2 # description: directory browser for internet radio / audio streams # depends: gtk, pygtk, xml.dom.minidom, threading, lxml, pyquery, kronos -# version: 2.0.8.5 +# version: 2.0.9 # author: mario salzer # license: public domain # url: http://freshmeat.net/projects/streamtuner2 # config: # category: multimedia @@ -29,12 +29,11 @@ # """ project status """ # -# Cumulative development time is two months now, but the application -# runs mostly stable already. The GUI interfaces are workable. +# The application runs mostly stable. The GUI interfaces are workable. # There haven't been any optimizations regarding memory usage and # performance. The current internal API is acceptable. Documentation is # coming up. # # current bugs: @@ -521,11 +520,11 @@ # auxiliary window: about dialog class AboutStreamtuner2: # about us def __init__(self): a = gtk.AboutDialog() - a.set_version("2.0.8.5") + a.set_version("2.0.9") a.set_name("streamtuner2") a.set_license("Public Domain\n\nNo Strings Attached.\nUnrestricted distribution,\nmodification, use.") a.set_authors(["Mario Salzer \n\nConcept based on streamtuner 0.99.99 from\nJean-Yves Lefort, of which some code remains\nin the Google stations plugin.\n\n\nMyOggRadio plugin based on cooperation\nwith Christian Ehm. "]) a.set_website("http://milki.include-once.org/streamtuner2/") a.connect("response", lambda a, ok: ( a.hide(), a.destroy() ) )