1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
# encoding: UTF-8
# api: streamtuner2
# title: Xiph.org
# description: ICEcast radio directory. Now utilizes a cached JSON API.
# type: channel
# url: http://dir.xiph.org/
# version: 0.4
# category: radio
# config:
# { name: xiph_min_bitrate, value: 64, type: int, description: "Minimum bitrate; filter lesser quality streams.", category: filter }
# { name: xiph_source, value: cache, type: select, select: "cache=JSON cache srv|xml=Clunky XML blob|web=Forbidden fruits", description: "Source for station list extraction." }
# priority: standard
# png:
# iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAg5JREFUOI2lk1tIE2AUx3+7CG1tlmlG1rSEHrKgEUF7yO40taQiRj10I4qKkOaT4hIUItuTkC8hpJAQtJCICrFpzEKw
# h61eQorGNBOTzbEt16ZrnR5Wq3mZD/3heziX//983znngyyov+eSbHEA5WKBhs4BKVy9gsqajqwiCwo0dA5IQX5u2s4moliMPPV1nCeDzxgNBFDHE2wsKMPzsGVefobjcnO7RMfeMuL341ZBrNEGRmPqqjdvsbbf
# w7irO4Oj+rdywNNNucmERsLUVndR8uYRU13PCew6hpgP8W02xMpIsik++qk5oweW6y3yob8WnXacZDKJWh1Cp4OtRUHsh19TUlUGViv09RGqKAenU5QnLKm+rK88LjgcUnxmr/h8iNO5XYJBRAQZ/qiVeptGWjty
# 5cClDWLwugQRIRiU5UdPCoD6S89jhV6pks9WG6fuwtBtF5v72vC1v+B86SsM+jD56hjnyiM0lRrAbofeXjQJLdE/78jbXSU5166I6f5VeeDdKdq6GtlSd0QkVU+8XsQhlt9W6izbZ5aMKWgtp2WT/yUHd0xSYU7i
# dsPQ+1WMKIsJD08wEV2HGLeRyNMjawqRxhuKBfdgz1m7fI/4mVX+ZGxmgniOoJv+QZHGAMC7p60ZnHkC8HfzZmLTBCd9af9ccnqMc9HTdmFe4kLkJbH/4h0xVtcu+SP/C78AL6btab6woPcAAAAASUVORK5CYII=
#
# Xiph.org maintains the Ogg streaming standard and Vorbis
# audio compression format, amongst others. The ICEcast
# server is an alternative to SHOUTcast.
#
# It also provides a directory listing of known internet
# radio stations, only a handful of them using Ogg though.
# The category list is hardwired in this plugin.
#
# And there are three fetch-modes now:
# → "Cache" retrieves a refurbished JSON station list,
# both sliceable genres and searchable.
# → "XML" fetches the olden YP.XML once, buffers it,
# and tries to uncover per-genre categories from it.
# → "HTML" extracts from the raw dir.xiph.org directory,
# where homepages and listener/max infos are available.
from config import *
from uikit import uikit
import ahttp
from channels import *
import xml.dom.minidom
|
|
|
|
|
|
|
>
<
|
>
|
|
>
|
|
>
>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
# encoding: UTF-8
# api: streamtuner2
# title: Xiph.org
# description: ICEcast radios. Scans per JSON API, slow XML, or raw directory.
# type: channel
# url: http://dir.xiph.org/
# version: 0.5
# category: radio
# config:
# { name: xiph_min_bitrate, value: 64, type: int, description: "Minimum bitrate; filter lesser quality streams.", category: filter }
# { name: xiph_source, value: cache, type: select, select: "cache=JSON cache srv|xml=Clunky XML blob|web=Forbidden fruits", description: "Source for station list extraction." }
# priority: standard
# png:
# iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAg5JREFUOI2lk1tIE2AUx3+7CG1tlmlG1rSEHrKgEUF7yO40taQiRj10I4qKkOaT4hIUItuTkC8hpJAQtJCICrFpzEKw
# h61eQorGNBOTzbEt16ZrnR5Wq3mZD/3heziX//983znngyyov+eSbHEA5WKBhs4BKVy9gsqajqwiCwo0dA5IQX5u2s4moliMPPV1nCeDzxgNBFDHE2wsKMPzsGVefobjcnO7RMfeMuL341ZBrNEGRmPqqjdvsbbf
# w7irO4Oj+rdywNNNucmERsLUVndR8uYRU13PCew6hpgP8W02xMpIsik++qk5oweW6y3yob8WnXacZDKJWh1Cp4OtRUHsh19TUlUGViv09RGqKAenU5QnLKm+rK88LjgcUnxmr/h8iNO5XYJBRAQZ/qiVeptGWjty
# 5cClDWLwugQRIRiU5UdPCoD6S89jhV6pks9WG6fuwtBtF5v72vC1v+B86SsM+jD56hjnyiM0lRrAbofeXjQJLdE/78jbXSU5166I6f5VeeDdKdq6GtlSd0QkVU+8XsQhlt9W6izbZ5aMKWgtp2WT/yUHd0xSYU7i
# dsPQ+1WMKIsJD08wEV2HGLeRyNMjawqRxhuKBfdgz1m7fI/4mVX+ZGxmgniOoJv+QZHGAMC7p60ZnHkC8HfzZmLTBCd9af9ccnqMc9HTdmFe4kLkJbH/4h0xVtcu+SP/C78AL6btab6woPcAAAAASUVORK5CYII=
#
# Xiph.org maintains the Ogg streaming standard and Vorbis,
# Opus, FLAC audio, and Theora video compression formats.
# The ICEcast server is a modern alternative to SHOUTcast.
#
# It also provides a directory listing of known internet
# radio stations, only a handful of them using Ogg though.
# The category list is hardwired in this plugin. And there
# are three station fetching modes now:
#
# → "JSON cache" retrieves a refurbished JSON station list,
# both sliceable genres and searchable.
#
# → "Clunky XML" fetches the olden YP.XML, which is really
# slow, then slices out genres. No search.
#
# → "Forbidden Fruits" extracts from dir.xiph.org HTML pages,
# with homepages and listener/max infos available. Search
# is also possible.
#
from config import *
from uikit import uikit
import ahttp
from channels import *
import xml.dom.minidom
|
︙ | | | ︙ | |
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
|
# Fetch directly from website. Which Xiph does not approve of; but
# hey, it's a fallback option here. And the only way to actually
# uncover station homepages.
#@use_rx
def from_raw_html(self, cat, search=None, use_rx=False):
# Build request URL
if search:
return []
elif cat in ("Ogg_Vorbis", "NSV", "WebM", "Opus"):
url = "http://dir.xiph.org/by_format/{}".format(cat)
elif cat:
url = "http://dir.xiph.org/by_genre/{}".format(cat.title())
# Collect all result pages
html = ahttp.get(url)
for i in range(1,4):
self.status(i/5.1)
html += ahttp.get(url, {"search": cat.title(), "page": i})
try: html = html.encode("raw_unicode_escape").decode("utf-8")
except: pass
# Find streams
r = []
|
>
<
>
>
|
|
>
>
|
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
|
# Fetch directly from website. Which Xiph does not approve of; but
# hey, it's a fallback option here. And the only way to actually
# uncover station homepages.
#@use_rx
def from_raw_html(self, cat, search=None, use_rx=False):
# Build request URL
by_format = {t.lower(): t for t in self.categories[-1]}
if search:
url = "http://dir.xiph.org/search?search={}".format(search)
cat = "search"
elif by_format.get(cat):
url = "http://dir.xiph.org/by_format/{}".format(by_format[cat])
elif cat:
url = "http://dir.xiph.org/by_genre/{}".format(cat.title())
# Collect all result pages
html = ahttp.get(url)
for i in range(1,4):
if html.find('page={}">{}</a></li>'.format(i, i+1)) < 0:
break
self.status(i/5.1)
html += ahttp.get(url, {"search": cat.title(), "page": i})
try: html = html.encode("raw_unicode_escape").decode("utf-8")
except: pass
# Find streams
r = []
|
︙ | | | ︙ | |
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
|
.*? "listeners">\[(\d+)
.*? "stream-description">(.*?)<
.*? Tags: (.*?) </div>
.*? href="(/listen/\d+/listen.xspf)"
.*? class="format"\s+title="([^"]+)"
.*? /by_format/([^"]+)
""", html, re.X|re.S)
# Assemble
for homepage, title, listeners, playing, tags, url, bits, fmt in ls:
r.append(dict(
genre = unhtml(tags),
title = unhtml(title),
homepage = ahttp.fix_url(homepage),
playing = unhtml(playing),
|
>
|
|
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
|
.*? "listeners">\[(\d+)
.*? "stream-description">(.*?)<
.*? Tags: (.*?) </div>
.*? href="(/listen/\d+/listen.xspf)"
.*? class="format"\s+title="([^"]+)"
.*? /by_format/([^"]+)
""", html, re.X|re.S)
print ls
# Assemble
for homepage, title, listeners, playing, tags, url, bits, fmt in ls:
r.append(dict(
genre = unhtml(tags),
title = unhtml(title),
homepage = ahttp.fix_url(homepage),
playing = unhtml(playing),
|
︙ | | | ︙ | |
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
|
],
"ska",
[
"punkrock",
"oi"
],
"darkwave",
"Ogg_Vorbis", "NSV", "WebM", "Opus",
]
# Helper functions for XML extraction mode
# Shortcut to get text content from XML subnode by name
|
>
|
|
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
|
],
"ska",
[
"punkrock",
"oi"
],
"darkwave",
"/FORMAT",
["Ogg_Vorbis", "Ogg_Theora", "Opus", "NSV", "WebM"],
]
# Helper functions for XML extraction mode
# Shortcut to get text content from XML subnode by name
|
︙ | | | ︙ | |