|
| >
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
> | 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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148 | # encoding: UTF-8
# api: streamtuner2
# title: Compound★
# description: combines station lists from multiple channels
# version: 0.1
# type: channel
# category: virtual
# url: http://fossil.include-once.org/streamtuner2/
# config: -
# { name: compound_channels, type: text, value: "shoutcast, internet_radio, xiph, surfmusic", description: "Merge channels, or use all (*)" }
# { name: compound_genres, type: text, value: "Top 40, x", description: "Extract categories, or just use intersection (x)" }
# priority: unsupported
#
# Use this plugin to mix categories and their station entries from two
# or more different directory channels. It merges the lists, albeit in
# a rather crude way. (If anyone is interested, I could add a proper
# regex or something now.)
#
# Per default it lists only selected categories. But can be configured to
# merge just intersectioning categories/genres. Entry order is determined
# from station occourence count in channels AND their individual listener
# count (where available) using some guesswork to eliminate duplicates.
from channels import *
import action
import http
from config import conf
# merging channel
class compound (ChannelPlugin):
# runtime options
has_search = False
listformat = "href" # row entries will contain exact `listformat` classification
audioformat = "audio/*" # same as for correct encoding mime type
# references
parent = None
# data
streams = {}
categories = []
# which categories
def update_categories(self):
# as-is category list
cats = self.split(conf.compound_genres)
self.categories = [c for c in cats if c != "x"]
# if intersection
if "x" in cats:
once = []
for chan in self.channels():
for add in self.flatten(self.parent.channels[chan].categories):
# second occourence in two channels
if add.lower() in once:
if add not in self.categories:
self.categories.append(add)
else: #if add not in self.categories:
once.append(add.lower())
pass
# flatten our two-level categories list
def flatten(self, a):
return [i for sub in a for i in (sub if type(sub)==list else [sub])]
# break up name lists
def split(self, s):
return [s.strip() for s in s.split(",")]
# get list of channels
def channels(self):
# get list
ls = self.split(conf.compound_channels)
# resolve "*"
if "*" in ls:
ls = self.parent.channel_names # includes bookmarks
if self.module in ls:
ls.remove(self.module) # but not compound
return ls
# combine stream lists
def update_streams(self, cat):
r = []
have = []
# look through channels
if cat in self.categories:
for cn in self.channels():
# get channel, refresh list
c = self.parent.channels[cn]
#
for row in self.get_streams(c, cat):
# copy
row = dict(row)
#row["listeners"] = 1000 + row.get("listeners", 0) / 10
row["extra"] = cn # or genre?
row["listformat"] = c.listformat
# duplicates
if row["title"].lower() in have or row["url"] in have:
for i,cmp in enumerate(r):
if cmp["title"].lower()==row["title"].lower() or cmp["url"].find(row["url"])>=0:
r[i]["listeners"] = row.get("listeners",0) + 5000
pass
else:
r.append(row)
have.append(row["title"].lower()) # we're comparing lowercase titles
have.append(row["url"][:row["url"].find("http://")]) # save last http:// part (internet-radio redirects to shoutcast urls)
# sort by listeners
r = sorted(r, key=lambda x: -x.get("listeners", 0))
return r
# extract station list from other channel plugin
def get_streams(self, c, cat):
# if empty?
#c.load(cat)
return c.streams.get(cat) \
or c.update_streams(cat.replace(" ","")) \
or []
|