Esempio n. 1
0
def get_plugins_info(bundles):
    if len(bundles) == 0:
        raise Exception('get_plugins_info() - no bundles provided')

    world = lilv.World()

    # this is needed when loading specific bundles instead of load_all
    # (these functions are not exposed via World yet)
    lilv.lilv_world_load_specifications(world.me)
    lilv.lilv_world_load_plugin_classes(world.me)

    # load all bundles
    for bundle in bundles:
        # lilv wants the last character as the separator
        bundle = os.path.abspath(bundle)
        if not bundle.endswith(os.sep):
            bundle += os.sep

        # convert bundle string into a lilv node
        bundlenode = lilv.lilv_new_file_uri(world.me, None, bundle)

        # load the bundle
        world.load_bundle(bundlenode)

        # free bundlenode, no longer needed
        lilv.lilv_node_free(bundlenode)

    plugins = world.get_all_plugins()

    if plugins.size() == 0:
        raise Exception(
            'get_plugins_info() - selected bundles have no plugins')

    return [Plugin(world, p, False) for p in plugins]
Esempio n. 2
0
def get_plugins_info(bundles):
    if len(bundles) == 0:
        raise Exception('get_plugins_info() - no bundles provided')

    world = lilv.World()

    # this is needed when loading specific bundles instead of load_all
    # (these functions are not exposed via World yet)
    lilv.lilv_world_load_specifications(world.me)
    lilv.lilv_world_load_plugin_classes(world.me)

    # load all bundles
    for bundle in bundles:
        # lilv wants the last character as the separator
        bundle = os.path.abspath(bundle)
        if not bundle.endswith(os.sep):
            bundle += os.sep

        # convert bundle string into a lilv node
        bundlenode = lilv.lilv_new_file_uri(world.me, None, bundle)

        # load the bundle
        world.load_bundle(bundlenode)

        # free bundlenode, no longer needed
        lilv.lilv_node_free(bundlenode)

    plugins = world.get_all_plugins()

    if plugins.size() == 0:
        raise Exception('get_plugins_info() - selected bundles have no plugins')

    return [Plugin(world, p, False) for p in plugins]
Esempio n. 3
0
def get_pedalboard_name(bundle):
    # lilv wants the last character as the separator
    bundle = os.path.abspath(bundle)
    if not bundle.endswith(os.sep):
        bundle += os.sep

    # Create our own unique lilv world
    # We'll load a single bundle and get all plugins from it
    world = lilv.World()

    # this is needed when loading specific bundles instead of load_all
    # (these functions are not exposed via World yet)
    lilv.lilv_world_load_specifications(world.me)
    lilv.lilv_world_load_plugin_classes(world.me)

    # convert bundle string into a lilv node
    bundlenode = lilv.lilv_new_file_uri(world.me, None, bundle)

    # load the bundle
    world.load_bundle(bundlenode)

    # free bundlenode, no longer needed
    lilv.lilv_node_free(bundlenode)

    # get all plugins in the bundle
    plugins = world.get_all_plugins()

    # make sure the bundle includes 1 and only 1 plugin (the pedalboard)
    if plugins.size() != 1:
        raise Exception('get_pedalboard_info(%s) - bundle has 0 or > 1 plugin'.format(bundle))

    # no indexing in python-lilv yet, just get the first item
    plugin = None
    for p in plugins:
        plugin = p
        break

    if plugin is None:
        raise Exception('get_pedalboard_info(%s) - failed to get plugin, you are using an old lilv!'.format(bundle))

    # define the needed stuff
    ns_rdf = NS(world, lilv.LILV_NS_RDF)

    # check if the plugin is a pedalboard
    def fill_in_type(node):
        return node.as_string()
    plugin_types = [i for i in LILV_FOREACH(plugin.get_value(ns_rdf.type_), fill_in_type)]

    if "http://moddevices.com/ns/modpedal#Pedalboard" not in plugin_types:
        raise Exception('get_pedalboard_info(%s) - plugin has no mod:Pedalboard type'.format(bundle))

    return plugin.get_name().as_string()
