Repository: Spotifyd/spotifyd (language: rust)

Issue:
Bring back pipe backend
#283 removed the pipe backend during the Great Refactoring (TM pending). Might be a good idea to bring it back at some point.

Thinks to take care of:
- [ ] Logging to stdout would interfere with this feature. So if `--backend pipe` is specified and the daemon runs with the `--no-daemon` flag, all logging would have to happen on stderr instead.
- [ ] Add the new backend to the `Backend` enum.
- [ ] Add the needed code to `setup.rs` (I think this is were the backend logic lies).

"Fixes" #256 by basically providing the functionality in the first place.


Discussion / hints:
Something like this would be better:
```
backend = pipe
device = "ffmpeg -re -i - -c:a copy -f rtp rtp://127.0.0.1:1234"
```
```spotifyd``` would start ```ffmpeg``` and pipe audio to ```ffmpeg```'s ```stdin``` and there would not be any logging issues.
> `spotifyd` would start `ffmpeg` and pipe audio to `ffmpeg`'s `stdin` and there would not be any logging issues.

A new option (`pipe_command`) could be added for this, as the `device` should be used to point to a named pipe. I don't think `spotifyd` should be spawning processes though.
I've added this feature to librespot-org, should be easy to adapt: https://github.com/librespot-org/librespot/pull/422
Being "easy to adapt" isn't a good reason to implement a feature.

The proposal for a `subprocess` backend (there it's a separate backend, a little different than what's proposed here) it's bad solution because then you have to handle failure in the spawned process. If spotifyd fails, then you have to restart both processes.

The pipe backend on the other hand would already allow, when re-integrated, in two ways

1) `spotifyd --backend pipe | player ...`
  - redirect log to stderr, just like any tool have been doing (curl, wget, etc.)
2) `spotifyd --backend pipe --device /tmp/musicfifo` `player /tmp/musicfifo`
  - both processes can be run as separate users, which is great from a security perspective

Is there an use case where the `subprocess` backend would help where `pipe` wouldn't?
> If spotifyd fails, then you have to restart both processes.

This is handled by init system.

> Is there an use case where the `subprocess` backend would help where `pipe` wouldn't?

You'll need to modify systemd unit to use pipe backend. When stdout consumer crashes, it won't be restarted. With subprocess you'll only need to edit spotifyd's config and consumer restarts will be handled automatically.
> > If spotifyd fails, then you have to restart both processes.
> 
> This is handled by init system.

Let me rephrase: If spotifyd fails, then both processes _have to be_ restarted. It doesn't matter if such restart is automatic, there's still the instability in one process taking down the other. It doesn't need to be case with the `pipe` backend.

> > Is there an use case where the `subprocess` backend would help where `pipe` wouldn't?
> 
> You'll need to modify systemd unit to use pipe backend. When stdout consumer crashes, it won't be restarted. With subprocess you'll only need to edit spotifyd's config and consumer restarts will be handled automatically.

The subcommand approach requires the single systemd unit to have maximum permissions. With the pipe backend, you can have one system unit, running a separate user, minimal permissions, only one directory and network. Then the playback is either a regular process, or an user systemd service.

In sum, the subcommand backend doesn't add any new capability over the pipe backend, which allows more control over the processes.
Is there anything I can do, to get pipe backend ? 
Is someone working on that ? 

Just to get you right: 
There is no pipe backend implemented at all at the moment ? 
Or it just doesn’t get build with pipe support ? 
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.


Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index 3c8bacfd..4ecf0fdf 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -54,7 +54,8 @@ env_logger = "0.10"
 alsa_backend = ["librespot-playback/alsa-backend", "alsa"]
 dbus_keyring = ["keyring"]
 dbus_mpris = ["dbus", "dbus-tokio", "dbus-crossroads", "rspotify"]
-default = ["alsa_backend"]
+pipe_backend = []
+default = ["alsa_backend", "pipe_backend"]
 portaudio_backend = ["librespot-playback/portaudio-backend"]
 pulseaudio_backend = ["librespot-playback/pulseaudio-backend"]
 rodio_backend = ["librespot-playback/rodio-backend"]
diff --git a/src/config.rs b/src/config.rs
index 1d14cb9a..048e4558 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -25,6 +25,7 @@ const CONFIG_FILE_NAME: &str = "spotifyd.conf";
     feature = "pulseaudio_backend",
     feature = "portaudio_backend",
     feature = "alsa_backend",
+    feature = "pipe_backend",
     feature = "rodio_backend",
     feature = "rodiojack_backend",
 )))]
@@ -38,6 +39,8 @@ static BACKEND_VALUES: &[&str] = &[
     "portaudio",
     #[cfg(feature = "rodio_backend")]
     "rodio",
+    #[cfg(feature = "pipe_backend")]
+    "pipe",
     #[cfg(feature = "rodiojack_backend")]
     "rodiojack",
 ];
@@ -50,6 +53,7 @@ pub enum Backend {
     PortAudio,
     PulseAudio,
     Rodio,
+    Pipe,
     RodioJack,
 }
 
@@ -66,6 +70,7 @@ impl FromStr for Backend {
             "portaudio" => Ok(Backend::PortAudio),
             "pulseaudio" => Ok(Backend::PulseAudio),
             "rodio" => Ok(Backend::Rodio),
+            "pipe" => Ok(Backend::Pipe),
             "rodiojack" => Ok(Backend::RodioJack),
             _ => unreachable!(),
         }
@@ -79,6 +84,7 @@ impl fmt::Display for Backend {
             Backend::PortAudio => write!(f, "portaudio"),
             Backend::PulseAudio => write!(f, "pulseaudio"),
             Backend::Rodio => write!(f, "rodio"),
+            Backend::Pipe => write!(f, "pipe"),
             Backend::RodioJack => write!(f, "rodiojack"),
         }
     }
@@ -444,7 +450,7 @@ pub struct SharedConfigValues {
     #[serde(alias = "volume-control")]
     volume_controller: Option<VolumeController>,
 
-    /// The audio device
+    /// The audio device (or file handle if using pipe backend)
     #[structopt(long, value_name = "string")]
     device: Option<String>,
 


Test patch: