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

โŒˆโŒ‹ โŽ‡ branch:  streamtuner2


Check-in [21d6d1cf4b]

Overview
Comment:Basic rewrites to transition to fully plugin meta data capable implementation.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 21d6d1cf4bec4425a0f7f0919f9b0df1ba354b13
User & Date: mario on 2015-03-28 07:33:09
Other Links: manifest | tags
Context
2015-03-28
07:34
Minor additions, more cross references, and Mallard note icons. Document Jamendo plugin options. check-in: 89ba7b5c8e user: mario tags: trunk
07:33
Basic rewrites to transition to fully plugin meta data capable implementation. check-in: 21d6d1cf4b user: mario tags: trunk
07:32
Moved `bookmarks` channel into plugin. Implemented plugin .meta data consumption to replace .config = [] builtins. (Still need to rescan disabled channel/feature plugins later..) check-in: 9de894c13c user: mario tags: trunk
Changes

Modified channels/file.py from [f92686b376] to [4d4dd4f9cc].

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






-
+


+
+
+













-
+






-

+


-
+







#
# api: streamtuner2
# title: File browser
# description: Displays mp3/oggs or m3u/pls files from local media file directories.
# type: channel
# category: media
# version: 0.0
# version: 0.1
# priority: optional
# depends: mutagen
# config:  
#   {name:"file_browser_dir", "type":"text", "value": "~/Music, /media/music", "description":"list of directories to scan for audio files"},
#   {name:"file_browser_ext", "type":"text", "value":"mp3,ogg, m3u,pls,xspf, avi,flv,mpg,mp4", "description":"file type filter"},
#
#
# Local file browser.
#
#



# modules
import os
import re

from channels import *
from config import conf
from config import *

# ID3 libraries
try:
    from mutagen import File as get_meta
except:
    try:
        print("just basic ID3 support")
        from ID3 import ID3
        __print__(dbg.INFO, "Just basic ID3 support")
        get_meta = lambda fn: dict([(k.lower(),v) for k,v in ID3(fn).iteritems()])
    except:
        print("you are out of luck in regards to mp3 browsing")
        __print__(dbg.INIT, "You are out of luck in regards to mp3 browsing. No ID3 support.")
        get_meta = lambda *x: {}


