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

⌈⌋ ⎇ branch:  streamtuner2


Check-in [0169107f28]

Overview
Comment:Support alternative lists for #depends: fields, allow #alias: names, and supply `python` builtin for pluginconf.dependency() checker.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 0169107f2880a6fa889296071f18bb9e54398573
User & Date: mario on 2016-12-27 21:23:36
Other Links: manifest | tags
Context
2016-12-27
22:37
bump version, pluginconf 0.6.5 check-in: 878941a044 user: mario tags: trunk
21:23
Support alternative lists for #depends: fields, allow #alias: names, and supply `python` builtin for pluginconf.dependency() checker. check-in: 0169107f28 user: mario tags: trunk
21:22
Fix a few typos. check-in: 0044d5a6c1 user: mario tags: trunk
Changes

Modified pluginconf.py from [69c2b6a573] to [8f7b9c3abc].

395
396
397
398
399
400
401
402
403









404
405
406
407

408


409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438



439
440
441
442
443
444
445
446

447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466

467
468
469
470
471
472
473
#
# In practice there's little need for full-blown dependency resolving
# for application-level modules.
#
class dependency(object):

    # prepare list of known plugins and versions
    def __init__(self, core={}):
        self.have = {}.update(core)









        self.have = all_plugin_meta()
        # dependencies on core modules are somewhat more interesting:
        for name in ("st2", "uikit", "config", "action"):
            self.have[name] = plugin_meta(module=name, extra_base=["config"])

        self.have["streamtuner2"] = self.have["st2"]


    have = {}

    # depends:
    def depends(self, plugin):
        if plugin.get("depends"):
            d = self.deps(plugin["depends"])
            if not self.cmp(d, self.have):
                return False
        return True

    # basic list pre-filtering (skip __init__, filter by api:,
    # exclude installed & same-version plugins)
    def valid(self, newpl):
        id = newpl.get("$name", "__invalid")
        have_ver = self.have.get(id, {}).get("version", "0")
        if id.find("__") == 0:
            pass
        elif newpl.get("api") not in ("python", "streamtuner2"):
            pass
        elif set((newpl.get("status"), newpl.get("priority"))).intersection(set(("obsolete", "broken"))):
            pass
        elif have_ver >= newpl.get("version", "0.0"):
            pass
        else:
            return True

    # Split trivial "pkg, mod >= 1, uikit < 4.0" list
    def deps(self, dep_str):
        d = []
        for dep in re.split(r"\s*[,;]+\s*", dep_str):



            # skip deb:pkg-name, rpm:name, bin:name etc.
            if not len(dep) or dep.find(":") >= 0:
                continue
            # find comparison and version num
            dep += " >= 0"
            m = re.search(r"([\w.-]+)\s*([>=<!~]+)\s*([\d.]+([-~.]\w+)*)", dep)
            if m and m.group(2):
                d.append([m.group(i) for i in (1, 2, 3)])

        return d

    # Do actual comparison
    def cmp(self, d, have):
        r = True
        for name, op, ver in d:
            # skip unknown plugins, might be python module references
            if not have.get(name, {}).get("version"):
                continue
            curr = have[name]["version"]
            tbl = {
                ">=": curr >= ver,
                "<=": curr <= ver,
                "==": curr == ver,
                ">":  curr > ver,
                "<":  curr < ver,
                "!=": curr != ver,
            }
            #log.VERSION_COMPARE(name, " → (", curr, op, ver, ") == ", r)
            r &= tbl.get(op, True)

        return r


# Add plugin defaults to conf.* store
# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
# Utility function which applies plugin meta data to a config
# store. Which in the case of streamtuner2 is really just a







|
|
>
>
>
>
>
>
>
>
>
|
|
|

>
|
>
>
|




|
|



















|

|
|
>
>
>
|
|
|
|
|
|
|
|
>
|

















<

>







395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480

