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

⌈⌋ ⎇ branch:  streamtuner2


Check-in [e4fa4859c6]

Overview
Comment:Implement new .resolve_urn() hook (replacing .row() override), which is added automatically now in ChannelPlugin init.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: e4fa4859c664fe4856869de74639f0b09c650f25
User & Date: mario on 2016-11-06 11:42:32
Other Links: manifest | tags
Context
2016-11-06
11:43
Show player config screenshot also in config_apps/placeholder page. check-in: 2a46669362 user: mario tags: trunk
11:42
Implement new .resolve_urn() hook (replacing .row() override), which is added automatically now in ChannelPlugin init. check-in: e4fa4859c6 user: mario tags: trunk
11:40
Also perform an action.resolve_urn() right when accessing a row. Thus the stremaing lsits get updated on any .row() acccess (= now centrally covered). check-in: 2859a51985 user: mario tags: trunk
Changes

Modified contrib/delicast.py from [f07d91d0e9] to [ecaaf5255a].

1
2
3
4
5
6
7
8
9
10
11
12
13
# encoding: UTF-8
# api: streamtuner2
# title: Delicast
# description: directory of streaming media
# url: http://delicast.com/
# version: 0.3
# type: channel
# category: radio
# config: -
# png:
#    iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAA
#    AmJLR0QA/4ePzL8AAAAHdElNRQffBB4UJAsX77G0AAAANUlEQVQY02OwQwMMdv/BAEUASCFEoAIIEZIEIGYjBCAUwpb/6O5ACEABGQJ2cFsQIlB3oAEA6iVo+vl+BbQA
#    AAAldEVYdGRhdGU6Y3JlYXRlADIwMTUtMDQtMzBUMjI6MzY6MDMrMDI6MDAFLUvfAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE1LTA0LTMwVDIyOjM2OjAzKzAyOjAwdHDz





|







1
2
3
4
5
6
7
8
9
10
11
12
13
# encoding: UTF-8
# api: streamtuner2
# title: Delicast
# description: directory of streaming media
# url: http://delicast.com/
# version: 0.4
# type: channel
# category: radio
# config: -
# png:
#    iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAA
#    AmJLR0QA/4ePzL8AAAAHdElNRQffBB4UJAsX77G0AAAANUlEQVQY02OwQwMMdv/BAEUASCFEoAIIEZIEIGYjBCAUwpb/6O5ACEABGQJ2cFsQIlB3oAEA6iVo+vl+BbQA
#    AAAldEVYdGRhdGU6Y3JlYXRlADIwMTUtMDQtMzBUMjI6MzY6MDMrMDI6MDAFLUvfAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE1LTA0LTMwVDIyOjM2OjAzKzAyOjAwdHDz
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# page (50 stations) is fetched.


import re
from config import *
from channels import *
import ahttp



# Delayed streaming URL discovery
class delicast (ChannelPlugin):

    # control flags
    has_search = False
    listformat = "srv"
    audioformat = "audio/mpeg"
    titles = dict(listeners=False, bitrate=False, playing="Location")

    categories = ["60s", "70s", "80s", "90s", "Alternative", "Blues",
    "Chillout", "Christian", "Classical", "Community", "Country", "Culture",
    "Dance", "Disco", "Easy listening", "Electronic", "Folk", "Funk",
    "Gospel", "Hiphop", "House Indie", "Information", "Jazz", "Latin",
    "Lounge", "Love", "Metal", "Oldies", "Pop", "R n b", "Reggae", "Rock",
    "Romantic", "Soul", "Sports", "Student", "Talk", "Techno", "Trance",
    "Urban", "World music"]


    # static
    def update_categories(self):
        pass


    # Fetch entries







>


















|







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# page (50 stations) is fetched.


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


# Delayed streaming URL discovery
class delicast (ChannelPlugin):

    # control flags
    has_search = False
    listformat = "srv"
    audioformat = "audio/mpeg"
    titles = dict(listeners=False, bitrate=False, playing="Location")

    categories = ["60s", "70s", "80s", "90s", "Alternative", "Blues",
    "Chillout", "Christian", "Classical", "Community", "Country", "Culture",
    "Dance", "Disco", "Easy listening", "Electronic", "Folk", "Funk",
    "Gospel", "Hiphop", "House Indie", "Information", "Jazz", "Latin",
    "Lounge", "Love", "Metal", "Oldies", "Pop", "R n b", "Reggae", "Rock",
    "Romantic", "Soul", "Sports", "Student", "Talk", "Techno", "Trance",
    "Urban", "World music"]
    

    # static
    def update_categories(self):
        pass


    # Fetch entries
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
                    genre = cat,
             #      genre = unhtml(tags),
                ))
        return r
      

    # Update `url` on station data access (incurs a delay for playing or recording)
    def row(self):
        r = ChannelPlugin.row(self)
        if r.get("url") == "urn:delicast":
            html = ahttp.get(r["homepage"])
            ls = re.findall("^var url = \"(.+)\";", html, re.M)
            r["url"] = ls[0]
        return r