Esempio n. 4
0
def get_plugins_info(bundles):
    # if empty, do nothing
    if len(bundles) == 0:
        raise Exception('get_plugins_info() - no bundles provided')

    # Create our own unique lilv world
    # We'll load the selected bundles and get all plugins from it
    world = lilv.World()

    # this is needed when loading specific bundles instead of load_all
    # (these functions are not exposed via World yet)
    lilv.lilv_world_load_specifications(world.me)
    lilv.lilv_world_load_plugin_classes(world.me)

    # load all bundles
    for bundle in bundles:
        # lilv wants the last character as the separator
        if not bundle.endswith(os.sep):
            bundle += os.sep

        # convert bundle string into a lilv node
        bundlenode = lilv.lilv_new_file_uri(world.me, None, bundle)

        # load the bundle
        world.load_bundle(bundlenode)

        # free bundlenode, no longer needed
        lilv.lilv_node_free(bundlenode)

    # get all plugins available in the selected bundles
    plugins = world.get_all_plugins()

    # make sure the bundles include something
    if plugins.size() == 0:
        raise Exception('get_plugins_info() - selected bundles have no plugins')

    # return all the info
    return [get_plugin_info(world, p) for p in plugins]
Esempio n. 5
0
def get_pedalboard_info(bundle):
    # lilv wants the last character as the separator
    if not bundle.endswith(os.sep):
        bundle += os.sep

    # Create our own unique lilv world
    # We'll load a single bundle and get all plugins from it
    world = lilv.World()

    # this is needed when loading specific bundles instead of load_all
    # (these functions are not exposed via World yet)
    lilv.lilv_world_load_specifications(world.me)
    lilv.lilv_world_load_plugin_classes(world.me)

    # convert bundle string into a lilv node
    bundlenode = lilv.lilv_new_file_uri(world.me, None, bundle)

    # load the bundle
    world.load_bundle(bundlenode)

    # free bundlenode, no longer needed
    lilv.lilv_node_free(bundlenode)

    # get all plugins in the bundle
    plugins = world.get_all_plugins()

    # make sure the bundle includes 1 and only 1 plugin (the pedalboard)
    if plugins.size() != 1:
        raise Exception('get_info_from_lv2_bundle(%s) - bundle has 0 or > 1 plugin'.format(bundle))

    # no indexing in python-lilv yet, just get the first item
    plugin = None
    for p in plugins:
        plugin = p
        break

    if plugin is None:
        raise Exception('get_info_from_lv2_bundle(%s) - failed to get plugin, you are using an old lilv!'.format(bundle))

    # define the needed stuff
    rdf = NS(lilv.LILV_NS_RDF, world)
    ingen = NS('http://drobilla.net/ns/ingen#', world)
    lv2core = NS(lilv.LILV_NS_LV2, world)
    modpedal = NS("http://portalmod.com/ns/modpedal#", world)

    # check if the plugin is a pedalboard
    def fill_in_type(node):
        return node.as_string()
    plugin_types = [i for i in LILV_FOREACH(plugin.get_value(rdf.type_), fill_in_type)]

    if "http://portalmod.com/ns/modpedal#Pedalboard" not in plugin_types:
        raise Exception('get_info_from_lv2_bundle(%s) - plugin has no mod:Pedalboard type'.format(bundle))

    # let's get all the info now
    ingenarcs   = []
    ingenblocks = []

    info = {
        'name':   plugin.get_value(modpedal.name).get_first().as_string(),
        'author': plugin.get_author_name().as_string() or '', # Might be empty
        'uri':    plugin.get_uri().as_string(),
        'hardware': {
            # we save this info later
            'audio': {
                'ins': 0,
                'outs': 0
             },
            'cv': {
                'ins': 0,
                'outs': 0
             },
            'midi': {
                'ins': 0,
                'outs': 0
             }
        },
        'size': {
            'width':  plugin.get_value(modpedal.width).get_first().as_int(),
            'height': plugin.get_value(modpedal.height).get_first().as_int(),
        },
        'screenshot': os.path.basename(plugin.get_value(modpedal.screenshot).get_first().as_string()),
        'thumbnail':  os.path.basename(plugin.get_value(modpedal.thumbnail).get_first().as_string()),
        'connections': [], # we save this info later
        'plugins':     []  # we save this info later
    }

    # connections
    arcs = plugin.get_value(ingen.arc)
    it = arcs.begin()
    while not arcs.is_end(it):
        arc = arcs.get(it)
        it  = arcs.next(it)

        if arc.me is None:
            continue

        head = lilv.lilv_world_get(world.me, arc.me, ingen.head.me, None)
        tail = lilv.lilv_world_get(world.me, arc.me, ingen.tail.me, None)

        if head is None or tail is None:
            continue

        ingenarcs.append({
            "source": lilv.lilv_node_as_string(tail).replace("file://","",1).replace(bundle,"",1),
            "target": lilv.lilv_node_as_string(head).replace("file://","",1).replace(bundle,"",1)
        })

    # hardware ports
    handled_port_uris = []
    ports = plugin.get_value(lv2core.port)
    it = ports.begin()
    while not ports.is_end(it):
        port = ports.get(it)
        it   = ports.next(it)

        if port.me is None:
            continue

        # check if we already handled this port
        port_uri = port.as_uri()
        if port_uri in handled_port_uris:
            continue
        handled_port_uris.append(port_uri)

        # get types
        port_types = lilv.lilv_world_find_nodes(world.me, port.me, rdf.type_.me, None)

        if port_types is None:
            continue

        portDir  = "" # input or output
        portType = "" # atom, audio or cv

        it2 = lilv.lilv_nodes_begin(port_types)
        while not lilv.lilv_nodes_is_end(port_types, it2):
            port_type = lilv.lilv_nodes_get(port_types, it2)
            it2 = lilv.lilv_nodes_next(port_types, it2)

            if port_type is None:
                continue

            port_type_uri = lilv.lilv_node_as_uri(port_type)

            if port_type_uri == "http://lv2plug.in/ns/lv2core#InputPort":
                portDir = "input"
            elif port_type_uri == "http://lv2plug.in/ns/lv2core#OutputPort":
                portDir = "output"
            elif port_type_uri == "http://lv2plug.in/ns/lv2core#AudioPort":
                portType = "audio"
            elif port_type_uri == "http://lv2plug.in/ns/lv2core#CVPort":
                portType = "cv"
            elif port_type_uri == "http://lv2plug.in/ns/ext/atom#AtomPort":
                portType = "atom"

        if not (portDir or portType):
            continue

        if portType == "audio":
            if portDir == "input":
                info['hardware']['audio']['ins'] += 1
            else:
                info['hardware']['audio']['outs'] += 1

        elif portType == "atom":
            if portDir == "input":
                info['hardware']['midi']['ins'] += 1
            else:
                info['hardware']['midi']['outs'] += 1

        elif portType == "cv":
            if portDir == "input":
                info['hardware']['cv']['ins'] += 1
            else:
                info['hardware']['cv']['outs'] += 1

    # plugins
    blocks = plugin.get_value(ingen.block)
    it = blocks.begin()
    while not blocks.is_end(it):
        block = blocks.get(it)
        it    = blocks.next(it)

        if block.me is None:
            continue

        protouri1 = lilv.lilv_world_get(world.me, block.me, lv2core.prototype.me, None)
        protouri2 = lilv.lilv_world_get(world.me, block.me, ingen.prototype.me, None)

        if protouri1 is not None:
            proto = protouri1
        elif protouri2 is not None:
            proto = protouri2
        else:
            continue

        uri = lilv.lilv_node_as_uri(proto)

        ingenblocks.append({
            "uri": uri,
            "x": lilv.lilv_node_as_float(lilv.lilv_world_get(world.me, block.me, ingen.canvasX.me, None)),
            "y": lilv.lilv_node_as_float(lilv.lilv_world_get(world.me, block.me, ingen.canvasY.me, None))
        })

    info['connections'] = ingenarcs
    info['plugins']     = ingenblocks

    return info
