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

⌈⌋ branch:  streamtuner2


Diff

Differences From Artifact [2154593dfb]:

To Artifact [af23ce772a]:


1
2
3
4
5
6
7






8
9
10
11
12
13
14
1
2
3
4
5
6

7
8
9
10
11
12
13
14
15
16
17
18
19






-
+
+
+
+
+
+






#
# encoding: UTF-8
# api: streamtuner2
# type: class
# title: global config object
# description: reads ~/.config/streamtuner/*.json files
# config: {type:var, name:z, description:v}
# config:
#    { arg: -d,     type: str,      name: plugin[],  description: omit plugin from initialization  }
#    { arg: --gtk3, type: boolean,  name: gtk3,      description: use gtk3 interface }
#    { arg: -D,     type: boolean,  name: debug,     description: enable debug messages on console }
#    { arg: action, type: str*,     name: action[],  description: commandline actions }
#    { arg: -x,     type: boolean,  name: exit,      description: terminate right away }
#
# In the main application or module files which need access
# to a global conf.* object, just import this module as follows:
#
#   from config import *
#
# Here conf is already an instantiation of the underlying
27
28
29
30
31
32
33

34
35
36
37
38
39
40
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46







+






import platform
import re
from compat2and3 import gzip_decode, find_executable
import zlib
import zipfile
import inspect
import pkgutil
import argparse

# export symbols
__all__ = ["conf", "__print__", "dbg", "plugin_meta", "module_list", "get_data", "find_executable"]


#-- create a stub instance of config object
conf = object()
48
49
50
51
52
53
54

55
56
57
58
59
60
61
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68







+






# Global configuration store
#
# Autointializes itself on startup, makes conf.vars available.
# Also provides .load() and .save() for JSON data/cache files.
#
class ConfigDict(dict):

    args = {}

    # start
    def __init__(self):
    
        # object==dict means conf.var is conf["var"]
        self.__dict__ = self  # let's pray this won't leak memory due to recursion issues
73
74
75
76
77
78
79



80
81
82
83
84
85
86
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96







+
+
+






                del last["share"]
            self.update(last)
            self.migrate()
        # store defaults in file
        else:
            self.save("settings")
            self.firstrun = 1
        
        # add argv
        self.args = self.init_args(argparse.ArgumentParser())


    # some defaults
    def defaults(self):
        self.play = {
           "audio/mpeg": self.find_player(),
           "audio/ogg": self.find_player(),
245
246
247
248
249
250
251
252








































253
254
255
256
257
258
259
255
256
257
258
259
260
261

262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
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







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






                except:
                     netrc = parser(self.xdg() + "/netrc").hosts
            except:
                pass
        for server in varhosts:
            if server in netrc:
                return netrc[server]
        

    # Use config: definitions for argv extraction
    def init_args(self, ap):
        for opt in plugin_meta(frame=0).get("config"):
            kwargs = self.argparse_map(opt)
            #print kwargs
            if kwargs:
                args = kwargs["args"]
                del kwargs["args"]
                ap.add_argument(*args, **kwargs)
        return ap.parse_args()

    # Transform config: description into quirky ArgumentParser list
    def argparse_map(self, opt):
        if not ("arg" in opt and opt["name"] and opt["type"]):
            return {}

        # Extract --flag names
        args = opt["arg"].split() + re.findall("-+\w+", opt["name"])

        # Prepare mapping options
        typing = re.findall("bool|str|\[\]|store|append|const", opt["type"])
        naming = re.findall("\[\]", opt["name"])
        name   = re.findall("(?<!-)\\b\\w+", opt["name"])
        nargs  = re.findall("\\b\d+\\b|\?|\*", opt["type"]) or [None]

        # Populate partially - ArgumentParser is highly fragile with combinations of named params
        kwargs = {
            "args": args,
            "dest": name[0] if not name[0] in args else None,
            "action": "append" if "[]" in (naming+typing) else ("store_true" if "bool" in typing else "store"),
            "nargs": nargs[0],
            "default": opt["value"],
           # "type":  int if "int" in typing else bool if "bool" in typing else str,
            "choices": opt["select"].split("|") if "select" in opt else None,
           # "required": "required" in opt,
            "help": opt["description"] or "",
        }
        return dict((k,w) for k,w in kwargs.items() if w is not None)



# Retrieve content from install path or pyzip archive (alias for pkgutil.get_data)
#
def get_data(fn, decode=False, gz=False, file_base="config"):
    try:
        bin = pkgutil.get_data(file_base, fn)