The hints in @derobert and @Graeme's comments are helpful, but the output format is inconvenient, so I wrote a parser to transform it to 0 Name 0.0
, which is easier to parse. While doing so, I also added a helper to change the volume easily.
It's possible to embed this into a loop and to do whatever you want. This is in the Nim language.
import osproc, strutils, pegs, os, parseopt2, sequtils
type Application = object
id: int
name: string
volume: float
var verbose = false
proc tryExec(cmd): auto =
if verbose: echo("executing $1" % [cmd])
let output = execCmdEx(cmd)
if output.exitCode != 0:
raise newException(EBase, "Failed to execute `$1` with error code $2\nOutput:\n===\n$3\n===" %
[cmd, $output.exitCode, output.output])
return output.output
iterator getPlayingApps(): auto =
let paOutput = tryExec("pactl list sink-inputs")
let sinkStrings = paOutput.split("\l\l")
for str in sinkStrings:
var num: int
var volume: float
var applicationName: string
var matches = ["", ""]
if str.find(peg"'Sink Input #'{\d+}", matches) != -1:
num = parseInt(matches[0])
else:
raise newException(EBase, "Could not find sink input number in \n===\n$1" % [str])
if str.find(peg"'Volume'@'/'\s*{\d+}'%'", matches) != -1:
volume = parseFloat(matches[0])
else:
raise newException(EBase, "Could not find application volume in \n===\n$1" % [str])
if str.find(peg"'application.name = ""'{[^""]+}'""'", matches) != -1:
applicationName = matches[0]
else:
raise newException(EBase, "Could not find application name in \n===\n$1" % [str])
yield Application(id: num, name: applicationName, volume: volume)
proc adjustAppVolume(app: Application, percent: float) =
let nextVol = min((app.volume + (app.volume * (percent - 100) / 100.0)).int, 100)
discard tryExec("pactl set-sink-input-volume $1 $2%" % [$app.id, $nextVol])
proc adjustName(name: string, percent: float) =
for app in getPlayingApps():
if app.name.contains(peg(name)):
adjustAppVolume(app, percent)
proc printInfo() =
for app in getPlayingApps():
echo("$1\t$2\t$3" % [$app.id, app.name, $app.volume])
var params: seq[string] = @[]
for kind, key, val in getopt():
case kind
of cmdArgument:
params.add(key)
of cmdLongOption, cmdShortOption:
case key
of "help", "h":
echo "usage: parsepa [-h|--help] [-v|--verbose] [<name expression> percent (-a|--adjust)] [-p|--print]"
quit(QuitSuccess)
of "verbose", "v":
verbose = true
of "adjust", "a":
adjustName(params[0], parseFloat(params[1]))
quit(QuitSuccess)
of "print", "p":
printInfo()
quit(QuitSuccess)
else:
echo "Invalid Argument"
quit(QuitFailure)
else:
echo "Invalid Argument"
quit(QuitFailure)
x64 binary in this gist, for the lazy: https://gist.github.com/flaviut/9feb9a75bd452e6ddd03