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

⌈⌋ ⎇ branch:  streamtuner2


Diff

Differences From Artifact [59ff23a453]:

To Artifact [cb67ede034]:


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
#
# encoding: UTF-8
# api: streamtuner2

# type: class
# title: global config object
# description: reads ~/.config/streamtuner/*.json files

#
# In the main application or module files which need access
# to a global conf object, just import this module as follows:
#
#   from config import conf
#
# Here conf is already an instantiation of the underlying
# Config class.
#


import os
import sys
import json
import gzip
import platform





# export symbols
__all__ = ["conf", "__print__", "dbg"]



#-- create a single instance of config object
conf = object()





>



>
















>
>
>



|







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
#
# encoding: UTF-8
# api: streamtuner2
#  .2
# type: class
# title: global config object
# description: reads ~/.config/streamtuner/*.json files
# config: {type:var, name:z, description:v}
#
# In the main application or module files which need access
# to a global conf object, just import this module as follows:
#
#   from config import conf
#
# Here conf is already an instantiation of the underlying
# Config class.
#


import os
import sys
import json
import gzip
import platform
import re
import zipfile
import inspect


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



#-- create a single instance of config object
conf = object()


42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
            self.__dict__ = self  # let's pray this won't leak memory due to recursion issues

            # prepare
            self.defaults()
            self.xdg()
            
            # runtime
            dirs = ["/usr/share/streamtuner2", "/usr/local/share/streamtuner2", sys.path[0], "."]
            self.share = [d for d in dirs if os.path.exists(d)][0]
            
            # settings from last session
            last = self.load("settings")
            if (last):
                self.update(last)
                self.migrate()
            # store defaults in file







<
|







47
48
49
50
51
52
53

54
55
56
57
58
59
60
61
            self.__dict__ = self  # let's pray this won't leak memory due to recursion issues

            # prepare
            self.defaults()
            self.xdg()
            
            # runtime

            self.share = os.path.dirname(__file__)
            
            # settings from last session
            last = self.load("settings")
            if (last):
                self.update(last)
                self.migrate()
            # store defaults in file
201
202
203
204
205
206
207















































208
209
210
211
212
213
214
        def find_in_dirs(self, dirs, file):
            for d in dirs:
                if os.path.exists(d+"/"+file):
                    return d+"/"+file



















































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









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







205
206
207
208
209
210
211
212
213
214
215
216
217
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
253
254
255
256
257
258
259
260
261
262
263
264
265
        def find_in_dirs(self, dirs, file):
            for d in dirs:
                if os.path.exists(d+"/"+file):
                    return d+"/"+file



# Plugin meta data extraction
#
# Extremely crude version for Python and streamtuner2 plugin usage.
# Doesn't check top-level comment coherency.
# But supports plugins within python zip archives.
#
rx_zipfn  = re.compile(r"""^(.+\.(?:zip|pyz|pyzw|pyzip)(?:\.py)?)/(\w.*)$""")
rx_meta   = re.compile(r"""^ {0,4}# *([\w-]+): *(.+(\n *#  +(?![\w-]+:).+)*)$""", re.M)  # Python comments only
rx_lines  = re.compile(r"""\n *# """)        # strip multi-line prefixes
rx_config = re.compile(r"""[\{\<](.+?)[\}\>]""")     # extract only from JSOL/YAML scheme
rx_fields = re.compile(r"""["']?(\w+)["']?\s*[:=]\s*["']?([^,]+)(?<!["'])""")  # simple key: value entries
#
def plugin_meta(fn=None, frame=1, src=""):

    # filename of caller
    if not fn:
        fn = inspect.getfile(sys._getframe(frame))

    # within zip archive?
    zip = rx_zipfn.match(fn)
    if zip and zipfile.is_zipfile(zip.group(1)):
        src = zipfile.ZipFile(zip.group(1), "r").read(zip.group(2))
    else:
        src = open(fn).read(4096)

    # defaults
    meta = {
        "fn": fn,
        "id": os.path.basename(fn).replace(".py", "")
    }
    # extraction
    for field in rx_meta.findall(src):
        meta[field[0]] = rx_lines.sub("", field[1])

    # unpack config: structures
    meta["config"] = [
        dict([field for field in rx_fields.findall(entry)])
        for entry in rx_config.findall(meta.get("config", ""))
    ]
        
    return meta







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


227
228
229
230
231
232
233
234
235
236
237


   
#-- actually fill global conf instance
conf = ConfigDict()
if conf:
    __print__(dbg.PROC, "ConfigDict() initialized")












<
<
<
278
279
280
281
282
283
284
285





   
#-- actually fill global conf instance
conf = ConfigDict()
if conf:
    __print__(dbg.PROC, "ConfigDict() initialized")