Skip to content

Commit

Permalink
Merge pull request 'feat: add support for resuming audio' (#1) from f…
Browse files Browse the repository at this point in the history
…eat/resume into main

Reviewed-on: https://codeberg.org/tomkoid/audstopper/pulls/1
  • Loading branch information
tomkoid committed Jun 2, 2024
2 parents bd7f9d6 + 0ab43bc commit d334d46
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 18 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ This will start the AudStopper daemon, which will monitor audio output changes a
To configure AudStopper, you can edit the configuration file located at `~/.config/audstopper/config.toml`. Here is an example configuration file:

```toml
# Resume audio after you change output back
# WARNING: This feature is not stable yet. The current implementation has big issues.
# Example: audio is playing on audio 1 and then you switch to
# audio 2, the audio gets paused. after you switch
# back to audio 1, the audio gets unmuted.

# Enable MPC pausing
mpc = false

Expand Down
30 changes: 29 additions & 1 deletion internal/audio/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import (
"codeberg.org/tomkoid/audstopper/internal/config"
)

type mutedSink struct {
Name string
Players []string
}

func AudioMonitor(c *pulseaudio.Client, config *config.Config) {
outputs, activeIndex, err := c.Outputs()
if err != nil {
Expand All @@ -26,6 +31,9 @@ func AudioMonitor(c *pulseaudio.Client, config *config.Config) {
log.Fatal(err)
}

/// Muted sink
var ms mutedSink

log.Println("Starting audio monitoring.")

// Monitor the default sink for changes
Expand All @@ -49,8 +57,28 @@ func AudioMonitor(c *pulseaudio.Client, config *config.Config) {
defaultSink.CardID,
)

if config.Resume && ms.Name == defaultSink.CardID {
log.Println(
"Audio was muted, unmuted it.",
)

initialSinkName = defaultSink.CardID

resumeAudio(config, ms.Players)
ms.Name = ""
ms.Players = []string{}
continue
}

// stop audio if mpc or playerctl is running
stopAudio(config)
playingPlayers := stopAudio(config)
log.Printf("Playing: %t\n", len(playingPlayers) != 0)
if len(playingPlayers) != 0 {
ms.Name = initialSinkName
ms.Players = playingPlayers
}
// log.Printf("1: %s\n", ms.Name)
// log.Printf("2: %s\n", defaultSink.CardID)

initialSinkName = defaultSink.CardID
}
Expand Down
36 changes: 36 additions & 0 deletions internal/audio/players.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package audio

import (
"os/exec"

"codeberg.org/tomkoid/audstopper/internal/config"
)

type Player struct {
Name string
PauseCommand []string
ResumeCommand []string
}

func listPlayers(config *config.Config) []Player {
var players []Player

// if these binaries exist on the running system
if _, err := exec.LookPath("mpc"); err == nil && config.Mpc {
players = append(players, Player{
Name: "mpc",
PauseCommand: []string{"mpc", "pause"},
ResumeCommand: []string{"mpc", "play"},
})
}

if _, err := exec.LookPath("playerctl"); err == nil && config.PlayerCtl {
players = append(players, Player{
Name: "playerctl",
PauseCommand: []string{"playerctl", "pause"},
ResumeCommand: []string{"playerctl", "resume"},
})
}

return players
}
39 changes: 39 additions & 0 deletions internal/audio/playing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package audio

import (
"strings"

"codeberg.org/tomkoid/audstopper/internal/tools"
)

func isPlaying(player string) (bool, []string) {
playing := false
players := []string{}

if player == "playerctl" {
output, err := tools.RunCommand("playerctl", "status")
if err != nil {
return false, nil
}

if string(output) == "Playing\n" {
playing = true
players = append(players, player)
}
}
if player == "mpc" {
output, err := tools.RunCommand("mpc", "status")
if err != nil {
return false, nil
}

println(string(output))
if strings.Contains(string(output), "[playing]") {
playing = true
players = append(players, player)
}
}

// log.Printf("returned from isplaying: %v", players)
return playing, players
}
24 changes: 24 additions & 0 deletions internal/audio/resume.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package audio

import (
"log"

"codeberg.org/tomkoid/audstopper/internal/config"
"codeberg.org/tomkoid/audstopper/internal/tools"
)

func resumeAudio(config *config.Config, givenPlayers []string) {
log.Println("Resuming audio.")

for _, player := range listPlayers(config) {
for _, givenPlayer := range givenPlayers {
if player.Name == givenPlayer {
log.Printf("Resuming audio for %s\n", givenPlayer)
_, err := tools.RunCommand(player.ResumeCommand[0], player.ResumeCommand[1:]...)
if err != nil {
log.Println(err)
}
}
}
}
}
24 changes: 14 additions & 10 deletions internal/audio/stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,30 @@ package audio

import (
"log"
"os/exec"

"codeberg.org/tomkoid/audstopper/internal/config"
"codeberg.org/tomkoid/audstopper/internal/tools"
)

func stopAudio(config *config.Config) {
/// returns an array of players that were playing before stopped
func stopAudio(config *config.Config) []string {
log.Println("Stopping audio.")
// if mpc binary exists on system
if _, err := exec.LookPath("mpc"); err == nil && config.Mpc {
err = tools.RunCommand("mpc", "pause")
if err != nil {
log.Println(err)

var players []string
for _, player := range listPlayers(config) {
currentPlaying, currentPlayers := isPlaying(player.Name)

if currentPlaying {
for _, playingPlayer := range currentPlayers {
players = append(players, playingPlayer)
}
}
}

if _, err := exec.LookPath("playerctl"); err == nil && config.PlayerCtl {
err = tools.RunCommand("playerctl", "pause")
_, err := tools.RunCommand(player.PauseCommand[0], player.PauseCommand[1:]...)
if err != nil {
log.Println(err)
}
}

return players
}
8 changes: 7 additions & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func GetConfig() Config {
var config Config = Config{
Mpc: false,
PlayerCtl: true,
Resume: false,
}

userConfigDir, err := os.UserConfigDir()
Expand All @@ -49,8 +50,13 @@ func GetConfig() Config {

configStr, err := readConfigFile(configFilePath)
if err != nil {
log.Println("using default config")
log.Println("Using default config.")
if err.Error() != "config file does not exist" {
log.Printf("config error: %s\n", err.Error())
}
return config
} else {
log.Printf("Using config from %s.\n", configFilePath)
}

err = toml.Unmarshal([]byte(configStr), &config)
Expand Down
1 change: 1 addition & 0 deletions internal/config/model.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package config

type Config struct {
Resume bool `toml:"resume"`
Mpc bool `toml:"mpc"`
PlayerCtl bool `toml:"playerctl"`
}
9 changes: 5 additions & 4 deletions internal/tools/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package tools

import "os/exec"

func RunCommand(name string, arg ...string) error {
func RunCommand(name string, arg ...string) ([]byte, error) {
cmd := exec.Command(name, arg...)

if err := cmd.Run(); err != nil {
return err
output, err := cmd.CombinedOutput()
if err != nil {
return nil, err
}

return nil
return output, nil
}
4 changes: 2 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

func main() {
config := config.GetConfig()
cfg := config.GetConfig()

// Create a PulseAudio context
c, err := pulseaudio.NewClient()
Expand All @@ -23,7 +23,7 @@ func main() {
go tools.HandleCleanup(c)

// Start audio monitoring
audio.AudioMonitor(c, &config)
audio.AudioMonitor(c, &cfg)

defer c.Close()
}

0 comments on commit d334d46

Please sign in to comment.