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

⌈⌋ branch:  streamtuner2


Diff

Differences From Artifact [13bc7ee950]:

  • File channels/jamendo.py — part of check-in [7149d92fe1] at 2015-04-18 17:19:38 on branch trunk — Updated Jamendo plugin audioformat and listformat descriptors. Attempted to use v3.0 API for playlist tracks. Still no playlist API endpoint. So using a separate track requests now. Implemented a new action/playlist_convert URL extractor, which shall henceforth be known as "jamj" (JamJibberish). Fixed XML url extraction in regex mode, trivial backslash deescaping for JSON formats; and fixed multiply URL bug by copying row{} dict during conversion. (user: mario, size: 13306) [annotate] [blame] [check-ins using]

To Artifact [3c40a66c05]:


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

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









-
+











-
+
+

-
+
-
+

-
+
-
+
+

-
-
+
+
+
+







# encoding: UTF-8
# api: streamtuner2
# title: Jamendo
# description: A license-free music collection and artist hub.
# type: channel
# version: 2.2
# category: radio
# url: http://jamendo.com/
# depends: json
# config: 
#    { name: jamendo_stream_format, value: ogg,  type: select,  select: "ogg=Ogg Vorbis|mp32=MP3, 192vbr|mp31=MP3, 96kbps|flac=Xiph FLAC",  description: "Default streaming audio format. Albums and playlists still return Vorbis mostly for best quality." }
#    { name: jamendo_stream_format, value: ogg,  type: select,  select: "ogg=Ogg Vorbis, 112kbit/s, (HD)|mp32=MP3, 192kbit/s VBR|mp31=MP3, 96kbit/s|flac=Xiph FLAC, ≳600kbit/s",  description: "Default streaming audio format for tracks, albums and playlists. Radios are just MP3." }
#    { name: jamendo_image_size,    value: 50,   type: select,  select: "25=25px|35=35px|50=50px|55=55px|60=60px|65=65px|70=70px|75=75px|85=85px|100=100px|130=130px|150=150px|200=200px|300=300px",  description: "Preview images size (height and width) for albums or tracks." }
#    { name: jamendo_count,         value: 1,    type:text,     description: "How many result sets (200 entries each) to retrieve." }
# priority: default
# png:
#   iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAwNJREFUOI1lk0toXGUYhp/v/8+ZzDQzmdZMZ8YSkxIQCWTTYsALLsQsUsWFQgJeEJQqIigUvBK0FBQULIJKMEKrFjdOW9xoW1AbRdRCEYtd2GCxxUQbk8bMrTNnzpn//1xMLYLv6l2877N7oBcB0GMY
#   IAUY/p9w/glCrVzby3+LVqaxM4dx+inQV7KIHcKYAbzG4FYgqcmudYBAF+jKnT2QAExPYysVHCdKRQgewwRT0B1FfQboYsLLeE7hk4Oo/iD3rFit4GQGRCsYwJMt7yQVfrS6Go3HUQtvskiwCWM82qlijGdbaUsbTV5G/X65+y+rCzgBRI+VbsSmvvltqVkuX78t6rvpKWtvuMsgOfHtJia66OPz
#   86564UtDkA2Lm/VJYF6mLgXo50X0xNAHy4cK2jk+diVe+1FVVX/57oIuL66qqmriVF23rZ1TjyTLH2YT/WJ4TY+XRgAMKqNgJ41vebbvTgeFneo6Tb567zPmHn+LxdPnCIxHTFrt2CtB/8BWqtVOAewDWgGDmHG0u1VtztjC7SLqRSUkM5Clsd5ibs/7nDzyLd51xfQPq82PKS5SsHfQAEMsIZG3
#   khgvJqUqBhFBnSeTS1O/XOPou0eoVxuIsfgkRDsqxBIQgaEVXyIyTVdvGFdbQlD1LibpJDTWG9w8NcHsgRfIb8ni4qbIxhISGU87+Z00BMTuDPhfg47b4X4+oHZ4VxiksxRHi9x3y71M7p5EjAWE6OxBkj/OaSqTgyvJUUIQncuDDx6qRsHHLqpHuR0PW3vbbCj5EYyAAq7dgMVPfHRytt2Kkv7i
#   5vAw0n1wZuFvFcDq/kGP5/X1tnm+2aiTLQy59MiE00wZ49okK2fNxsWfgnRfmvJA8D3E98tztTWdBtG9IPuw+tqgI+RRTLin1miNJ3EkRhQFxIRcl8+tgj9EN35VXqrV9G2MPIPvWfgv5I1Bh/pNBOGtYCeALEKM6nlc52uUP+XFDfRpRN7psa/ZeBWSAmJ99qrQGaANeJA3AUjpXhLZ1zsD+g+5
#   Cnd9pANrngAAAABJRU5ErkJggg==
#
# Now utilizes the Jamendo /v3.0/ API.
# Utilizes the Jamendo /v3.0/ API now completely. That means all tracks will
# be available as Ogg Vorbis per default.
#
# Radio station lists are fixed for now. Querying the API twice per station
# Radio stations are a fixed internal list. There's not much point in querying
# doesn't seem overly sensible.
# the API for them. (They're really just automated playlists, and MP3-only.)
#
# Tracks are queried by genre, where currently there's just a small built-in
# Genre lists for tracks are built-in, but give a good enough overview. There's
# tag list in ST2
# no "related" lookup possibility yet in our station lists. (Might be feasible
# as plugin though, via [load more] button etc...)
#
# Per default Ogg Vorbis is used as streaming format. Playlists and albums
# return as XSPF playlists.
# Per default Ogg Vorbis is used as streaming format. Track URLs can be played
# back directly. Playlists and albums now require a roundtrip over the action
# module to extract the JAMJson format into pls/m3u/xspf. (The previous v2 API
# retrieval is going to become inaccessible soon.)


