NixOS / nixpkgs

Nix Packages collection & NixOS
MIT License
18.05k stars 14.08k forks source link

Spotifyd service doesn't work #71362

Open NilsIrl opened 5 years ago

NilsIrl commented 5 years ago

Describe the bug Spotifyd doesn't work and outputs the following errors.

$ journalctl -u spotifyd.service
Oct 18 22:07:44 nixos spotifyd[24789]: Home directory not accessible: Permission denied
Oct 18 22:07:44 nixos pulseaudio[7646]: [autospawn] core-util.c: Home directory not accessible: Permission denied
Oct 18 22:07:44 nixos pulseaudio[7646]: [autospawn] lock-autospawn.c: Cannot access autospawn lock.
Oct 18 22:07:44 nixos spotifyd[24789]: ALSA lib pulse.c:243:(pulse_connect) PulseAudio: Unable to connect: Connection refused
Oct 18 22:07:44 nixos spotifyd[24789]: 22:07:44 [ERROR] Alsa error PCM open ALSA function 'snd_pcm_open' failed with error 'ECONNREFUSED: Connection refused'
Oct 18 22:07:44 nixos spotifyd[24789]: 22:07:44 [ERROR] Could not start audio: Alsa error: PCM open failed
Oct 18 22:07:44 nixos pulseaudio[7646]: [pulseaudio] main.c: Failed to acquire autospawn lock

To Reproduce Steps to reproduce the behavior:

  1. services.spotifyd.enable = true
  2. services.spotifyd.config = "just password and username set"
  3. These errors and music doesn't play when the device is selected on the spotify web interface.

Expected behavior

Music plays and no errors.

Additional context

If spotifyd is called from bash manually, there is no problem.

@anderslundstedt @marsam

worldofpeace commented 5 years ago

Can you try reverting 361b5f7f6533c2b597057805b16bdf229d675d15, and see what changes? That's the only pulseaudio related change that happened on master.

Are you using unstable? (nixos-info output would be helpful)

NilsIrl commented 5 years ago

@worldofpeace

Will try

Please note that spotifyd is supposed to use alsa by default. And when I explicitly tell it to use alsa I get the same error. So I don't know to what extent this is pulseaudio related.

NilsIrl commented 5 years ago

I am not using unstable

worldofpeace commented 5 years ago

@NilsIrl Ok, that commit doesn't touch 19.09 so it's likely unrelated. From the errors, maybe there's permission problems on the spotifyd user's home.

NilsIrl commented 5 years ago

From the errors, maybe there's permission problems on the spotifyd user's home.

spotifyd doesn't have a home directory.

worldofpeace commented 5 years ago

Have you enabled the pulseaudio service? Looking at the definition in all-packages.nix

withALSA = stdenv.isLinux;
withPulseAudio = config.pulseaudio or stdenv.isLinux;

You probably have it built with it.

Also not sure if the pulseaudio backend needs spotifyd user to have a home directory.

NilsIrl commented 5 years ago

Have you enabled the pulseaudio service?

I have hardware.pulseaudio.enable = true;

and Firefox has audio so I suppose Pulseaudio is working.

worldofpeace commented 5 years ago

Rough idea, paplay as the spotifyd user?

NilsIrl commented 5 years ago

Rough idea, paplay as the spotifyd user?

I am not sure what you mean but spotifyd runs under the spotifyd user when using services.spotifyd.

NilsIrl commented 5 years ago
$ ps aux | grep spotifyd
nils     23814  0.0  0.0 129024  2288 pts/1    S+   23:39   0:00 grep spotifyd
spotifyd 24789  0.0  0.0 366632  5676 ?        Ssl  20:27   0:02 /nix/store/f9810s7y3f4nfc4lkzl077fj0qh20qgz-spotifyd-0.2.11/bin/spotifyd --no-daemon --cache_path /var/cache/spotifyd --config /nix/store/8k2b9gczrfijs964bbnlk6wdm79gvb7i-spotifyd.conf
worldofpeace commented 5 years ago