Esempio n. 6
0
def get_plugins_info(bundles):
    # if empty, do nothing
    if len(bundles) == 0:
        raise Exception('get_plugins_info() - no bundles provided')

    # Create our own unique lilv world
    # We'll load the selected bundles and get all plugins from it
    world = lilv.World()

    # this is needed when loading specific bundles instead of load_all
    # (these functions are not exposed via World yet)
    lilv.lilv_world_load_specifications(world.me)
    lilv.lilv_world_load_plugin_classes(world.me)

    # load all bundles
    for bundle in bundles:
        # lilv wants the last character as the separator
        if not bundle.endswith(os.sep):
            bundle += os.sep

        # convert bundle string into a lilv node
        bundlenode = lilv.lilv_new_file_uri(world.me, None, bundle)

        # load the bundle
        world.load_bundle(bundlenode)

        # free bundlenode, no longer needed
        lilv.lilv_node_free(bundlenode)

    # get all plugins available in the selected bundles
    plugins = world.get_all_plugins()

    # make sure the bundles include something
    if plugins.size() == 0:
        raise Exception('get_plugins_info() - selected bundles have no plugins')

    # define the needed stuff
    doap    = NS(world, lilv.LILV_NS_DOAP)
    rdf     = NS(world, lilv.LILV_NS_RDF)
    rdfs    = NS(world, lilv.LILV_NS_RDFS)
    lv2core = NS(world, lilv.LILV_NS_LV2)
    pprops  = NS(world, "http://lv2plug.in/ns/ext/port-props#")
    units   = NS(world, "http://lv2plug.in/ns/extensions/units#")
    modgui  = NS(world, "http://portalmod.com/ns/modgui#")

    # function for filling port info
    def fill_port_info(plugin, port):
        global index
        index += 1

        # port types
        types = [typ.rsplit("#",1)[-1].replace("Port","",1) for typ in get_port_data(port, rdf.type_)]

        # unit block
        uunit = lilv.lilv_nodes_get_first(port.get_value(units.unit.me))

        # contains unit
        if "Control" in types and uunit is not None:
            uuri = lilv.lilv_node_as_uri(uunit)

            # using pre-existing lv2 unit
            if uuri is not None and uuri.startswith("http://lv2plug.in/ns/extensions/units#"):
                ulabel, urender, usymbol = get_port_unit(uuri.replace("http://lv2plug.in/ns/extensions/units#","",1))

            # using custom unit
            else:
                xlabel  = world.find_nodes(uunit, rdfs  .label.me, None).get_first()
                xrender = world.find_nodes(uunit, units.render.me, None).get_first()
                xsymbol = world.find_nodes(uunit, units.symbol.me, None).get_first()

                ulabel  = xlabel .as_string() if xlabel .me else ""
                urender = xrender.as_string() if xrender.me else ""
                usymbol = xsymbol.as_string() if xsymbol.me else ""

        # no unit
        else:
            ulabel  = ""
            urender = ""
            usymbol = ""

        return {
            'index' : index,
            'name'  : lilv.lilv_node_as_string(port.get_name()),
            'symbol': lilv.lilv_node_as_string(port.get_symbol()),
            'type'  : types,
            'range' : {
                'default': lilv.lilv_node_as_float(lilv.lilv_nodes_get_first(port.get_value(lv2core.default.me))),
                'minimum': lilv.lilv_node_as_float(lilv.lilv_nodes_get_first(port.get_value(lv2core.minimum.me))),
                'maximum': lilv.lilv_node_as_float(lilv.lilv_nodes_get_first(port.get_value(lv2core.maximum.me))),
            } if ("Control", "Input") == types else {},
            'units' : {
                'label' : ulabel,
                'render': urender,
                'symbol': usymbol,
            } if "Control" in types and (ulabel or urender or usymbol) else {},
            'designation': (get_port_data(port, lv2core.designation) or [None])[0],
            'properties' : [typ.rsplit("#",1)[-1] for typ in get_port_data(port, lv2core.portProperty)],
            'rangeSteps' : (get_port_data(port, pprops.rangeSteps) or [None])[0],
            "scalePoints": [],
        }

    # function for filling plugin info
    def fill_plugin_info(plugin):
        bundleuri = plugin.get_bundle_uri().as_string()
        microver  = plugin.get_value(lv2core.microVersion).get_first()
        minorver  = plugin.get_value(lv2core.minorVersion).get_first()
        modguigui = plugin.get_value(modgui.gui).get_first()

        if modguigui.me is not None:
            modgui_scrn  = world.find_nodes(modguigui.me, modgui.screenshot      .me, None).get_first()
            modgui_thumb = world.find_nodes(modguigui.me, modgui.thumbnail       .me, None).get_first()
            modgui_icon  = world.find_nodes(modguigui.me, modgui.iconTemplate    .me, None).get_first()
            modgui_setts = world.find_nodes(modguigui.me, modgui.settingsTemplate.me, None).get_first()
            modgui_data  = world.find_nodes(modguigui.me, modgui.templateData    .me, None).get_first()

        global index
        index = -1

        return {
            'name': plugin.get_name().as_string(),
            'uri' : plugin.get_uri().as_string(),
            'author': {
                'name'    : plugin.get_author_name().as_string() or "",
                'homepage': plugin.get_author_homepage().as_string() or "",
                'email'   : (plugin.get_author_email().as_string() or "").replace(bundleuri,"",1),
            },

            'ports': [fill_port_info(plugin, p) for p in (plugin.get_port_by_index(i) for i in range(plugin.get_num_ports()))],

            'gui': {
                'screenshot'      : lilv.lilv_uri_to_path(modgui_scrn .as_string()) if modgui_scrn .me else "",
                'thumbnail'       : lilv.lilv_uri_to_path(modgui_thumb.as_string()) if modgui_thumb.me else "",
                'iconTemplate'    : lilv.lilv_uri_to_path(modgui_icon .as_string()) if modgui_icon .me else "",
                'settingsTemplate': lilv.lilv_uri_to_path(modgui_setts.as_string()) if modgui_setts.me else "",
                'templateData'    :
                    json.load(open(lilv.lilv_uri_to_path(modgui_data.as_string()), 'r')) if modgui_data.me else {},
            } if modguigui.me is not None else {},

            'binary'  : lilv.lilv_uri_to_path(plugin.get_library_uri().as_string()),
            'category': get_category(plugin.get_value(rdf.type_)),
            'license' : (plugin.get_value(doap.license).get_first().as_string() or "").replace(bundleuri,"",1),

            'description'  : plugin.get_value(rdfs.comment).get_first().as_string() or "",
            'documentation': plugin.get_value(lv2core.documentation).get_first().as_string() or "",
            'microVersion' : microver.as_int() if microver.me else 0,
            'minorVersion' : minorver.as_int() if minorver.me else 0,
        }

    # return all the info
    return [fill_plugin_info(p) for p in plugins]
