# Linking Scripts

These scripts contain all the logic for creating links between nodes. This involves, to a large extent, deciding which links to create.

## Hooks

The hooks in this section are organized in 3 sub-categories. The first category includes hooks that are triggered by changes in the graph. Some of them are tasked to schedule a "rescan-for-linking" event, which is the lowest priority linking event and its purpose is to scan through all the linkable session items and link them to a particular target. The "rescan-for-linking" event is always scheduled to run once for all the graph changes in a cycle. This is achieved by flagging the event as already scheduled in the module-standard-event-source; this flag is then cleared by a hook that runs on this event.

Selecting a target for each linkable and linking to it is deferred to another set of hooks by pushing a "select-target" event for each linkable. This event is the highest priority event and therefore no other changes in the graph are processed while targets are being selected.

Hooks triggered by changes in the graph
Hook name | File | Triggered by | Action
--- | --- | --- | ---
linking/rescan-trigger | rescan.lua | linkable SI added|removed or metadata-changed | schedules rescan-for-linking event
linking/linkable-removed | rescan.lua | linkable SI removed | destroys links related to the removed linkable
linking/rescan-trigger-on-linkable-added-removed | rescan-on-linkable.lua | linkable SI added|removed | schedules rescan-for-linking; can be disabled by setting `hooks.linking.rescan-on-linkable = disabled` in `wireplumber.profiles`
linking/rescan-trigger-on-target-metadata-changed | rescan.lua | metadata-changed on `target.object` or `target.node` | schedules rescan-for-linking when node target metadata properties are changed; registered only while the `linking.allow-moving-streams` setting is enabled
linking/linkable-added-immediate | rescan.lua | linkable SI added | links the new linkable immediately, without waiting for a full rescan, when it is a simple stream node
linking/session-item-added | rescan.lua | session-item-added | re-enables rescanning (see linking/bluez-session-item-removed)
linking/bluez-session-item-removed | rescan.lua | session-item-removed on a `bluez5` device | disables rescanning for 2 seconds, to avoid audio being played on internal nodes while the BT device switches profiles
linking/rescan-media-role-links | rescan-media-role-links.lua | link SI added, removed or metadata-changed | activates or deactivates role-based links based on role priorities and actions
linking/mpris-pause@track-links | mpris-pause.lua | link added|removed | tracks which MPRIS media players are linked to which sinks
linking/mpris-pause | mpris-pause.lua | session-item-removed on an input device linkable | pauses the MPRIS media applications whose streams were linked to the sink that is going away

Note

The `linking/mpris-pause*` hooks are only registered while the `linking.pause-playback` setting is enabled.

rescan-for-linking hooks, in order of execution
Hook name | File | Description
--- | --- | ---
m-standard-event-source/rescan-done | module-standard-event-source.c | clears the rescan_scheduled flag
linking/mpris-pause-disable-rescan | mpris-pause.lua | stops the rescan while MPRIS media players are still being paused, and re-schedules it afterwards; only registered while `linking.pause-playback` is enabled
linking/rescan | rescan.lua | schedules select-target for each linkable session item

select-target hooks, in order of execution
Hook name | File | Description
--- | --- | ---
linking/find-defined-target | find-defined-target.lua | Select the target that has been defined explicitly by the 'target.object' property or metadata
linking/find-audio-group-target | find-audio-group-target.lua | Select the audio group loopback node as target, if the subject belongs to an audio group
linking/find-filter-target | find-filter-target.lua | Select the target of a filter node, if the subject is a filter node
linking/find-media-role-target | find-media-role-target.lua | Select the target based on the stream's media.role and the target's device.intended-roles
linking/find-media-role-sink-target | find-media-role-sink-target.lua | Pick up the preferred target node for the output stream of a role-based loopback
linking/find-default-target | find-default-target.lua | Select the default source/sink as target
linking/find-best-target | find-best-target.lua | Select target based on priority.session
linking/get-filter-from-target | get-filter-from-target.lua | Translate the found target to a filter target that should be linked instead
linking/prepare-link | prepare-link.lua | Break existing link if needed, check if the target is available for linking; send error to the client if needed
linking/link-target | link-target.lua | Create si-standard-link session item to create links between the subject linkable and the selected target

## Extending target selection

The `select-target` hooks above form a chain: each one bypasses itself if a target has already been picked, so the first hook that finds a target wins. A custom script can therefore participate in target selection simply by registering another hook on the `select-target` event and ordering it relative to the stock hooks with `before` / `after`.

Example 4: participating in target selection walks through such a hook in full: the script itself, the `wireplumber.conf.d` fragment that defines a component for it and enables it in the `main` profile, and the reasoning behind the ordering key and the bypass guard.