# work around mutagens difficult interface
def mutagen_postprocess(d):
    if d.get("TIT2"):
        return {
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
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







-


-


-

-
-
-
-









-
+










# file browser / mp3 directory listings
class file (ChannelPlugin):

    # info
    api = "streamtuner2"
    module = "file"
    title = "file browser"
    version = -0.5
    listtype = "url/file"
    
    
    # data
    config = [
        {"name":"file_browser_dir", "type":"text", "value":os.environ["HOME"]+"/Music, /media/music", "description":"list of directories to scan for audio files"},
        {"name":"file_browser_ext", "type":"text", "value":"mp3,ogg, m3u,pls,xspf, avi,flv,mpg,mp4", "description":"file type filter"},
    ]
    streams = {}
    categories = []
    dir = []
    ext = []
    
    # display
    datamap = [ # coltitle   width	[ datasrc key, type, renderer, attrs ]	[cellrenderer2], ...
           ["",		20,	["state",	str,  "pixbuf",	{}],	],
           ["Genre",	65,	['genre',	str,	"t",	{"editable":8}],	],
           ["File",	160,	["filename",	str,	"t",	{"strikethrough":11, "cell-background":12, "cell-background-set":13}],	],
           ["File",	160,	["filename",	str,	"t",	{"strikethrough":10, "cell-background":11, "cell-background-set":12}],	],
           ["Title",	205,	["title",	str,    "t",	{"editable":8}], ],
           ["Artist",	125,	["artist",	str,	"t",	{"editable":8}],	],
           ["Album", 	125,	["album",	str,	"t",	{"editable":8}],	],
           ["Bitrate",	35,	["bitrate",	int,	"t",	{}],	],
           ["Format",	50,	["format",	str,	None,	{}],	],
           [False,	0,	["editable",	bool,	None,	{}],	],
           [False,	0,	["favourite",	bool,	None,	{}],	],

Modified channels/global_key.py from [3a244b8b88] to [8937954709].

25
26
27
28
29
30
31

32
33
34
35
36
37
38
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39







+









# register a key
class global_key(object):

    module = "global_key"
    title = "keyboard shortcut"
    meta = plugin_meta()
    
    config = [
        dict(name="switch_key", type="text", value="XF86Forward", description="global key for switching radio"),
        dict(name="switch_channel", type="text", value="bookmarks:favourite", description="station list to alternate in"),
        dict(name="switch_random", type="boolean", value=0, description="pick random channel, instead of next"),
    ]
    last = 0

Modified channels/history.py from [d33de09886] to [07819073c4].

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








+
+









-
+









-
-
+
+
-
-
-
-
-
-
-
-
-
-







+







#
# api: streamtuner2
# title: History
# description: List recently played stations under favourites > history.
# version: 1.0
# type: category
# category: ui
# priority: optional
# config:  { name: history,  type: int,  value: 20,  description: Number of last played streams to keep in history list.,  category: limit }
#
# 
# Lists last activated streams in a new [history] tab in the favourites
# channel.
#
#
#



from config import conf, __print__, dbg
from config import *
from channels import *



class history:

    # plugin info
    module = "history"
    title = "History"
    
    
    meta = plugin_meta()

    # configuration settings
    config = [
        {
            "name": "history",
            "type": "int",
            "value": "20",
            "description": "Number of last played streams to keep in history list.",
            "category": "limit"
        }
    ]
    
    # store
    bm = None


    # hook up to main tab
    def __init__(self, parent):
        self.config = self.meta["config"]

        # keep reference to main window    
        self.bm = parent.channels["bookmarks"]

        # create category
        self.bm.add_category("history");
        self.bm.reload_if_current(self.module)

Modified channels/jamendo.py from [949c1eaeb0] to [fc2a3bf698].

1
2
3
4
5
6
7
8
9



10
11
12
13
14
15
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19









+
+
+








# api: streamtuner2
# title: Jamendo
# description: A license-free music collection and artist hub.
# type: channel
# version: 2.2
# category: radio
# depends: json
# priority: default
# config: 
#    { name: "jamendo_stream_format", type: "select", value: "ogg", 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." }
#
#
# Now utilizes the Jamendo /v3.0/ API.
#
# Radio station lists are fixed for now. Querying the API twice per station
# doesn't seem overly sensible.
#
# Tracks are queried by genre, where currently there's just a small built-in

Modified channels/links.py from [fb5b65ac66] to [4b9e35f7fc].

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







+












+







#
# Simply adds a "links" entry in bookmarks tab, where known channels
# and some others are listed with homepage links.
#
#


from config import *
from channels import *
import copy



# hooks into main.bookmarks
class links (object):

    # plugin info
    module = "links"
    title = "Links"
    version = 0.1
    meta = plugin_meta()
    
    
    # configuration settings
    config = [    ]
    
    # list
    streams = [    ]

Modified channels/surfmusik.py from [02e3c00437] to [f86a97dfce].

10
11
12
13
14
15
16
17

18
19
20
21
22
23
24
10
11
12
13
14
15
16

17
18
19
20
21
22
23
24







-
+







# recognizes: max_streams
#
# This plugin comes in German (SurfMusik) and English (SurfMusic) variations.
# It provides a vast collection of international stations and genres.
# While it's not an open source project, most entries are user contributed.
#
# They do have a Windows client, hencewhy it's even more important for
# streamtuner2 to support it on other plattforms.
# streamtuner2 to support it on other platforms.
#
# TV stations don't seem to work mostly. And loading the webtv/ pages would
# be somewhat slow (for querying the actual mms:// streams).
#
#
#

Modified channels/timer.py from [cb57b7822f] to [2de8202f62].

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







-
+















+







#
# Programmed events are visible in "timer" under the "bookmarks" channel. Times
# are stored in the description field, and can thus be edited. However, after editing
# times manually, streamtuner2 must be restarted for the changes to take effect.
#


from config import __print__, dbg
from config import *
from channels import *
import kronos
from mygtk import mygtk
from action import action
import copy
import re



# timed events (play/record) within bookmarks tab
class timer:

    # plugin info
    module = "timer"
    title = "Timer"
    meta = plugin_meta()
    
    
    # configuration settings
    config = [
    ]
    timefield = "playing"
    

Modified channels/xiph.py from [7363e5b0c8] to [a46d943cfe].

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16









17
18
19
20
21
22
23

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








-






+
+
+
+
+
+
+
+
+







#
# encoding: UTF-8
# api: streamtuner2
# title: Xiph.org
# description: ICEcast radio directory. Now utilizes a cached JSON API.
# type: channel
# category: radio
# version: 0.3
# priority: standard
#
#
# Xiph.org maintains the Ogg streaming standard and Vorbis audio compression
# format, amongst others. The ICEcast server is an alternative to SHOUTcast.
#
# It meanwhile provides a JSOL dump, which is faster to download and process.
# So we'll use that over the older yp.xml. (Sadly it also doesn't output
# homepage URLs, listeners, etc.)
#
# Xiphs JSON is a horrible mysqldump concatenation, not parseable. Thus it's
# refurbished on api.io for consumption. Which also provides compressed HTTP
# transfers and category slicing.
#
# Xiph won't be updating the directory for another while. The original feature
# request is now further delayed as summer of code project:
# ยท https://trac.xiph.org/ticket/1958
# ยท https://wiki.xiph.org/Summer_of_Code_2015#Stream_directory_API
#
#



# streamtuner2 modules
from config import conf
39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
47
48
49
50
51
52
53

54
55
56
57
58
59
60
61







-
+







class xiph (ChannelPlugin):

        # desc
        api = "streamtuner2"
        module = "xiph"
        title = "Xiph.org"
        homepage = "http://dir.xiph.org/"
        #base_url = "http://api.dir.xiph.org/"
        #xml_url = "http://dir.xiph.org/yp.xml"
        json_url = "http://api.include-once.org/xiph/cache.php"
        listformat = "url/http"
        config = [
           {"name":"xiph_min_bitrate", "value":64, "type":"int", "description":"minimum bitrate, filter anything below", "category":"filter"}
        ]
        has_search = True