Index: _package.epm ================================================================== --- _package.epm +++ _package.epm @@ -1,7 +1,7 @@ %product streamtuner2 - internet radio browser -%version 2.1.1 +%version 2.1.2 %vendor Mario Salzer %license %copyright Placed into the Public Domain, 2009-2014 %readme README @@ -31,11 +31,11 @@ d 755 root root /usr/share/streamtuner2 - f 644 root root /usr/share/streamtuner2/streamtuner2.png ./streamtuner2.png f 644 root root /usr/share/pixmaps/streamtuner2.png ./logo.png f 644 root root /usr/share/streamtuner2/gtk2.xml ./gtk2.xml f 644 root root /usr/share/streamtuner2/gtk3.xml ./gtk3.xml -#f 644 root root /usr/share/streamtuner2/pson.py ./pson.py +#f 644 root root /usr/share/streamtuner2/pson.py ./pson.py #f 644 root root /usr/share/streamtuner2/processing.py ./processing.py f 644 root root /usr/share/streamtuner2/compat2and3.py ./compat2and3.py f 644 root root /usr/share/streamtuner2/action.py ./action.py f 644 root root /usr/share/streamtuner2/config.py ./config.py f 644 root root /usr/share/streamtuner2/ahttp.py ./ahttp.py @@ -45,12 +45,16 @@ f 644 root root /usr/share/streamtuner2/pq.py ./pq.py #-- channels d 755 root root /usr/share/streamtuner2/channels - f 644 root root /usr/share/streamtuner2/channels/__init__.py ./channels/__init__.py f 644 root root /usr/share/streamtuner2/channels/_generic.py ./channels/_generic.py +f 644 root root /usr/share/streamtuner2/channels/icast.py ./channels/icast.py +f 644 root root /usr/share/streamtuner2/channels/icast.png ./channels/icast.png f 644 root root /usr/share/streamtuner2/channels/internet_radio.py ./channels/internet_radio.py f 644 root root /usr/share/streamtuner2/channels/internet_radio.png ./channels/internet_radio.png +f 644 root root /usr/share/streamtuner2/channels/itunes.py ./channels/itunes.py +f 644 root root /usr/share/streamtuner2/channels/itunes.png ./channels/itunes.png f 644 root root /usr/share/streamtuner2/channels/jamendo.py ./channels/jamendo.py f 644 root root /usr/share/streamtuner2/channels/jamendo.png ./channels/jamendo.png f 644 root root /usr/share/streamtuner2/channels/live365.py ./channels/live365.py f 644 root root /usr/share/streamtuner2/channels/live365.png ./channels/live365.png f 644 root root /usr/share/streamtuner2/channels/modarchive.py ./channels/modarchive.py @@ -73,12 +77,10 @@ f 644 root root /usr/share/streamtuner2/channels/links.py ./channels/links.py f 644 root root /usr/share/streamtuner2/channels/timer.py ./channels/timer.py #-- scripts #d 755 root root /usr/share/streamtuner2/scripts - #f 644 root root /usr/share/streamtuner2/scripts/radiotop40_de.py ./scripts/radiotop40_de.py -#-- themes -#f 644 root root /usr/share/streamtuner2/themes/MountainDew/gtk-2.0/gtkrc ./themes/MountainDew/gtk-2.0/gtkrc #-- help files f 644 root root /usr/share/man/man1/streamtuner2.1 ./help/streamtuner2.1 d 755 root root /usr/share/doc/streamtuner2/help - f 644 root root /usr/share/doc/streamtuner2/help/action_homepage.page ./help/action_homepage.page f 644 root root /usr/share/doc/streamtuner2/help/action_playing.page ./help/action_playing.page Index: ahttp.py ================================================================== --- ahttp.py +++ ahttp.py @@ -54,11 +54,11 @@ #-- Retrieve data via HTTP # # Well, it says "get", but it actually does POST and AJAXish GET requests too. # -def get(url, params={}, referer="", post=0, ajax=0, binary=0, feedback=None): +def get(url, params={}, referer="", post=0, ajax=0, binary=0, feedback=None, content=True): __print__( dbg.HTTP, "GET", url, params ) # statusbar info progress_feedback(url) @@ -76,18 +76,22 @@ r = session.get(url, params=params, headers=headers) __print__( dbg.HTTP, r.request.headers ); __print__( dbg.HTTP, r.headers ); - # result + # finish, clean statusbar #progress_feedback(0.9) - content = (r.content if binary else r.text) - - # finish, clean statusbar progress_feedback("") - __print__( dbg.INFO, "Content-Length", len(content) ) - return content + + # result + __print__( dbg.INFO, "Content-Length", len(r.content) ) + if not content: + return r + elif binary: + return r.content + else: + return r.text #-- Append missing trailing slash to URLs Index: channels/file.py ================================================================== --- channels/file.py +++ channels/file.py @@ -4,11 +4,11 @@ # description: Displays mp3/oggs or m3u/pls files from local media file directories. # type: channel # category: media # version: 0.0 # priority: optional -# depends: mutagen, kiwi +# depends: mutagen # # # Local file browser. # # @@ -178,8 +178,10 @@ # same as init def update_streams(self, cat, x=0): self.scan_dirs() + print(self.streams) + print(self.categories) return self.streams.get(os.path.basename(cat)) Index: channels/live365.py ================================================================== --- channels/live365.py +++ channels/live365.py @@ -5,24 +5,22 @@ # type: channel # category: radio # version: 0.3 # priority: optional # -# 2.1.2 broken, -# new URLs: -# -# GET /cgi-bin/mini.cgi?version=3&templateid=xml&from=web&site=web&caller=&tag=web&station_name=bofbm&_=1404610275892 -# (session id) -# -# GET /play?now=59&membername=&session=1404610276-475426&tag=web&s=bofbm&d=LIVE365&r=0 -# &app_id=web%3ABROWSER&token=b99d7f579bacab06b9baa1502d53bedc-3101060080001248&AuthType=NORMAL -# &lid=276006-deu&SaneID=178.24.130.71-1404610229579 +# +# +# We're currently extracting from the JavaScript; +# +# stn.set("param", "value"); +# +# And using a HTML5 player direct URL now: +# +# /cgi-bin/play.pls?stationid=%s&direct=1&file=%s.pls +# # # - -raise Exception - # streamtuner2 modules from config import conf from mygtk import mygtk @@ -39,10 +37,11 @@ import copy import urllib from itertools import groupby from time import time from xml.dom.minidom import parseString + # channel live365 class live365(ChannelPlugin): # desc @@ -81,78 +80,76 @@ pass # extract stream infos def update_streams(self, cat): - - url = "http://www.live365.com/genres/%s" % cat.lower() - html = http.get(url, feedback=self.parent.status) + + # Retrieve genere index pages + html = "" + for i in [1, 17, 33, 49]: + url = "http://www.live365.com/cgi-bin/directory.cgi?first=%i&site=web&mode=3&genre=%s&charset=UTF-8&target=content" % (i, cat.lower()) + html += http.get(url, feedback=self.parent.status) # Extract from JavaScript rx = re.compile(r""" stn.set\( " (\w+) ", \s+ " ((?:[^"\\]+|\\.)*) "\); \s+ """, re.X|re.I|re.S|re.M) # Group entries before adding them ls = [] - for i,g in groupby(rx.findall(html), self.group_by_station): - row = dict(g) + for i,row in groupby(rx.findall(html), self.group_by_station): + row = dict(row) ls.append({ "name": row["stationName"], "title": row["title"], - "playing": "", + "playing": "n/a", "id": row["id"], "access": row["listenerAccess"], "status": row["status"], "mode": row["serverMode"], "rating": int(row["rating"]), - "rating": row["ratingCount"], + #"rating": row["ratingCount"], "listeners": int(row["tlh"]), "location": row["location"], "favicon": row["imgUrl"], "format": self.mediatype, - "url": "urn:live365:%s:%s" % (row["id"], row["stationName"]) + "url": "%scgi-bin/play.pls?stationid=%s&direct=1&file=%s.pls" % (self.base_url, row["id"], row["stationName"]) }) - print ls return ls - - # inject session id etc. into direct audio url - def play(self, row): - if row.get("url"): - - # params - id = row["id"] - name = row["name"] - - # get session - mini = "http://www.live365.com/cgi-bin/mini.cgi?version=3&templateid=xml&from=web&site=web" \ - + "&caller=&tag=web&station_name=%s&_=%i111" % (name, time()) - xml = parseString(http.get(mini)).getElementsByTagName("LIVE365_PLAYER_WINDOW")[0] - x = lambda name: xml.getElementsByTagName(name)[0].childNodes[0].data - - # mk audio url - play = "http://www.live365.com/play?now=0&" \ - + x("NANOCASTER_PARAMS") \ - + "&token=" + x("TOKEN") \ - + "&AuthType=NORMAL&lid=276006-deu&SaneID=178.24.130.71-1406763621701" - __print__(dbg.DATA, play) - - # let's see what happens - action.action.play(play, self.mediatype, self.listformat) - - - # itertools.groupby filter gi = 0 def group_by_station(self, kv): if kv[0] == "stationName": self.gi += 1 return self.gi - # we can no longer cache all the things - def cache(self): - pass - def save(self): - pass + + # inject session id etc. into direct audio url + def UNUSED_play(self, row): + if row.get("url"): + + # params + id = row["id"] + name = row["name"] + + # get mini.cgi station resource + mini_url = "http://www.live365.com/cgi-bin/mini.cgi?version=3&templateid=xml&from=web&site=web" \ + + "&caller=&tag=web&station_name=%s&_=%i111" % (name, time()) + mini_r = http.get(mini_url, content=False) + mini_xml = parseString(mini_r.text).getElementsByTagName("LIVE365_PLAYER_WINDOW")[0] + mini = lambda name: mini_xml.getElementsByTagName(name)[0].childNodes[0].data + + # authorize with play.cgi + play_url = "" + + # mk audio url + play = "http://%s/play" % mini("STREAM_URL") \ + + "?now=0&" \ + + mini("NANOCASTER_PARAMS") \ + + "&token=" + mini("TOKEN") \ + + "&AuthType=NORMAL&lid=276006-deu&SaneID=178.24.130.71-1406763621701" + + # let's see what happens + action.action.play(play, self.mediatype, self.listformat) 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: pygtk | pygi, threading, pyquery, kronos, requests -# version: 2.1.1 +# version: 2.1.2 # author: mario salzer # license: public domain # url: http://freshmeat.net/projects/streamtuner2 # config: # category: multimedia @@ -94,11 +94,11 @@ import channels from channels import * import favicon -__version__ = "2.1.1" +__version__ = "2.1.2" # this represents the main window # and also contains most application behaviour main = None