|
<
|


|
|

79
80
81
82
83
84
85
86

87
88
89
90
91
92
                    genre = cat,
             #      genre = unhtml(tags),
                ))
        return r
      

    # Update `url` on station data access (incurs a delay for playing or recording)
    def resolve_urn(self, row):

        if row.get("url").startswith("urn:delicast"):
            html = ahttp.get(r["homepage"])
            ls = re.findall("^var url = \"(.+)\";", html, re.M)
            row["url"] = ls[0]
        return row

Modified contrib/reciva.py from [56fdd75c9a] to [12c0c0ed91].

1
2
3
4
5
6
7
8
9
10
11
# encoding: UTF-8
# api: streamtuner2
# title: Reciva
# url: https://radios.reciva.com/stations/genre/34?&start=100&count=50
# description: Home internet radio app and diverse station database.
# version: 0.4
# type: channel
# category: radio
# config: -
# priority: contrib
# png:



|







1
2
3
4
5
6
7
8
9
10
11
# encoding: UTF-8
# api: streamtuner2
# title: Reciva
# url: https://radios.reciva.com/
# description: Home internet radio app and diverse station database.
# version: 0.4
# type: channel
# category: radio
# config: -
# priority: contrib
# png:
25
26
27
28
29
30
31

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# going to be used implicitly.
#


import re
from pq import pq
import ahttp

from config import *
from channels import *


# Reciva directory
class reciva (ChannelPlugin):

    # module attributes
    module = "reciva"
    listformat = "pls"
    has_search = True
    categories = ['60s', '70s', '80s', '90s', 'Adult', ['Adult Contemporary'], 'Alternative', 'Ambient', 'Bluegrass', 'Blues', 'Bollywood', 'Christian', ['Christian Contemporary'], 'Classic Rock', 'Classical', 'College', 'Comedy', 'Contemporary', 'Country', 'Dance', 'Discussion', 'Easy', 'Electronica', 'Experimental', 'Folk', 'Gospel', 'Greek', 'Hip Hop', 'Indian', 'Indie', ['Indie Rock'], 'Jazz', 'Jungle', 'Kids', 'Latin Hits', 'New Age', 'News', ['News Talk', 'News Updates'], 'Oldies', 'Pop', 'Public', 'Punk', 'Rap', 'Reggae', 'Religious', 'Rock', 'Short Wave Radio', 'Soft Rock', 'Spanish', 'Sports', 'Talk', 'Top 40', 'Unknown', 'Varied', 'World', ['World Africa', 'World Asia', 'World Caribbean', 'World Europe', 'World Mediterranean', 'World Middle East', 'World Tropical']]
    catmap = { 'classical': '14', 'dance': '18', 'bluegrass': '52', 'contemporary': '16', 'pop': '34', 'spanish': '66', 'college': '15', 'rap': '38', 'ambient': '69', 'talk': '43', 'alternative': '9', 'religious': '39', 'blues': '10', 'folk': '23', 'classic rock': '13', '90s': '7', 'adult contemporary': '8', 'oldies': '33', 'indie rock': '54', 'electronica': '21', 'unknown': '45', 'discussion': '19', 'news talk': '31', 'world mediterranean': '55', 'sports': '42', 'new age': '51', 'indie': '27', 'indian': '65', 'easy': '20', '80s': '6', 'world africa': '67', 'comedy': '62', 'public': '35', 'jungle': '72', 'reggae': '48', 'world middle east': '50', 'christian': '11', 'world caribbean': '68', '60s': '58', 'world europe': '56', 'jazz': '28', '70s': '5', 'soft rock': '41', 'top 40': '44', 'adult': '57', 'news': '30', 'bollywood': '60', 'world tropical': '53', 'latin hits': '29', 'varied': '46', 'christian contemporary': '12', 'kids': '59', 'short wave radio': '73', 'world': '49', 'world asia': '47', 'country': '17', 'news updates': '32', 'punk': '36', 'greek': '25', 'hip hop': '26', 'rock': '40', 'gospel': '24', 'experimental': '22' }
    titles = dict( genre="Genre", title="Station", playing="Location", bitrate="Bitrate", listeners=False )
    base_url = "https://radios.reciva.com/stations/genre/%s?&start=0&count=%s"
    
    
    # init
    def __init__(self, parent):
        ChannelPlugin.__init__(self, parent)
        self.login()


    # update list
    def update_categories(self):
        self.categories = []
        html = ahttp.get(self.base_url % (1, 1))
        for c in re.findall('id="cg-(\d+)">([\w\d\s]+)</a></li>', html):
            self.catmap[c[1].lower()] = c[0]
            self.categories.append(c[1])







>

















<
<
<
<
<
<







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49






50
51
52
53
54
55
56
# going to be used implicitly.
#


import re
from pq import pq
import ahttp
import action
from config import *
from channels import *