import re
import ahttp as http
from config import conf, __print__, dbg
from channels import *
import json
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
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







-
+













+






# Seem to resolve to OGG Vorbis each.
#
class jamendo (ChannelPlugin):

    # control flags
    has_search = True
    base = "http://www.jamendo.com/en/"
    audioformat = "ogg"
    audioformat = "audio/ogg"
    listformat = "srv"
    api_base = "http://api.jamendo.com/v3.0/"
    cid = "49daa4f5"
    categories = []
    titles = dict( title="Title", playing="Album/Artist/User", bitrate=False, listeners=False )


    # refresh category list
    def update_categories(self):

        self.categories = [
            "radios",
            "playlists",
                ["feeds"],  # should go below `albums`, but looks nicer here
            "albums",
                ["newest"],
            "tracks",
                [
                "pop",
                "synthpop",
                "trashpop",
256
257
258
259
260
261
262
263


264
265
266
267
268
269
270
261
262
263
264
265
266
267

268
269
270
271
272
273
274
275
276







-
+
+






        return self.categories


    # retrieve category or search
    def update_streams(self, cat, search=None):

        entries = []
        fmt = self.stream_mime(conf.jamendo_stream_format)
        fmt = conf.jamendo_stream_format
        fmt_mime = self.stream_mime(fmt)
                
        # Static list of Radios
        if cat == "radios":
            for radio in ["BestOf", "Pop", "Rock", "Lounge", "Electro", "HipHop", "World", "Jazz", "Metal", "Soundtrack", "Relaxation", "Classical"]:
                entries.append({
                    "genre": radio,
                    "title": radio,
279
280
281
282
283
284
285
286

287
288
289
290
291
292
293
294

295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313






314
315
















316
317
318
319

320
321
322

323
324
325
326
327
328
329
330
331
332
333
334
335
336
337



338
339
340
341
342
343
344
285
286
287
288
289
290
291

292
293
294
295
296
297
298
299

300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316



317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343

344

345

346

347
348
349
350
351
352
353
354
355
356
357



358
359
360
361
362
363
364
365
366
367







-
+







-
+
















-
-
-
+
+
+
+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+
-

-
+
-











-
-
-
+
+
+






        elif cat == "playlists":
            for e in self.api(method="playlists", order="creationdate_desc"):
                entries.append({
                    "title": e["name"],
                    "playing": e["user_name"],
                    "homepage": e["shareurl"],
                    "extra": e["creationdate"],
                    "format": "audio/mpeg",
                    "format": fmt_mime,
                    #"listformat": "xspf", # deprecated
                    #"url": "http://api.jamendo.com/get2/stream/track/xspf/?playlist_id=%s&n=all&order=random&from=app-%s" % (e["id"], self.cid),
                    #"listformat": "href", # raw ZIP redirect
                    #"url": "http://api.jamendo.com/v3.0/playlists/file?client_id={}&audioformat=mp32&id={}".format(self.cid, e["id"]),
                    #"listformat": "href", # raw ZIP direct
                    #"url": e["zip"],
                    "listformat": "jamj",
                    "url": "http://api.jamendo.com/v3.0/playlists/tracks?client_id={}&audioformat=mp32&id={}".format(self.cid, e["id"]),
                    "url": "http://api.jamendo.com/v3.0/playlists/tracks?client_id={}&audioformat={}&id={}".format(self.cid, fmt, e["id"]),
                })

        # Albums
        elif cat in ["albums", "newest"]:
            if cat == "albums":
                order = "popularity_week"
            else:
                order = "releasedate_desc"
            for e in self.api(method = "albums/musicinfo", order = order, include = "musicinfo"):
                entries.append({
                    "genre": " ".join(e["musicinfo"]["tags"]),
                    "title": e["name"],
                    "playing": e["artist_name"],
                    "img": e["image"],
                    "homepage": e["shareurl"],
                    #"url": "http://api.jamendo.com/v3.0/playlists/file?client_id=%s&id=%s" % (self.cid, e["id"]),
                    "url": "http://api.jamendo.com/get2/stream/track/xspf/?album_id=%s&streamencoding=ogg2&n=all&from=app-%s" % (e["id"], self.cid),
                    "format": "audio/ogg",
                    "listformat": "xspf",
                    #"url": "http://api.jamendo.com/get2/stream/track/xspf/?album_id=%s&streamencoding=ogg2&n=all&from=app-%s" % (e["id"], self.cid),
                    #"format": "audio/ogg",
                    #"listformat": "xspf",
                    "url": "http://api.jamendo.com/v3.0/tracks?client_id={}&audioformat={}&album_id={}".format(self.cid, fmt, e["id"]),
                    "listformat": "jamj",
                    "format": fmt_mime,
                })
		
        # Feeds (News)
        elif cat == "feeds":
            for e in self.api(method="feeds", order="date_start_desc", target="notlogged"):
              if e.get("joinid") and e.get("subtitle"):
                print e
                entries.append({
                    "genre": e["type"],
                    "title": e["title"]["en"],
                    "playing": e["subtitle"]["en"],
                    "extra": e["text"]["en"],
                    "homepage": e["link"],
                    "format": fmt_mime,
                    "listformat": "jamj",
                    "url": "http://api.jamendo.com/v3.0/tracks?client_id={}&audioformat={}&album_id={}".format(self.cid, fmt, e["joinid"]),
                })

        # Genre list, or Search
        else:
            if cat:
                data = self.api(method = "tracks", order = "popularity_week", include = "musicinfo",
                data = self.api(method="tracks", order="popularity_week", include="musicinfo", fuzzytags=cat, audioformat=fmt)
                                fuzzytags = cat, audioformat = conf.jamendo_stream_format)
            elif search:
                data = self.api(method = "tracks", order = "popularity_week", include = "musicinfo",
                data = self.api(method="tracks", order="popularity_week", include="musicinfo", search=search, audioformat=fmt)
                                search = search, audioformat = conf.jamendo_stream_format)
            else:
                data = []
            for e in data:
                entries.append({
                    "lang": e["musicinfo"]["lang"],
                    "genre": " ".join(e["musicinfo"]["tags"]["genres"]),
                    "extra": ", ".join(e["musicinfo"]["tags"]["vartags"]),
                    "title": e["name"],
                    "playing": e["album_name"] + " / " + e["artist_name"],
                    "img": e["album_image"],
                    "homepage": e["shareurl"],
                    #"url": e["audio"],
                    "url": "http://storage-new.newjamendo.com/?trackid=%s&format=ogg2&u=0&from=app-%s" % (e["id"], self.cid),
                    "format": fmt,
                    "url": e["audio"],
                    #"url": "http://storage-new.newjamendo.com/?trackid=%s&format=ogg2&u=0&from=app-%s" % (e["id"], self.cid),
                    "format": fmt_mime,
                    "listformat": "srv",
                })
 
        # done    
        return entries