@NilsIrl Sorry was vague. It seems it's trying to play audio with pulseaudio. The rough idea was getting a minimal case not related to spotifyd, by using paplay to play something as the spotifyd user. (some output from there maybe, to find out what home directory pulseaudio talking about?)

NilsIrl commented 5 years ago

by using paplay to play something as the spotifyd user

$ sudo -u spotifyd paplay imagine.wav
Home directory not accessible: Permission denied
Connection failure: Connection refused
pa_context_connect() failed: Connection refused

this seems to be the problem

worldofpeace commented 5 years ago

So I guess it might be a good idea to try adding

StateDirectory = "spotifyd";
RuntimeDirectory = "spotifyd";

to the systemd service config.

If I was accepting a PR I'd want to add

ProtectHome=true 
PrivateTmp=true

as well.

NilsIrl commented 5 years ago

These didn't fix anything.

spotifyd has no need of StateDirectory and RuntimeDirectory.

What did change stuff:

However none fix the problem

For example, without DynamicUser and with hardware.pulseaudio.systemWide, I get:

Oct 19 02:22:49 nixos spotifyd[14965]: ALSA lib pulse.c:243:(pulse_connect) PulseAudio: Unable to connect: Access denied
Oct 19 02:22:49 nixos spotifyd[14965]: 02:22:49 [ERROR] Alsa error PCM open ALSA function 'snd_pcm_open' failed with error 'ECONNREFUSED: Connection refused'
Oct 19 02:22:49 nixos spotifyd[14965]: 02:22:49 [ERROR] Could not start audio: Alsa error: PCM open failed
NilsIrl commented 5 years ago

This could be linked to the following line that appears when running sudo nixos-rebuild switch:

[/etc/tmpfiles.d/var.conf:19] Duplicate line for path "/var/cache", ignoring.

content of these files:

