Internet radio browser GUI for music/video streams from various directory services.

⌈⌋ ⎇ branch:  streamtuner2


live365.py at [883ba616fa]

File channels/live365.py artifact 861e1f12f9 part of check-in 883ba616fa



# api: streamtunter2
# title: Live365
# description: Around 5000 categorized internet radio streams, some paid ad-free ones.
# 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
#      <NANOCASTER_PARAMS> (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
#
#

raise Exception



# streamtuner2 modules
from config import conf
from mygtk import mygtk
import ahttp as http
from channels import *
from config import __print__, dbg
import action

# python modules
import re
import xml.dom.minidom
from xml.sax.saxutils import unescape as entity_decode, escape as xmlentities
import gtk
import copy
import urllib
from itertools import groupby
from time import time
from xml.dom.minidom import parseString

# channel live365
class live365(ChannelPlugin):

    # desc
    module = "live365"
    title = "Live365"
    homepage = "http://www.live365.com/"
    base_url = "http://www.live365.com/"
    has_search = True
    listformat = "url/http"
    mediatype = "audio/mpeg"
    has_search = False

    # content
    categories = ['Alternative', 'Blues', 'Classical', 'Country', 'Easy Listening', 'Electronic/Dance', 'Folk', 'Freeform', 'Hip-Hop/Rap', 'Inspirational', 'International', 'Jazz', 'Latin', 'Metal', 'New Age', 'Oldies', 'Pop', 'R&B/Urban', 'Reggae', 'Rock', 'Seasonal/Holiday', 'Soundtracks', 'Talk']
    current = "Alternative"
    default = "Pop"
    empty = None
    
    # redefine
    streams = {}
    

    def __init__(self, parent=None):
    
        # override datamap fields  //@todo: might need a cleaner method, also: do we really want the stream data in channels to be different/incompatible?
        self.datamap = copy.deepcopy(self.datamap)
        self.datamap[5][0] = "Rating"
        self.datamap[5][2][0] = "rating"
        
        # superclass
        ChannelPlugin.__init__(self, parent)


    # fixed for now
    def update_categories(self):
        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)
        
        # 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)
            ls.append({
                "name": row["stationName"],
                "title": row["title"],
                "playing": "",
                "id": row["id"],
                "access": row["listenerAccess"],
                "status": row["status"],
                "mode": row["serverMode"],
                "rating": int(row["rating"]),
                "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"])
            })
        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