Esempio n. 7
0
def get_info_from_lv2_bundle(bundle):
    # lilv wants the last character as the separator
    if not bundle.endswith(os.sep):
        bundle += os.sep

    # Create our own unique lilv world
    # We'll load a single bundle and get all plugins from it
    world = lilv.World()

    # this is needed when loading specific bundles instead of load_all
    # (these functions are not exposed via World yet)
    lilv.lilv_world_load_specifications(world.me)
    lilv.lilv_world_load_plugin_classes(world.me)

    # convert bundle string into a lilv node
    bundlenode = lilv.lilv_new_file_uri(world.me, None, bundle)

    # load the bundle
    world.load_bundle(bundlenode)

    # free bundlenode, no longer needed
    lilv.lilv_node_free(bundlenode)

    # get all plugins in the bundle
    plugins = world.get_all_plugins()

    # make sure the bundle includes 1 and only 1 plugin (the pedalboard)
    if plugins.size() != 1:
        raise Exception('get_info_from_lv2_bundle(%s) - bundle has 0 or > 1 plugin'.format(bundle))

    # no indexing in python-lilv yet, just get the first item
    plugin = None
    for p in plugins:
        plugin = p
        break

    if plugin is None:
        raise Exception('get_info_from_lv2_bundle(%s) - failed to get plugin, you are using an old lilv!'.format(bundle))

    # handy class to get lilv nodes from. copied from lv2.py in mod-ui
    class NS(object):
        def __init__(self, base):
            self.base = base
            self._cache = {}

        def __getattr__(self, attr):
            if attr not in self._cache:
                self._cache[attr] = lilv.Node(world.new_uri(self.base+attr))
            return self._cache[attr]

    # define the needed stuff
    NS_lv2core       = NS('http://lv2plug.in/ns/lv2core#')
    NS_lv2core_proto = NS_lv2core.prototype

    NS_modgui       = NS('http://moddevices.com/ns/modgui#')
    NS_modgui_thumb = NS_modgui.thumbnail

    NS_ingen           = NS('http://drobilla.net/ns/ingen#')
    NS_ingen_block     = NS_ingen.block
    NS_ingen_prototype = NS_ingen.prototype

    # check if the plugin has modgui:thumnail, if not it's probably not a real pedalboard
    thumbnail_check = plugin.get_value(NS_modgui_thumb).get_first()

    if thumbnail_check.me is None:
        raise Exception('get_info_from_lv2_bundle(%s) - plugin has no modgui:thumbnail'.format(bundle))

    # let's get all the info now
    ingenplugins = []

    info = {
        'name':      plugin.get_name().as_string(),
        #'author':    plugin.get_author_name().as_string() or '', # Might be empty
        #'uri':       plugin.get_uri().as_string(),
        'thumbnail': os.path.basename(thumbnail_check.as_string()),
        'plugins':   [] # we save this info later
    }

    blocks = plugin.get_value(NS_ingen_block)

    it = blocks.begin()
    while not blocks.is_end(it):
        block = blocks.get(it)
        it    = blocks.next(it)

        if block.me is None:
            continue

        protouri1 = lilv.lilv_world_get(world.me, block.me, NS_lv2core_proto.me, None)
        protouri2 = lilv.lilv_world_get(world.me, block.me, NS_ingen_prototype.me, None)

        if protouri1 is not None:
            ingenplugins.append(lilv.lilv_node_as_uri(protouri1))
        elif protouri2 is not None:
            ingenplugins.append(lilv.lilv_node_as_uri(protouri2))

    info['plugins'] = ingenplugins

    return info