$ grep '/var/cache' /etc/tmpfiles.d/*
/etc/tmpfiles.d/nixos.conf:d /var/cache 0755 root root -
/etc/tmpfiles.d/systemd.conf:d /var/cache/private 0700 root root -
/etc/tmpfiles.d/var.conf:d /var/cache 0755 - - -
worldofpeace commented 5 years ago

Weird, I don't have a /etc/tmpfiles.d/nixos.conf:d /var/cache 0755 root root - entry on my system on nixos-unstable.

NilsIrl commented 5 years ago

@worldofpeace have you managed to get it working (for you)?

anderslundstedt commented 5 years ago
  1. services.spotifyd.config = "just password and username set"

Could you try without this? One should be able to connect to the service anyway if one is on the same network—at least that is how it works for me. I have never used this option so perhaps this option does not work as intended.

I am quite busy right now but could look more at this in a couple of days or so. Feel free to ping me in a week or so if the problem is not resolved and it seems like I have forgotten about it.

NilsIrl commented 5 years ago
  1. services.spotifyd.config = "just password and username set"

Could you try without this? One should be able to connect to the service anyway if one is on the same network

On the same network as what? The official Spotify client?

anderslundstedt commented 5 years ago

On the same network as what? The official Spotify client?

Sorry, it might be that I do not understand your use case. I have spotifyd running on a Raspberry Pi on my home network. I then use any official Spotify client to control the spotifyd instance (via Spotify Connect). This works without any username or password set as long as the client I connect with is connected to my home network. I guess this is not how you use spotifyd?

NilsIrl commented 5 years ago

I am running spotifyd on my local computer, the one I am using right now to type this.

And I hope to control spotifyd from the web interface. Also with this current setup that doesn't work I can control spotifyd but nothing plays.

NilsIrl commented 5 years ago

Sorry, it might be that I do not understand your use case. I have spotifyd running on a Raspberry Pi on my home network. I then use any official Spotify client to control the spotifyd instance (via Spotify Connect). This works without any username or password set as long as the client I connect with is connected to my home network.

How do you connect to the raspberry pi? Do you have to input the IP into the Spotify client?

anderslundstedt commented 5 years ago

How do you connect to the raspberry pi? Do you have to input the IP into the Spotify client?

No, I just use an official Spotify client and connect via Spotify Connect:

https://www.spotify.com/connect/

NilsIrl commented 5 years ago

This could be linked to the following line that appears when running sudo nixos-rebuild switch:

[/etc/tmpfiles.d/var.conf:19] Duplicate line for path "/var/cache", ignoring.

content of these files:

$ grep '/var/cache' /etc/tmpfiles.d/*
/etc/tmpfiles.d/nixos.conf:d /var/cache 0755 root root -
/etc/tmpfiles.d/systemd.conf:d /var/cache/private 0700 root root -
/etc/tmpfiles.d/var.conf:d /var/cache 0755 - - -

This seems to be caused partly by locate. Though disabling locate doesn't resolve the spotifyd problem.

https://github.com/NixOS/nixpkgs/blob/a6b64a828d161cf8471e0bf5e709cef99b11f4e1/nixos/modules/misc/locate.nix#L129-L134

colemickens commented 5 years ago

I haven't tried any workarounds yet, but I also have this issue when trying to get spotifyd working. This is still a regular single user laptop situation, but I'm still reluctant to change how pulseaudio is running just to get spotifyd working, but I'd like to use spotifyd+spotify-tui over using Spotify in browser or the electron client.

colemickens commented 4 years ago

This got the daemon up and running for me: https://github.com/colemickens/nixpkgs/commit/ece91964dc1

colemickens commented 4 years ago

Also, it was defaulting to alsa for me, I specified backend = pulseaudio in my config file.

jtojnar commented 4 years ago

Yeah, I think running spotifyd as user service is the correct solution, as that is what upstream suggests. We should install the upstream service file into the package and just do the following in the module:

systemd.packages = [ pkgs.spotifyd ];
systemd.user.services.spotifyd.ExecStart = [ "" "${pkgs.spotifyd}/bin/spotifyd --no-daemon --cache-path /var/cache/spotifyd --config-path ${spotifydConf}" ];
anderslundstedt commented 4 years ago

I would prefer having spotifyd running under a separate user. (I only interact with spotifyd via Spotify Connect on official clients.) What is the use case requiring spotifyd running as a user service?

jtojnar commented 4 years ago

I would guess that since MPRIS usually runs on session bus, we would need to run it as user service to be able to use that.

anderslundstedt commented 4 years ago

I see, thanks. Would it make sense to have an option which makes spotifyd run under a separate user?

NilsIrl commented 4 years ago

I see, thanks. Would it make sense to have an option which makes spotifyd run under a separate user?

I'm pretty sure that currently it runs under the spotifyd user

anderslundstedt commented 4 years ago

Yes, I meant if the default behaviour was changed to run it as a user service.

NilsIrl commented 4 years ago

Having the same problem with mpd :(

pinage404 commented 4 years ago

Hi,

I have a working configurations !

SpotifyD with PulseAudio in system wide mode

{ config, pkgs, ... }:

{
  hardware.pulseaudio = {
    enable = true;
    package = pkgs.pulseaudioFull;

    # there is no real user on the Raspberry Pi
    # running daemon system wide should be safe this allow some services (such as: SpotifyD)
    systemWide = true;
  };

  services.spotifyd = {
    # don't use SpotifyD service from nixpkgs
    # * PulseAudio seems to not work with DynamicUser
    # * cache's directory is forced
    enable = false;
  };

  # put configuration file on the default location for SpotifyD as per the documentation https://github.com/Spotifyd/spotifyd/blob/cfef0ad92f/README.md#L172
  environment.etc."xdg/spotifyd/spotifyd.conf".text = ''
    [global]
    username = pinage404
    password = ${builtins.readFile ./spotifyd.pass}

    backend = pulseaudio

    cache_path = ${config.users.users.spotifyd.home}/.cache/spotifyd/
  '';

  users = {
    users.spotifyd = {
      isSystemUser = true;
      home = "/home/spotifyd";
      createHome = true;
      description = "spotifyd";
      group = "spotifyd";
      shell = pkgs.bash;
      extraGroups = [ "audio" ];
    };
    groups.spotifyd = {};
  };

  systemd.services.spotifyd = {
    wantedBy = [ "multi-user.target" ];
    after = [ "network-online.target" "sound.target" ];
    description = "spotifyd, a Spotify playing daemon";
    serviceConfig = {
      ExecStart = "${pkgs.spotifyd}/bin/spotifyd --no-daemon";
      Restart = "always";
      RestartSec = 12;
      DynamicUser = false;
      User = "spotifyd";
      SupplementaryGroups = [ "audio" ];
    };
  };
}

Full and real configuration can be found here :

SpotifyD with ALSA

{
    sound.enable = true;

    services.spotifyd = {
        enable = true;
        config = ''
            [global]
            username = pinage404
            password = ${builtins.readFile ./spotifyd.pass}

            backend = alsa

            #cache_path = NixOS' service force it to /var/cache/spotifyd/
        '';
    };
}

Full and real configuration can be found here :

flokli commented 3 years ago

Is this still up to date?

In my case, the only config necessary was to enable anonymous auth on module-native-protocol-unix:

  hardware.pulseaudio.enable = true;
  hardware.pulseaudio.systemWide = true;

  # required for spotifyd to be able to connect
  hardware.pulseaudio.extraConfig = ''
    unload-module module-native-protocol-unix
    load-module module-native-protocol-unix auth-anonymous=1
  '';
  services.spotifyd = {
    enable = true;
    config = ''
      [global]
      zeroconf_port = 5354
      backend = pulseaudio
      bitrate = 320
    '';
  };
TheDelus commented 3 years ago

Here is my approach. I wanted to run it as a user unit

{ pkgs, ... }:
let
  spotifyd = pkgs.spotifyd.override { withMpris = true; withPulseAudio = true; };
  spotifydConf = pkgs.writeText "spotifyd-config" ''
    [global]
    username = "??????"
    password = "??????"
    backend = "pulseaudio"
    bitrate = 320
    use_mpris = true
  '';
in
{
  systemd.user.services.spotifyd = {
    wantedBy = [ "multi-user.target" ];
    after = [ "network-online.target" "sound.target" ];
    description = "spotifyd, a Spotify playing daemon";
    serviceConfig = {
      ExecStart = "${spotifyd}/bin/spotifyd --no-daemon --cache-path=\${HOME}/.cache/spotifyd --config-path=${spotifydConf}";
      Restart = "always";
      RestartSec = 12;
    };
  };

  environment.systemPackages = with pkgs; [
    spotify-tui
  ];
}

and

{ pkgs, ... }:
{
  sound.enable = true;
  hardware.pulseaudio = {
    enable = true;
    package = pkgs.pulseaudioFull;
  };
}
ChanceHarrison commented 1 year ago

I made the change in my config that @flokli suggested, and it worked for a little bit, but I'm still getting frequent service crashes:

spotifyd[232705]: Failed to create secure directory (//.config/pulse): Read-only file system
spotifyd[232705]: Audio Sink Error Connection Refused: <PulseAudioSink> Connection refused
systemd[1]: spotifyd.service: Main process exited, code=exited, status=1/FAILURE

Once the service restarts, any attempts to connect to the device result in another crash with the same error.

Even when it was working, the logs frequently included:

spotifyd[229817]: Failed to create secure directory (//.config/pulse): Read-only file system
spotifyd[229817]: Failed to load cookie file from cookie: Read-only file system

I'm not sure how to go about troubleshooting why sometimes the PA server supposedly refuses the connection (there are no related logs in the system journal) or why spotifyd continuously complains about being unable to create a pulse config directory or read a pulse cookie (it apparently doesn't know that anonymous auth is enabled).

I can only assume that there is still some lingering issue related to the service being run with DynamicUser=yes. But maybe the issue is specific to my setup? I can try to make a minimal, reproducible example if that would be helpful...

I would love to find a solution that avoids getting stuck in a crash-loop state that doesn't involve switching to a user service or disabling DynamicUser.

If anyone has any advice/suggestions to share, I would be quite grateful!

nixos-discourse commented 11 months ago

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/squeezelite-services-fails-to-start-because-it-cant-connect-to-pulseaudio/35724/1