481
482
483
484
485
486
487
488
489
#
# In practice there's little need for full-blown dependency resolving
# for application-level modules.
#
class dependency(object):

    # prepare list of known plugins and versions
    def __init__(self, add={}, core=["st2", "uikit", "config", "action"]):
        self.have = {
            "python": { "version": sys.version }
        }
        # inject virtual modules
        for name, meta in add.items():
            if isinstance(meta, bool): meta = 1 if meta else -1
            if isinstance(meta, tuple): meta = ".".join(str(n) for n in meta)
            if isinstance(meta, (int, float, str)): meta = {"version": str(meta)}
            self.have[name] = meta
        # read plugins/*
        self.have.update(all_plugin_meta())
        # add core modules
        for name in core:
            self.have[name] = plugin_meta(module=name, extra_base=["config"])
        # aliases
        for name, meta in self.have.items():
            if meta.get("alias"):
                for alias in re.split("\s*[,;]\s*", meta["alias"]):
                    self.have[alias] = self.have[name]

    # depends:
    def depends(self, plugin):
        if plugin.get("depends"):
            dep_cmp = self.deps(plugin["depends"])
            if not (True in [self.cmp(alt_cmp, self.have) for alt_cmp in dep_cmp]):
                return False
        return True

    # basic list pre-filtering (skip __init__, filter by api:,
    # exclude installed & same-version plugins)
    def valid(self, newpl):
        id = newpl.get("$name", "__invalid")
        have_ver = self.have.get(id, {}).get("version", "0")
        if id.find("__") == 0:
            pass
        elif newpl.get("api") not in ("python", "streamtuner2"):
            pass
        elif set((newpl.get("status"), newpl.get("priority"))).intersection(set(("obsolete", "broken"))):
            pass
        elif have_ver >= newpl.get("version", "0.0"):
            pass
        else:
            return True

    # Split trivial "pkg | alt, mod >= 1, uikit < 4.0" string into nested list [[dep],[alt,alt],[dep]]
    def deps(self, dep_str):
        dep_cmp = []
        for alt_str in re.split(r"\s*[,;]+\s*", dep_str):
            alt_cmp = []
            # split alternatives |
            for part in re.split(r"\s*\|+\s*", alt_str):
                # skip deb:pkg-name, rpm:name, bin:name etc.
                if not len(part) or part.find(":") >= 0:
                    continue
                # find comparison and version num
                part += " >= 0"
                m = re.search(r"([\w.-]+)\s*([>=<!~]+)\s*([\d.]+([-~.]\w+)*)", part)
                if m and m.group(2):
                    alt_cmp.append([m.group(i) for i in (1, 2, 3)])
            dep_cmp.append(alt_cmp)
        return dep_cmp

    # Do actual comparison
    def cmp(self, d, have):
        r = True
        for name, op, ver in d:
            # skip unknown plugins, might be python module references
            if not have.get(name, {}).get("version"):
                continue
            curr = have[name]["version"]
            tbl = {
                ">=": curr >= ver,
                "<=": curr <= ver,
                "==": curr == ver,
                ">":  curr > ver,
                "<":  curr < ver,
                "!=": curr != ver,
            }

            r &= tbl.get(op, True)
            #print "log.VERSION_COMPARE: ", name, " → (", curr, op, ver, ") == ", r
        return r


# Add plugin defaults to conf.* store
# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
# Utility function which applies plugin meta data to a config
# store. Which in the case of streamtuner2 is really just a

Modified st2.py from [83bfadb12a] to [74554eebfe].

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

17
18
19
20
21
22
23
24
#!/usr/bin/env python
# encoding: UTF-8
# api: python
# type: application
# title: streamtuner2
# description: Directory browser for internet radio, audio and video streams
# version: 2.2.0-rc3
# state: stable
# author: Mario Salzer <mario@include-once.org>
# license: Public Domain
# url: http://freshcode.club/projects/streamtuner2
# config:  
#   { type: env, name: HTTP_PROXY, description: proxy for HTTP access }
#   { type: env, name: XDG_CONFIG_HOME, description: relocates user .config subdirectory }
# category: sound
# depends: pygtk | gi, threading, requests, pyquery, lxml

# id: streamtuner2
# pack: *.py, gtk3.xml.gz, bin, channels/__init__.py, bundle/*.py, CREDITS, help/index.page,
#   streamtuner2.desktop, README, help/streamtuner2.1=/usr/share/man/man1/,
#   NEWS=/usr/share/doc/streamtuner2/, icon.png=/usr/share/pixmaps/streamtuner2.png
# architecture: all
#
# Streamtuner2 is a GUI for browsing internet radio directories,
# music collections, and video services - grouped by genres or






|









>
|







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
#!/usr/bin/env python
# encoding: UTF-8
# api: python
# type: application
# title: streamtuner2
# description: Directory browser for internet radio, audio and video streams
# version: 2.2.0-rc4
# state: stable
# author: Mario Salzer <mario@include-once.org>
# license: Public Domain
# url: http://freshcode.club/projects/streamtuner2
# config:  
#   { type: env, name: HTTP_PROXY, description: proxy for HTTP access }
#   { type: env, name: XDG_CONFIG_HOME, description: relocates user .config subdirectory }
# category: sound
# depends: pygtk | gi, threading, requests, pyquery, lxml
# alias: streamtuner2, main
# id: st2
# pack: *.py, gtk3.xml.gz, bin, channels/__init__.py, bundle/*.py, CREDITS, help/index.page,
#   streamtuner2.desktop, README, help/streamtuner2.1=/usr/share/man/man1/,
#   NEWS=/usr/share/doc/streamtuner2/, icon.png=/usr/share/pixmaps/streamtuner2.png
# architecture: all
#
# Streamtuner2 is a GUI for browsing internet radio directories,
# music collections, and video services - grouped by genres or