Esempio n. 8
0
def get_pedalboard_name(bundle):
    # lilv wants the last character as the separator
    bundle = os.path.abspath(bundle)
    if not bundle.endswith(os.sep):
        bundle += os.sep

    # Create our own unique lilv world
    # We'll load a single bundle and get all plugins from it
    world = lilv.World()

    # this is needed when loading specific bundles instead of load_all
    # (these functions are not exposed via World yet)
    lilv.lilv_world_load_specifications(world.me)
    lilv.lilv_world_load_plugin_classes(world.me)

    # convert bundle string into a lilv node
    bundlenode = lilv.lilv_new_file_uri(world.me, None, bundle)

    # load the bundle
    world.load_bundle(bundlenode)

    # free bundlenode, no longer needed
    lilv.lilv_node_free(bundlenode)

    # get all plugins in the bundle
    plugins = world.get_all_plugins()

    # make sure the bundle includes 1 and only 1 plugin (the pedalboard)
    if plugins.size() != 1:
        raise Exception(
            'get_pedalboard_info(%s) - bundle has 0 or > 1 plugin'.format(
                bundle))

    # no indexing in python-lilv yet, just get the first item
    plugin = None
    for p in plugins:
        plugin = p
        break

    if plugin is None:
        raise Exception(
            'get_pedalboard_info(%s) - failed to get plugin, you are using an old lilv!'
            .format(bundle))

    # define the needed stuff
    ns_rdf = NS(world, lilv.LILV_NS_RDF)

    # check if the plugin is a pedalboard
    def fill_in_type(node):
        return node.as_string()

    plugin_types = [
        i for i in LILV_FOREACH(plugin.get_value(ns_rdf.type_), fill_in_type)
    ]

    if "http://moddevices.com/ns/modpedal#Pedalboard" not in plugin_types:
        raise Exception(
            'get_pedalboard_info(%s) - plugin has no mod:Pedalboard type'.
            format(bundle))

    return plugin.get_name().as_string()
