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

⌈⌋ branch:  streamtuner2

Artifact [29c966c64c]

Artifact 29c966c64c30f1d72a895f655c3dffe52821c624:

# encoding: UTF-8
# api: streamtuner2
# title:
# description: Europe's biggest radio platform
# url:
# version: 0.6
# type: channel
# category: radio
# png:
# priority: optional
# extraction-method: regex
# lists around 20.000 worldwide radio stations.
# A maximum of three pages from each genre are fetched here,
# some of the empty categories already omitted.
# The website heavily depends on JavaScript, a Flash player,
# some social tracking cookies. But still feasible to access
# per custom JSON extractor.
# May require refreshing the station lists once in a while,
# because there's an API key in each JSON station info URL.

import time
import json
import re
from config import *
from channels import *
import ahttp
import action

# hook special JSON format in to avoid grepping images by generic handler
action.playlist_fmt_prio.insert(5, "rnjs")
action.playlist_content_map.insert(7, ("rnjs", r'"logo175x175rounded"'))
action.extract_playlist.extr_urls["rnjs"] = dict(
    url   = r" (?x) \"streamUrl\" \s*:\s* \"(\w+:\\?/\\?/[^\"]+)\" ",
    title = r" (?x) \"(?:description|seoTitle)\" \s*:\s* \"([^\"]+)\" ",
    unesc = "json",

# extraction relies on HTML grepping, finding an api key required for station details,
# and letting the action module extract the station/stream URL from that JSON format.
class radionet (ChannelPlugin):

    # control flags
    has_search = False
    audioformat = "audio/mpeg"
    listformat = "rnjs"
    titles = dict(listeners=False, playing="Description")

    # sources
    apiPrefix = ""
    genre_url = "{}/"
    apiKey = None
    # Retrieve cat list and map
    def update_categories(self):
        html = ahttp.get("")
        ls = re.findall("""<li><a class="language-info".*?>([\w\s']+)</a>""", html)
        self.categories = [i for i in ls][0:-18]

    # Fetch entries
    def update_streams(self, cat, search=None):

        # category page, get key
        html = ahttp.get(self.genre_url.format(cat))
        for p in range(2, 4):
            if html.find('"?p={}">'.format(p)) >= 0:
                html += ahttp.get(self.genre_url.format(cat) + "?p={}".format(p))
        r = []
        <div class="stationinfo-content">Fresh pop music, R&#039;n&#039;B, the newest chart hits and the Top 40 can all be found on this Internet radio station, ANTENNE BAYERN.</div>
        </div><div class="stationinfo   stationinfo-extended  "  expandable ng-cloak >
        <a href="" class="stationinfo-link" id="">
        <svg class="icon icon-play" id=""><use xmlns:xlink="" xlink:href="#icon-play" id=""></use></svg>
        <img src="" onError="this.src=window.stationLogo;" alt="ANTENNE FRANKFURT 95.1 " id="">
        <strong id="">
        <svg class="icon icon-podcast"><use xmlns:xlink="" xlink:href="#icon-podcast"></use></svg>
        <span highlight-text="Top 40" highlight-class="highlight-tags" id="">antenne frankfurt 95.1 </span>
        <small highlight-text="Top 40" highlight-class="highlight-tags" id="">
        Frankfurt am Main, Germany / Charts, News, Pop, Top 40, News, Weather</small>
        <em now-playing="19304" searched-term="Top 40" id=""></em>
        <a class="stationinfo-info-toggle" href="" ng-click="toggle()">
        <svg class="icon icon-arrow-up"><use xmlns:xlink="" xlink:href="#icon-arrow-up"></use></svg>
        <svg class="icon icon-arrow-down"><use xmlns:xlink="" xlink:href="#icon-arrow-down"></use></svg>
        # split station blocks
        for row in re.split("""<div class="stationinfo""", html)[1:]:
            # extract text fields
            d = re.findall("""
              <a\s+href="(?:http:)?(//([\w-]+)\.radio\.net/?)" .*?
              <img\s+src="([^<">]+)" .*?
              <strong[^>]*>(.*?)</strong> .*?
              <small[^>]*>\s*(.*?)\s*</small> .*?
            """, row, re.X|re.S)
            # refurbish extracted strings
            if d and len(d) and len(d[0]) == 5:
                href, name, img, title, desc = d[0]
                    name = name,
                    genre = cat,
                    title = unhtml(title),
                    playing = unhtml(desc),
                    url = self._url(name),
                    homepage = "http:{}".format(href),
                    img = img,
        return r

    # Patch together JSON station info URL
    def _url(self, name):
        return \
            self.apiPrefix, self.apiKey, name
        )      # '?_={time}&' is omitted here, only relevant to jQuery/AJAX,
               # and just made bookmarks.is_in() fail due to randomized URLs

    # extract JavaScript key from any HTML blob (needed for station query)
    def set_key(self, html):
        ls = re.findall("""apiKey: '(\w+)'""", html)
        if ls:
            self.apiKey = ls[0]