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

⌈⌋ ⎇ branch:  streamtuner2


Check-in [2b63a42675]

Overview
Comment:Recategorized some dbg.ERR messages, but make them displayed now regardless of conf.debug setting.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 2b63a42675f913c97fd9781d6dd2083ac14348ae
User & Date: mario on 2015-04-05 20:27:33
Other Links: manifest | tags
Context
2015-04-05
22:04
Provide a `-D` debugging flag and a `-d shoutcast` option to disable plugins prior startup. check-in: 08b1306823 user: mario tags: trunk
20:27
Recategorized some dbg.ERR messages, but make them displayed now regardless of conf.debug setting. check-in: 2b63a42675 user: mario tags: trunk
18:24
Make Youtube channel also default. check-in: 333ed92349 user: mario tags: trunk
Changes

Modified channels/__init__.py from [f281706365] to [292484f13c].

218
219
220
221
222
223
224
225

226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245

246
247
248
249
250
251
252
218
219
220
221
222
223
224

225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244

245
246
247
248
249
250
251
252







-
+



















-
+







                modified = []
                for row in new_streams:
                    if len(set(["", None]) & set([row.get("title"), row.get("url")])):
                        continue
                    try:
                        modified.append( self.postprocess(row) )
                    except Exception as e:
                        __print__(e, dbg.ERR, row)
                        __print__(e, dbg.DATA, "Missing title or url. Postprocessing failed:", row)
                new_streams = modified
  
                # don't lose forgotten streams
                if conf.retain_deleted:
                   self.streams[category] = new_streams + self.deleted_streams(new_streams, self.streams.get(category,[]))
                else:
                   self.streams[category] = new_streams
  
                # save in cache
                self.save()
  
                # invalidate gtk list cache
                #if (self.liststore.has_key(category)):
                #    del self.liststore[category]
  
            else:
                # parse error
                self.parent.status("category parsed empty.")
                self.streams[category] = [{"title":"no contents found on directory server","bitrate":0,"max":0,"listeners":0,"playing":"error","favourite":0,"deleted":0}]
                __print__(dbg.ERR, "Oooops, parser returned nothing for category " + category)
                __print__(dbg.INFO, "Oooops, parser returned nothing for category " + category)
                
        # assign to treeview model
        #self.streams[self.default] = []
        #if (self.liststore.has_key(category)):  # was already loded before
        #    self.gtk_list.set_model(self.liststore[category])
        #else:   # currently list is new, had not been converted to gtk array before
        #    self.liststore[category] = \
369
370
371
372
373
374
375
376

377
378
379
380
381
382
383
369
370
371
372
373
374
375

376
377
378
379
380
381
382
383







-
+







            # if category tree is empty, initialize it
            if not self.categories:
                __print__(dbg.PROC, self.module+"first_show: reload_categories");
                #self.parent.thread(self.reload_categories)
                try:
                    self.reload_categories()
                except:
                    __print__(dbg.ERR, "HTTP Error or something")
                    __print__(dbg.ERR, "HTTP error or extraction failure.")
                    self.categories = ["empty"]
                self.display_categories()
                self.current = self.categories.keys()[0]
                __print__(dbg.STAT, "Use first category as current =", self.current)
                self.load(self.current)
        
            # load current category

Modified channels/bookmarks.py from [abc893deda] to [26db498411].

144
145
146
147
148
149
150
151

152
153
154
155
156
157
158
144
145
146
147
148
149
150

151
152
153
154
155
156
157
158







-
+







        return self.currentcat()
        
        
    # update bookmarks from freshly loaded streams data
    def heuristic_update(self, updated_channel, updated_category):

        if not conf.heuristic_bookmark_update: return
        __print__(dbg.ERR, "heuristic bookmark update")
        __print__(dbg.PROC, "heuristic bookmark update")
        save = 0
        fav = self.streams["favourite"]

        # First we'll generate a list of current bookmark stream urls, and then
        # remove all but those from the currently UPDATED_channel + category.
        # This step is most likely redundant, but prevents accidently re-rewriting
        # stations that are in two channels (=duplicates with different PLS urls).

Modified channels/internet_radio.py from [617008d44a] to [2353be4e7b].

138
139
140
141
142
143
144
145

146
147
148
149
150
151
152
138
139
140
141
142
143
144

145
146
147
148
149
150
151
152







-
+







                        "title": (title or "").strip().replace("\n", " "),
                        "playing": (playing or "").strip().replace("\n", " "),
                        "bitrate": int(bitrate or 0),
                        "listeners": int(listeners or 0),
                        "format": "audio/mpeg", # there is no stream info on that, but internet-radio.org.uk doesn't seem very ogg-friendly anyway, so we assume the default here
                    })
                else:
                    __print__(dbg.ERR, "rx missed", div)
                    __print__(dbg.DATA, "Regex couldn't decipher entry:", div)
        return r


    # DOM traversing
    def with_dom(self, html_list):
        __print__(dbg.PROC, "internet-radio, dom")
        rx_numbers = re.compile("(\d+)")

Modified channels/shoutcast.py from [645b712bff] to [05b00913ff].

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
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







-
+











-
+







        pass
        

    # downloads stream list from shoutcast for given category
    def update_streams(self, cat):

        if (cat not in self.catmap):
            __print__( dbg.ERR, "nocat" )
            __print__( dbg.ERR, "Category not in known map.", cat )
            return []
        id = self.catmap[cat]

        # page
        url = "http://www.shoutcast.com/Home/BrowseByGenre"
        params = { "genrename": cat }
        referer = None
        try:
            json = http.get(url, params=params, referer=referer, post=1, ajax=1)
            json = json_decode(json)
        except:
            __print__(db.ERR, "HTTP request or JSON decoding failed. Outdated python/requests perhaps.")
            __print__(dbg.ERR, "HTTP request or JSON decoding failed. Outdated python/requests perhaps.")
            return []
        self.parent.status(0.75)

        # remap JSON
        entries = []
        for e in json:
            entries.append({

Modified config.py from [f2de7a5aec] to [2154593dfb].

382
383
384
385
386
387
388
389

390
391
392
393
394
395
396
382
383
384
385
386
387
388

389
390
391
392
393
394
395
396







-
+







    """, re.X)




# wrapper for all print statements
def __print__(*args):
    if "debug" in conf and conf.debug:
    if "debug" in conf and conf.debug or args[0] == dbg.ERR:
        print(" ".join([str(a) for a in args]))


# error colorization
dbg = type('obj', (object,), {
    "ERR":  r"[ERR]",  # red    ERROR
    "INIT": r"[INIT]", # red    INIT ERROR

Modified help/index.page from [0fcf8063a0] to [03dacee74b].

1
2
3

4
5
6
7
8
9
10
1
2

3
4
5
6
7
8
9
10


-
+







<!--
# title: streamtuner2 mallard help pages
# pack: *.page, img/*
# pack: *.page=../../doc/streamtuner2/help/, img=../../doc/streamtuner2/help/
-->
<page	xmlns="http://projectmallard.org/1.0/"
	type="guide"
	id="index">

	<info>
	<credit type="author"><name>Mario Salzer</name></credit>