From 56a0698e9c313356ffc892d7f4f1f76595bdf445 Mon Sep 17 00:00:00 2001 From: IceDBorn Date: Wed, 8 May 2024 20:38:11 +0300 Subject: [PATCH] Update nix flake for rust rewrite --- flake.nix | 34 ++-- native/connector/connect-and-monitor.sh | 159 ------------------ .../pipewire-screen-audio-connector.sh | 109 ------------ native/connector/virtmic.sh | 99 ----------- 4 files changed, 11 insertions(+), 390 deletions(-) delete mode 100755 native/connector/connect-and-monitor.sh delete mode 100755 native/connector/pipewire-screen-audio-connector.sh delete mode 100755 native/connector/virtmic.sh diff --git a/flake.nix b/flake.nix index 2838195..b5b4346 100644 --- a/flake.nix +++ b/flake.nix @@ -14,39 +14,27 @@ (substring 4 2 longDate) (substring 6 2 longDate) ]); + + read = builtins.readFile; + write = builtins.toFile; + + firefoxJSON = write "firefox.json" (read native/native-messaging-hosts/firefox.json); in { packages = forAllSystems (system: { default = with pkgsFor.${system}; - stdenv.mkDerivation { + rustPlatform.buildRustPackage { name = "pipewire-screenaudio"; version = mkDate (self.lastModifiedDate or "19700101") + "_" + (self.shortRev or "dirty"); - src = self; - + src = ./native/connector-rs; buildInputs = [ gawk hexdump jq pipewire ]; + cargoHash = "sha256-+mLsrKt7WRyO++B0rFoRb/JodBPxhZCdg2qZguKzqUI="; - installPhase = '' - runHook preInstall - - # Replace jq with its absolute path - substituteInPlace native/connector/virtmic.sh --replace jq ${pkgs.jq}/bin/jq - substituteInPlace native/connector/pipewire-screen-audio-connector.sh --replace jq ${pkgs.jq}/bin/jq - substituteInPlace native/connector/connect-and-monitor.sh --replace jq ${pkgs.jq}/bin/jq - substituteInPlace native/connector/util.sh --replace jq ${pkgs.jq}/bin/jq - - # Install files - mkdir -p $out/lib/out - install -Dm755 native/connector/pipewire-screen-audio-connector.sh $out/lib/connector/pipewire-screen-audio-connector.sh - install -Dm755 native/connector/virtmic.sh $out/lib/connector/virtmic.sh - install -Dm755 native/connector/connect-and-monitor.sh $out/lib/connector/connect-and-monitor.sh - install -Dm755 native/connector/util.sh $out/lib/connector/util.sh - + postInstall = '' # Firefox manifest - substituteInPlace native/native-messaging-hosts/firefox.json --replace /usr/lib/pipewire-screenaudio $out/lib - install -Dm644 native/native-messaging-hosts/firefox.json $out/lib/mozilla/native-messaging-hosts/com.icedborn.pipewirescreenaudioconnector.json - - runHook postInstall + install -Dm644 ${firefoxJSON} "$out/lib/mozilla/native-messaging-hosts/com.icedborn.pipewirescreenaudioconnector.json" + substituteInPlace "$out/lib/mozilla/native-messaging-hosts/com.icedborn.pipewirescreenaudioconnector.json" --replace "/usr/lib/pipewire-screenaudio/connector-rs/target/debug" "$out/bin" ''; }; }); diff --git a/native/connector/connect-and-monitor.sh b/native/connector/connect-and-monitor.sh deleted file mode 100755 index 9f78c2f..0000000 --- a/native/connector/connect-and-monitor.sh +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env bash - -# This script finds and connects targetNodeSerial to virtual mic - -virtmicPortFlId=$1 -virtmicPortFrId=$2 -targetNodeSerial=$3 - -source $PROJECT_ROOT/connector/util.sh - -exec 2>>`UtilGetLogPathForFile $(basename $0)` - -EXCLUDED_TARGETS='"AudioCallbackDriver"' - -fullDumpFile=`UtilGetTempFile` -streamsFile=`UtilGetTempFile` -portsFile=`UtilGetTempFile` - -function monitor-nodes() { - tail -f /dev/null | pw-cli -m | grep --line-buffered -v 'pipewire.sec.label = "hex:' -} - -set -e - -# Cleanup on exit -trap "rm $fullDumpFile $streamsFile $portsFile; trap - SIGTERM && kill -- -$$" SIGTERM EXIT - -pw-dump | jq -s "flatten(1)" > $fullDumpFile -UtilLog "[connect-and-monitor.sh] [Got Full Dump] File: $fullDumpFile" - -# Get streams from $fullDumpFile -cat "$fullDumpFile" | jq -c '[ .[] | select(.info.props["media.class"] == "Stream/Output/Audio") ]' > $streamsFile -UtilLog "[connect-and-monitor.sh] [Got Streams] File: $streamsFile" - -# Get output ports of streams from $fullDumpFile -streamIds=`cat "$streamsFile" | jq -c '.[].id' | paste -sd ','` -UtilLog "[connect-and-monitor.sh] [Got Stream IDs] IDs: $streamIds" - -if [[ -n "$streamIds" ]]; then - cat "$fullDumpFile" | jq -c "[ .[] | select(.type == \"PipeWire:Interface:Port\") | select(.info.direction == \"output\") | select(.info.props[\"node.id\"] | contains($streamIds)) ]" > $portsFile -else - echo '[]' > $portsFile -fi -UtilLog "[connect-and-monitor.sh] [Got Ports] File: $portsFile" - -if [[ ! "$targetNodeSerial" -eq "-1" ]]; then - UtilLog "[connect-and-monitor.sh] [Entering Single Node Mode] Serial: $targetNodeSerial" - - # Get target node id from $streamsFile - targetNodeId=`cat "$streamsFile" | jq -c "[ .[] | select(.info.props[\"object.serial\"] == $targetNodeSerial) ][0].id"` - UtilLog "[connect-and-monitor.sh] [Got Node ID] ID: $targetNodeId" - - # Get target node ports ids from $portsFile - targetPortsFile=`UtilGetTempFile` - cat "$portsFile" | jq -c "[ .[] | select(.info.props[\"node.id\"] == $targetNodeId) ]" > $targetPortsFile - targetPortFlId=`cat "$targetPortsFile" | jq -c "[ .[] | select(.info.props[\"audio.channel\"] == \"FL\") ][0].id"` - targetPortFrId=`cat "$targetPortsFile" | jq -c "[ .[] | select(.info.props[\"audio.channel\"] == \"FR\") ][0].id"` - rm $targetPortsFile - UtilLog "[connect-and-monitor.sh] [Got Ports IDs] FL ID: $targetPortFlId, FR ID: $targetPortFrId" - - # Connect target to virtmic - pw-link $targetPortFlId $virtmicPortFlId - pw-link $targetPortFrId $virtmicPortFrId - UtilLog "[connect-and-monitor.sh] [Linked Ports] $targetPortFlId -> $virtmicPortFlId, $targetPortFrId -> $virtmicPortFrId" -else - UtilLog "[connect-and-monitor.sh] [Entering All Desktop Audio Mode] Serial: $targetNodeSerial" - - # Get target nodes ids to connect from $streamsFile - targetNodesIds=`cat $streamsFile | jq -c "[ .[] | select(.info.props[\"media.name\"] | contains($EXCLUDED_TARGETS) | not) ][].id" | paste -sd ','` - UtilLog "[connect-and-monitor.sh] [Got Nodes IDs] IDs: $targetNodesIds" - - if [[ ! "$targetNodesIds" -eq "" ]]; then - # Get target nodes ports ids from $portsFile - targetPortsFile=`UtilGetTempFile` - cat "$portsFile" | jq -c "[ .[] | select(.info.props[\"node.id\"] | contains($targetNodesIds)) ]" > $targetPortsFile - targetPortsFlIds=`cat "$targetPortsFile" | jq -c "[ .[] | select(.info.props[\"audio.channel\"] == \"FL\") ][].id"` - targetPortsFrIds=`cat "$targetPortsFile" | jq -c "[ .[] | select(.info.props[\"audio.channel\"] == \"FR\") ][].id"` - rm $targetPortsFile - UtilLog "[connect-and-monitor.sh] [Got Ports IDs] FL IDs: $targetPortsFlIds, FR IDs: $targetPortsFrIds" - - # Connect targets to virtmic - echo "$targetPortsFlIds" | while read -r id; do pw-link $id $virtmicPortFlId; done - echo "$targetPortsFrIds" | while read -r id; do pw-link $id $virtmicPortFrId; done - UtilLog "[connect-and-monitor.sh] [Linked Ports] $targetPortsFlIds -> $virtmicPortFlId, $targetPortsFrIds -> $virtmicPortFrId" - fi - - # Watch for new nodes to connect - monitor-nodes | { - stdbuf -o0 awk '/remote 0 added global/ && /Node/' | - grep --line-buffered -oP 'id \K\d+' | - while read -r id; do ( - UtilLog "[connect-and-monitor.sh] [Got New Node ID] ID: $id" - - # Skip excluded targets and targets with wrong class - pw-dump "$id" | jq --exit-status -c " - [ - .[] | - select(.id == $id) | - select(.info.props[\"media.name\"] | contains($EXCLUDED_TARGETS) | not) | - select(.info.props[\"media.class\"] == \"Stream/Output/Audio\" ) - ][0].id - " >/dev/null || { - UtilLog "[connect-and-monitor.sh] [Skipped Node ID] ID: $id" - exit 0 - } - - # 1. Find the ports with node.id == $id - # 2. Get only the FR and FL ports - # 3. Sort by audio.channel (FR > FL) - # 4. Return only the ids - ids=`pw-dump | jq -s -c " - flatten(1) | - [ - .[] | - select(.info.props[\"node.id\"] == $id) | - select(.info.props[\"audio.channel\"] | contains(\"FR\", \"FL\")) - ] | - sort_by(.info.props[\"audio.channel\"]) | - .[].id - " | xargs` # Merge to one line - - # As channels were sorted, $1 is FR and $2 is FL - flId=`echo "$ids" | awk '{print $1}'` - frId=`echo "$ids" | awk '{print $2}'` - - # If the node has been removed, ports will be empty. Skip node - [[ -z "$flId" ]] || [[ -z "$frId" ]] && { - UtilLog "[connect-and-monitor.sh] [Skipped Node ID] ID: $id. Reason: Could not find all ports" - exit 0 - } - - UtilLog "[connect-and-monitor.sh] [Got Ports IDs] FL IDs: $flId, FR IDs: $frId" - - linkLog="[connect-and-monitor.sh] [Linking Ports]" - - # Connect new node to virtmic - # Link FL - linkLog="$linkLog ($flId -> $virtmicPortFlId" - pw-link $flId $virtmicPortFlId && { - linkLog="$linkLog Success)" - } || { - linkLog="$linkLog Fail)" - } - - # Link FR - linkLog="$linkLog ($frId -> $virtmicPortFrId" - pw-link $frId $virtmicPortFrId && { - linkLog="$linkLog Success)" - } || { - linkLog="$linkLog Fail)" - } - - UtilLog "$linkLog" - - ) done - } & -fi - -wait diff --git a/native/connector/pipewire-screen-audio-connector.sh b/native/connector/pipewire-screen-audio-connector.sh deleted file mode 100755 index d9e4751..0000000 --- a/native/connector/pipewire-screen-audio-connector.sh +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env bash - -export LC_ALL=C -export PROJECT_ROOT="$( cd -- "$(dirname "$0")" > /dev/null 2>&1 ; cd .. ; pwd -P )" -source $PROJECT_ROOT/connector/util.sh - -exec 2>>`UtilGetLogPathForFile $(basename $0)` - -function GetVersion () { - UtilTextToMessage "{\"version\":\"$VERSION\"}" -} - -function GetSessionType () { - type=`[[ -z "$WAYLAND_DISPLAY" ]] && echo "x11" || echo "wayland"` - UtilTextToMessage "{\"type\": \"$type\"}" -} - -function GetNodes () { - local nodes=`pw-dump | jq -c ' - [{ - "properties": { - "media.name": "[All Desktop Audio]", - "application.name": "", - "object.serial": -1 - } - }] + [ .[] | - select(.info.props["media.class"] == "Stream/Output/Audio") | - .["properties"] = .info.props | - del(.info) - ] - '` - UtilTextToMessage "$nodes" - exit -} - -function StartPipewireScreenAudio () { - setsid $PROJECT_ROOT/connector/virtmic.sh & - - sleep 1 - local micId=` - pw-dump | - jq -c "[ .[] | select(.info.props[\"node.name\"] == \"$VIRTMIC_NODE_NAME\") ][0].id" - ` - - UtilTextToMessage '{"micId":'$micId'}' - exit -} - -function SetSharingNode () { - local node=`UtilGetArg 'node'` - local micId=`UtilGetArg 'micId'` - local fifoPath=`UtilGetFifoPath "$micId"` - - if [ -e "$fifoPath" ]; then - echo "$node" >> "$fifoPath" - fi - - UtilTextToMessage '{"success":true}' - exit -} - -function StopPipewireScreenAudio () { - local micId=`UtilGetArg 'micId'` - - if [ ! "`pw-cli info "$micId" 2>/dev/null | wc -l`" -eq "0" ]; then - [ "`pw-cli destroy "$micId" 2>&1 | wc -l`" -eq "0" ] && - UtilTextToMessage '{"success":true}' && exit - fi - - UtilTextToMessage '{"success":false}' - exit -} - -function IsPipewireScreenAudioRunning () { - local micId=`UtilGetArg 'micId'` - - if pw-cli info "$micId" 2>/dev/null | grep 'node.name' | grep "$VIRTMIC_NODE_NAME" >/dev/null; then - UtilTextToMessage '{"isRunning":true}' && exit - fi - - UtilTextToMessage '{"isRunning":false}' - exit -} - -UtilGetPayload - -case "$cmd" in - 'GetVersion') - GetVersion - ;; - 'GetSessionType') - GetSessionType - ;; - 'GetNodes') - GetNodes - ;; - 'StartPipewireScreenAudio') - StartPipewireScreenAudio - ;; - 'SetSharingNode') - SetSharingNode - ;; - 'IsPipewireScreenAudioRunning') - IsPipewireScreenAudioRunning - ;; - 'StopPipewireScreenAudio') - StopPipewireScreenAudio - ;; -esac diff --git a/native/connector/virtmic.sh b/native/connector/virtmic.sh deleted file mode 100755 index 8655b97..0000000 --- a/native/connector/virtmic.sh +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env bash - -source $PROJECT_ROOT/connector/util.sh - -exec 1>>`UtilGetLogPathForFile $(basename $0)` 2>&1 - -# Get all nodes to check if $VIRTMIC_NODE_NAME exists, and create it -pw-dump | - jq --exit-status -c -s "flatten(1) | [ .[] | select(.info.props[\"node.name\"] == \"$VIRTMIC_NODE_NAME\") ][0]" >/dev/null && ( - UtilLog "[virtmic.sh] [Found Node] $VIRTMIC_NODE_NAME" - ) || ( - pw-cli create-node adapter "{ factory.name=support.null-audio-sink node.name=$VIRTMIC_NODE_NAME media.class=Audio/Source/Virtual object.linger=1 audio.position=[FL,FR] }" - UtilLog "[virtmic.sh] [Created Node] $VIRTMIC_NODE_NAME" - ) - -fullDumpFile=`UtilGetTempFile` - -# === Collect required data from PipeWire === # -# Get all nodes again for further processing -pw-dump | jq -s "flatten(1)" > $fullDumpFile -UtilLog "[virtmic.sh] [Got Dump] File: $fullDumpFile" - -# Get id and ports of $VIRTMIC_NODE_NAME -virtmicId=`cat "$fullDumpFile" | jq -c "[ .[] | select(.info.props[\"node.name\"] == \"$VIRTMIC_NODE_NAME\") ][0].id"` -UtilLog "[virtmic.sh] [Got Id] $virtmicId" - -virtmicPortsFile=`UtilGetTempFile` -cat "$fullDumpFile" | jq -c "[ .[] | select(.info.direction == \"input\") | select(.info.props[\"node.id\"] == $virtmicId) ]" > $virtmicPortsFile -UtilLog "[virtmic.sh] [Got Ports] File: $virtmicPortsFile" - -virtmicPortFlId=`cat "$virtmicPortsFile" | jq -c "[ .[] | select(.info.props[\"audio.channel\"] == \"FL\") ][0].id"` -virtmicPortFrId=`cat "$virtmicPortsFile" | jq -c "[ .[] | select(.info.props[\"audio.channel\"] == \"FR\") ][0].id"` -UtilLog "[virtmic.sh] [Got Ports] FL: $virtmicPortFlId" -UtilLog "[virtmic.sh] [Got Ports] FR: $virtmicPortFrId" - -rm $virtmicPortsFile -UtilLog "[virtmic.sh] [Cleared Ports] File: $virtmicPortsFile" - -fifoPath=`UtilGetFifoPath "$virtmicId"` -mkfifo "$fifoPath" -UtilLog "[virtmic.sh] [Created FIFO] $fifoPath" - -# Cleanup on exit -trap "rm $fullDumpFile $fifoPath; kill -- -$CURRENT_PID" EXIT - -function monitor-nodes() { - tail -f /dev/null | pw-cli -m | grep --line-buffered -v 'pipewire.sec.label = "hex:' -} - -function disconnectInputs() { - node=$1 - UtilLog "[virtmic.sh] [Disconnecting Inputs] Node: $node" - pw-dump | jq -s -r " - flatten(1) | - [ - .[] | - select(.info[\"input-node-id\"] == $node) | - .id - ] | - .[] - " | xargs -r -n1 pw-cli destroy - UtilLog "[virtmic.sh] [Disconnected Inputs] Node: $node" -} - -# Listen to selected node change -tail -f "$fifoPath" | { - # Kill connect-and-monitor.sh process if it's still alive - function killMonitor() { - if [ ! -z "$monitorProcess" ]; then - UtilLog "[virtmic.sh] [Killing] PID: $monitorProcess" - kill $monitorProcess || : - fi - } - - # Kill monitor process when this background process gets killed - trap "killMonitor" EXIT - - # Read the new target node - while read -r targetNodeSerial; do - UtilLog "[virtmic.sh] [Got FIFO Data] $targetNodeSerial" - killMonitor - disconnectInputs "$virtmicId" - setsid bash -- connect-and-monitor.sh "$virtmicPortFlId" "$virtmicPortFrId" "$targetNodeSerial" & - monitorProcess=$! - UtilLog "[virtmic.sh] [Started Background Task] Script: connect-and-monitor.sh, PID: $monitorProcess" - done -} & - -# Send SIGTERM to this process when the virtmic gets removed -monitor-nodes | - stdbuf -o0 awk '/remote 0 removed global/ && /Node/' | - grep --line-buffered -oP "id \\K$virtmicId" | { - while read -r id; do - UtilLog "[virtmic.sh] [Killing] PID: $CURRENT_PID (self)" - kill -SIGTERM $CURRENT_PID - done -} & - -wait