Esempio n. 9
0
def get_pedalboard_info(bundle):
    # lilv wants the last character as the separator
    bundle = os.path.abspath(bundle)
    if not bundle.endswith(os.sep):
        bundle += os.sep

    # Create our own unique lilv world
    # We'll load a single bundle and get all plugins from it
    world = lilv.World()

    # this is needed when loading specific bundles instead of load_all
    # (these functions are not exposed via World yet)
    lilv.lilv_world_load_specifications(world.me)
    lilv.lilv_world_load_plugin_classes(world.me)

    # convert bundle string into a lilv node
    bundlenode = lilv.lilv_new_file_uri(world.me, None, bundle)

    # load the bundle
    world.load_bundle(bundlenode)

    # free bundlenode, no longer needed
    lilv.lilv_node_free(bundlenode)

    # get all plugins in the bundle
    plugins = world.get_all_plugins()

    # make sure the bundle includes 1 and only 1 plugin (the pedalboard)
    if plugins.size() != 1:
        raise Exception(
            'get_pedalboard_info(%s) - bundle has 0 or > 1 plugin'.format(
                bundle))

    # no indexing in python-lilv yet, just get the first item
    plugin = None
    for p in plugins:
        plugin = p
        break

    if plugin is None:
        raise Exception(
            'get_pedalboard_info(%s) - failed to get plugin, you are using an old lilv!'
            .format(bundle))

    # define the needed stuff
    ns_rdf = NS(world, lilv.LILV_NS_RDF)
    ns_lv2core = NS(world, lilv.LILV_NS_LV2)
    ns_ingen = NS(world, "http://drobilla.net/ns/ingen#")
    ns_modpedal = NS(world, "http://moddevices.com/ns/modpedal#")

    # check if the plugin is a pedalboard
    def fill_in_type(node):
        return node.as_string()

    plugin_types = [
        i for i in LILV_FOREACH(plugin.get_value(ns_rdf.type_), fill_in_type)
    ]

    if "http://moddevices.com/ns/modpedal#Pedalboard" not in plugin_types:
        raise Exception(
            'get_pedalboard_info(%s) - plugin has no mod:Pedalboard type'.
            format(bundle))

    # let's get all the info now
    ingenarcs = []
    ingenblocks = []

    info = {
        'name':
        plugin.get_name().as_string(),
        'uri':
        plugin.get_uri().as_string(),
        'author':
        plugin.get_author_name().as_string() or "",  # Might be empty
        'hardware': {
            # we save this info later
            'audio': {
                'ins': 0,
                'outs': 0
            },
            'cv': {
                'ins': 0,
                'outs': 0
            },
            'midi': {
                'ins': 0,
                'outs': 0
            }
        },
        'size': {
            'width': plugin.get_value(ns_modpedal.width).get_first().as_int(),
            'height':
            plugin.get_value(ns_modpedal.height).get_first().as_int(),
        },
        'screenshot':
        os.path.basename(
            plugin.get_value(ns_modpedal.screenshot).get_first().as_string()
            or ""),
        'thumbnail':
        os.path.basename(
            plugin.get_value(ns_modpedal.thumbnail).get_first().as_string()
            or ""),
        'connections': [],  # we save this info later
        'plugins': []  # we save this info later
    }

    # connections
    arcs = plugin.get_value(ns_ingen.arc)
    it = arcs.begin()
    while not arcs.is_end(it):
        arc = arcs.get(it)
        it = arcs.next(it)

        if arc.me is None:
            continue

        head = lilv.lilv_world_get(world.me, arc.me, ns_ingen.head.me, None)
        tail = lilv.lilv_world_get(world.me, arc.me, ns_ingen.tail.me, None)

        if head is None or tail is None:
            continue

        ingenarcs.append({
            "source":
            lilv.lilv_uri_to_path(lilv.lilv_node_as_string(tail)).replace(
                bundle, "", 1),
            "target":
            lilv.lilv_uri_to_path(lilv.lilv_node_as_string(head)).replace(
                bundle, "", 1)
        })

    # hardware ports
    handled_port_uris = []
    ports = plugin.get_value(ns_lv2core.port)
    it = ports.begin()
    while not ports.is_end(it):
        port = ports.get(it)
        it = ports.next(it)

        if port.me is None:
            continue

        # check if we already handled this port
        port_uri = port.as_uri()
        if port_uri in handled_port_uris:
            continue
        if port_uri.endswith("/control_in") or port_uri.endswith(
                "/control_out"):
            continue
        handled_port_uris.append(port_uri)

        # get types
        port_types = lilv.lilv_world_find_nodes(world.me, port.me,
                                                ns_rdf.type_.me, None)

        if port_types is None:
            continue

        portDir = ""  # input or output
        portType = ""  # atom, audio or cv

        it2 = lilv.lilv_nodes_begin(port_types)
        while not lilv.lilv_nodes_is_end(port_types, it2):
            port_type = lilv.lilv_nodes_get(port_types, it2)
            it2 = lilv.lilv_nodes_next(port_types, it2)

            if port_type is None:
                continue

            port_type_uri = lilv.lilv_node_as_uri(port_type)

            if port_type_uri == "http://lv2plug.in/ns/lv2core#InputPort":
                portDir = "input"
            elif port_type_uri == "http://lv2plug.in/ns/lv2core#OutputPort":
                portDir = "output"
            elif port_type_uri == "http://lv2plug.in/ns/lv2core#AudioPort":
                portType = "audio"
            elif port_type_uri == "http://lv2plug.in/ns/lv2core#CVPort":
                portType = "cv"
            elif port_type_uri == "http://lv2plug.in/ns/ext/atom#AtomPort":
                portType = "atom"

        if not (portDir or portType):
            continue

        if portType == "audio":
            if portDir == "input":
                info['hardware']['audio']['ins'] += 1
            else:
                info['hardware']['audio']['outs'] += 1

        elif portType == "atom":
            if portDir == "input":
                info['hardware']['midi']['ins'] += 1
            else:
                info['hardware']['midi']['outs'] += 1

        elif portType == "cv":
            if portDir == "input":
                info['hardware']['cv']['ins'] += 1
            else:
                info['hardware']['cv']['outs'] += 1

    # plugins
    blocks = plugin.get_value(ns_ingen.block)
    it = blocks.begin()
    while not blocks.is_end(it):
        block = blocks.get(it)
        it = blocks.next(it)

        if block.me is None:
            continue

        protouri1 = lilv.lilv_world_get(world.me, block.me,
                                        ns_lv2core.prototype.me, None)
        protouri2 = lilv.lilv_world_get(world.me, block.me,
                                        ns_ingen.prototype.me, None)

        if protouri1 is not None:
            proto = protouri1
        elif protouri2 is not None:
            proto = protouri2
        else:
            continue

        instance = lilv.lilv_uri_to_path(lilv.lilv_node_as_string(
            block.me)).replace(bundle, "", 1)
        uri = lilv.lilv_node_as_uri(proto)

        enabled = lilv.lilv_world_get(world.me, block.me, ns_ingen.enabled.me,
                                      None)
        minorver = lilv.lilv_world_get(world.me, block.me,
                                       ns_lv2core.minorVersion.me, None)
        microver = lilv.lilv_world_get(world.me, block.me,
                                       ns_lv2core.microVersion.me, None)

        ingenblocks.append({
            "instance":
            instance,
            "uri":
            uri,
            "x":
            lilv.lilv_node_as_float(
                lilv.lilv_world_get(world.me, block.me, ns_ingen.canvasX.me,
                                    None)),
            "y":
            lilv.lilv_node_as_float(
                lilv.lilv_world_get(world.me, block.me, ns_ingen.canvasY.me,
                                    None)),
            "enabled":
            lilv.lilv_node_as_bool(enabled) if enabled is not None else False,
            "minorVersion":
            lilv.lilv_node_as_int(minorver) if minorver else 0,
            "microVersion":
            lilv.lilv_node_as_int(microver) if microver else 0,
        })

    info['connections'] = ingenarcs
    info['plugins'] = ingenblocks

    return info