# Reciva directory
class reciva (ChannelPlugin):

    # module attributes
    module = "reciva"
    listformat = "pls"
    has_search = True
    categories = ['60s', '70s', '80s', '90s', 'Adult', ['Adult Contemporary'], 'Alternative', 'Ambient', 'Bluegrass', 'Blues', 'Bollywood', 'Christian', ['Christian Contemporary'], 'Classic Rock', 'Classical', 'College', 'Comedy', 'Contemporary', 'Country', 'Dance', 'Discussion', 'Easy', 'Electronica', 'Experimental', 'Folk', 'Gospel', 'Greek', 'Hip Hop', 'Indian', 'Indie', ['Indie Rock'], 'Jazz', 'Jungle', 'Kids', 'Latin Hits', 'New Age', 'News', ['News Talk', 'News Updates'], 'Oldies', 'Pop', 'Public', 'Punk', 'Rap', 'Reggae', 'Religious', 'Rock', 'Short Wave Radio', 'Soft Rock', 'Spanish', 'Sports', 'Talk', 'Top 40', 'Unknown', 'Varied', 'World', ['World Africa', 'World Asia', 'World Caribbean', 'World Europe', 'World Mediterranean', 'World Middle East', 'World Tropical']]
    catmap = { 'classical': '14', 'dance': '18', 'bluegrass': '52', 'contemporary': '16', 'pop': '34', 'spanish': '66', 'college': '15', 'rap': '38', 'ambient': '69', 'talk': '43', 'alternative': '9', 'religious': '39', 'blues': '10', 'folk': '23', 'classic rock': '13', '90s': '7', 'adult contemporary': '8', 'oldies': '33', 'indie rock': '54', 'electronica': '21', 'unknown': '45', 'discussion': '19', 'news talk': '31', 'world mediterranean': '55', 'sports': '42', 'new age': '51', 'indie': '27', 'indian': '65', 'easy': '20', '80s': '6', 'world africa': '67', 'comedy': '62', 'public': '35', 'jungle': '72', 'reggae': '48', 'world middle east': '50', 'christian': '11', 'world caribbean': '68', '60s': '58', 'world europe': '56', 'jazz': '28', '70s': '5', 'soft rock': '41', 'top 40': '44', 'adult': '57', 'news': '30', 'bollywood': '60', 'world tropical': '53', 'latin hits': '29', 'varied': '46', 'christian contemporary': '12', 'kids': '59', 'short wave radio': '73', 'world': '49', 'world asia': '47', 'country': '17', 'news updates': '32', 'punk': '36', 'greek': '25', 'hip hop': '26', 'rock': '40', 'gospel': '24', 'experimental': '22' }
    titles = dict( genre="Genre", title="Station", playing="Location", bitrate="Bitrate", listeners=False )
    base_url = "https://radios.reciva.com/stations/genre/%s?&start=0&count=%s"
    
    






    # update list
    def update_categories(self):
        self.categories = []
        html = ahttp.get(self.base_url % (1, 1))
        for c in re.findall('id="cg-(\d+)">([\w\d\s]+)</a></li>', html):
            self.catmap[c[1].lower()] = c[0]
            self.categories.append(c[1])
89
90
91
92
93
94
95
96
97
98

99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
                })

        # done    
        return entries


    # Fetch real `url` on stream access/playback (delay)
    def row(self):
        r = ChannelPlugin.row(self)
        if not r["url"].startswith("http"):

            html = ahttp.get("https://radios.reciva.com/streamer?stationid=%s&streamnumber=0" % r["id"])
            ls = re.findall("""(?:<iframe src=|iframe\()['"]([^'"]+)['"]""", html)
            if ls:
                r["url"] = ls[0]
            else:
                log.ERR("No stream found for reciva station #%s", row["id"])
        return r


    # Option login
    def login(self):
        lap = conf.netrc(varhosts = ("reciva.com", "radios.reciva.com"))
        if lap:
            log.LOGIN("Reciva account:", lap)
            ahttp.get(
                "https://radios.reciva.com/account/login",
                {
                    "username": lap[0] or lap[1],
                    "password": lap[2]
                },
                timeout=2
            )







|
<
|
>
|









|











84
85
86
87
88
89
90
91

92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
                })

        # done    
        return entries


    # Fetch real `url` on stream access/playback (delay)
    def resolve_urn(self, r):

        if r["url"].startswith("urn:"):
            id = r["url"].split(":")[2]
            html = ahttp.get("https://radios.reciva.com/streamer?stationid=%s&streamnumber=0" % id)
            ls = re.findall("""(?:<iframe src=|iframe\()['"]([^'"]+)['"]""", html)
            if ls:
                r["url"] = ls[0]
            else:
                log.ERR("No stream found for reciva station #%s", row["id"])
        return r


    # Option login
    def init2(self, *p):
        lap = conf.netrc(varhosts = ("reciva.com", "radios.reciva.com"))
        if lap:
            log.LOGIN("Reciva account:", lap)
            ahttp.get(
                "https://radios.reciva.com/account/login",
                {
                    "username": lap[0] or lap[1],
                    "password": lap[2]
                },
                timeout=2
            )