generated from itdominator/Python-With-Gtk-Template
94 lines
4.3 KiB
Python
94 lines
4.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
from __future__ import print_function, unicode_literals
|
|
|
|
import itertools as it, operator as op, functools as ft
|
|
import re
|
|
|
|
|
|
lookup_types = {
|
|
'sink': 'sink_list', 'source': 'source_list',
|
|
'sink-input': 'sink_input_list', 'source-output': 'source_output_list' }
|
|
lookup_types.update(it.chain.from_iterable(
|
|
((v, lookup_types[k]) for v in v) for k,v in
|
|
{ 'source': ['src'], 'sink-input': ['si', 'playback', 'play'],
|
|
'source-output': ['so', 'record', 'rec', 'mic'] }.items() ))
|
|
|
|
lookup_key_defaults = dict(
|
|
# No default keys for type = no implicit matches for that type
|
|
sink_input_list=[ # match sink_input_list objects with these keys by default
|
|
'media.name', 'media.icon_name', 'media.role',
|
|
'application.name', 'application.process.binary', 'application.icon_name' ] )
|
|
|
|
|
|
def pulse_obj_lookup(pulse, obj_lookup, prop_default=None):
|
|
'''Return set of pulse object(s) with proplist values matching lookup-string.
|
|
|
|
Pattern syntax:
|
|
[ { 'sink' | 'source' | 'sink-input' | 'source-output' } [ / ... ] ':' ]
|
|
[ proplist-key-name (non-empty) [ / ... ] ':' ] [ ':' (for regexp match) ]
|
|
[ proplist-key-value ]
|
|
|
|
Examples:
|
|
- sink:alsa.driver_name:snd_hda_intel
|
|
Match sink(s) with alsa.driver_name=snd_hda_intel (exact match!).
|
|
- sink/source:device.bus:pci
|
|
Match all sinks and sources with device.bus=pci.
|
|
- myprop:somevalue
|
|
Match any object (of all 4 supported types) that has myprop=somevalue.
|
|
- mpv
|
|
Match any object with any of the "default lookup props" (!!!) being equal to "mpv".
|
|
"default lookup props" are specified per-type in lookup_key_defaults above.
|
|
For example, sink input will be looked-up by media.name, application.name, etc.
|
|
- sink-input/source-output:mpv
|
|
Same as above, but lookup streams only (not sinks/sources).
|
|
Note that "sink-input/source-output" matches type spec, and parsed as such, not as key.
|
|
- si/so:mpv
|
|
Same as above - see aliases for types in lookup_types.
|
|
- application.binary/application.icon:mpv
|
|
Lookup by multiple keys with "any match" logic, same as with multiple object types.
|
|
- key\/with\/slashes\:and\:colons:somevalue
|
|
Lookup by key that has slashes and colons in it.
|
|
"/" and ":" must only be escaped in the proplist key part, used as-is in values.
|
|
Backslash itself can be escaped as well, i.e. as "\\".
|
|
- module-stream-restore.id:sink-input-by-media-role:music
|
|
Value has ":" in it, but there's no need to escape it in any way.
|
|
- device.description::Analog
|
|
Value lookup starting with : is interpreted as a regexp,
|
|
i.e. any object with device.description *containing* "Analog" in this case.
|
|
- si/so:application.name::^mpv\b
|
|
Return all sink-inputs/source-outputs ("si/so") where
|
|
"application.name" proplist value matches regexp "^mpv\b".
|
|
- :^mpv\b
|
|
Regexp lookup (stuff starting with "mpv" word) without type or key specification.
|
|
|
|
For python2, lookup string should be unicode type.
|
|
"prop_default" keyword arg can be used to specify
|
|
default proplist value for when key is not found there.'''
|
|
|
|
# \ue000-\uf8ff - private use area, never assigned to symbols
|
|
obj_lookup = obj_lookup.replace('\\\\', '\ue000').replace('\\:', '\ue001')
|
|
obj_types_re = '({0})(/({0}))*'.format('|'.join(lookup_types))
|
|
m = re.search(
|
|
( r'^((?P<t>{}):)?'.format(obj_types_re) +
|
|
r'((?P<k>.+?):)?' r'(?P<v>.*)$' ), obj_lookup, re.IGNORECASE )
|
|
if not m: raise ValueError(obj_lookup)
|
|
lookup_type, lookup_keys, lookup_re = op.itemgetter('t', 'k', 'v')(m.groupdict())
|
|
if lookup_keys:
|
|
lookup_keys = list(
|
|
v.replace('\ue000', '\\\\').replace('\ue001', ':').replace('\ue002', '/')
|
|
for v in lookup_keys.replace('\\/', '\ue002').split('/') )
|
|
lookup_re = lookup_re.replace('\ue000', '\\\\').replace('\ue001', '\\:')
|
|
obj_list_res, lookup_re = list(), re.compile( lookup_re[1:]
|
|
if lookup_re.startswith(':') else '^{}$'.format(re.escape(lookup_re)) )
|
|
for k in set( lookup_types[k] for k in
|
|
(lookup_type.split('/') if lookup_type else lookup_types.keys()) ):
|
|
if not lookup_keys: lookup_keys = lookup_key_defaults.get(k)
|
|
if not lookup_keys: continue
|
|
obj_list = getattr(pulse, k)()
|
|
if not obj_list: continue
|
|
for obj, k in it.product(obj_list, lookup_keys):
|
|
v = obj.proplist.get(k, prop_default)
|
|
if v is None: continue
|
|
if lookup_re.search(v): obj_list_res.append(obj)
|
|
return set(obj_list_res)
|