Skip to content

Commit

Permalink
Merge pull request #7 from kpcyrd/config
Browse files Browse the repository at this point in the history
Allow passing additional arguments to spotify binary
  • Loading branch information
kpcyrd committed Aug 15, 2022
2 parents 2fbaa42 + 0bcdba3 commit 9cc6c75
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 135 deletions.
227 changes: 121 additions & 106 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ dirs = "4.0.0"
env_logger = "0.9.0"
libflate = "1.2.0"
log = "0.4.17"
nix = { version = "0.24.1", default-features = false, features = ["process"] }
nix = { version = "0.25", default-features = false, features = ["process"] }
reqwest = "0.11.10"
serde = { version = "1.0.137", features = ["derive"] }
serde_json = "1.0.81"
sha2 = "0.10.2"
tar = "0.4.38"
tempfile = "3.3.0"
tokio = { version = "1.18.1", features = ["macros", "rt-multi-thread", "process"] }
toml = "0.5.9"
xch = "1.1.0"
2 changes: 2 additions & 0 deletions PKGBUILD
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ arch=('x86_64')
license=('MIT' 'Apache')
depends=('sequoia-sqv' 'zenity' 'alsa-lib>=1.0.14' 'gtk3' 'libxss' 'desktop-file-utils' 'openssl' 'nss' 'at-spi2-atk' 'libcurl-gnutls' 'libsm')
makedepends=('cargo')
backup=('etc/spotify-launcher.conf')

build() {
cd ..
Expand All @@ -24,6 +25,7 @@ package() {

install -Dm644 contrib/spotify-launcher.desktop -t "${pkgdir}/usr/share/applications"
install -Dm644 contrib/icons/spotify-linux-512.png "${pkgdir}/usr/share/pixmaps/spotify-launcher.png"
install -Dm644 contrib/spotify-launcher.conf -t "${pkgdir}/etc"

for size in 22 24 32 48 64 128 256 512; do
install -Dm644 "contrib/icons/spotify-linux-${size}.png" \
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,27 @@
Spotify has a free linux client but prohibits re-distribution, so this is a freely distributable opensource program that manages a spotify installation in your home folder from the official spotify release server.

If you work for spotify please reach out so we can talk. 👉👈

## Configuration

spotify-launcher is going to look for a configuration file at the following locations (in this order):

- `${XDG_CONFIG_HOME:-$HOME/.config}/spotify-launcher.conf`
- `/etc/spotify-launcher.conf`

If no config is found it's going to start with default settings. Your configuration file may look like this:

```toml
[spotify]
## Pass extra arguments to the spotify executable
## You can test this with `spotify-launcher -v --skip-update --no-exec`
#extra_arguments = []
## On HiDPI displays spotify doesn't pick up the scale factor automatically
#extra_arguments = ["--force-device-scale-factor=2.0"]
## On wayland you might need
#extra_arguments = ["--enable-features=UseOzonePlatform", "--ozone-platform=wayland"]
```

## License

GPLv3+
8 changes: 8 additions & 0 deletions contrib/spotify-launcher.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[spotify]
## Pass extra arguments to the spotify executable
## You can test this with `spotify-launcher -v --skip-update --no-exec`
#extra_arguments = []
## On HiDPI displays spotify doesn't pick up the scale factor automatically
#extra_arguments = ["--force-device-scale-factor=2.0"]
## On wayland you might need
#extra_arguments = ["--enable-features=UseOzonePlatform", "--ozone-platform=wayland"]
72 changes: 72 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use crate::errors::*;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::{Path, PathBuf};

#[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct ConfigFile {
#[serde(default)]
pub spotify: SpotifyConfig,
}

impl ConfigFile {
pub fn parse(s: &str) -> Result<ConfigFile> {
let c = toml::from_str(s)?;
Ok(c)
}

pub fn load_from(path: &Path) -> Result<ConfigFile> {
info!("Loading configuration file at {:?}", path);
let buf = fs::read_to_string(path)
.with_context(|| anyhow!("Failed to read config file at {:?}", path))?;
Self::parse(&buf)
}

pub fn locate_file() -> Result<Option<PathBuf>> {
for path in &[dirs::config_dir(), Some(PathBuf::from("/etc/"))] {
if let Some(path) = path {
let path = path.join("spotify-launcher.conf");
debug!("Searching for configuration file at {:?}", path);
if path.exists() {
debug!("Found configuration file at {:?}", path);
return Ok(Some(path));
}
}
}
Ok(None)
}

pub fn load() -> Result<ConfigFile> {
if let Some(path) = Self::locate_file()? {
Self::load_from(&path)
} else {
info!("No configuration file found, using default config");
Ok(Self::default())
}
}
}

#[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct SpotifyConfig {
#[serde(default)]
pub extra_arguments: Vec<String>,
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_empty_config() -> Result<()> {
let cf = ConfigFile::parse("")?;
assert_eq!(cf, ConfigFile::default());
Ok(())
}

#[test]
fn test_empty_spotify_config() -> Result<()> {
let cf = ConfigFile::parse("[spotify]")?;
assert_eq!(cf, ConfigFile::default());
Ok(())
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod apt;
pub mod args;
pub mod config;
pub mod crypto;
pub mod deb;
pub mod errors;
Expand Down
82 changes: 54 additions & 28 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ use env_logger::Env;
use libflate::gzip::Decoder;
use spotify_launcher::apt::Client;
use spotify_launcher::args::Args;
use spotify_launcher::config::ConfigFile;
use spotify_launcher::errors::*;
use spotify_launcher::paths;
use std::ffi::CString;
use std::fs;
use std::io::Read;
use std::path::Path;
use std::time::Duration;
use std::time::SystemTime;

Expand Down Expand Up @@ -37,18 +39,7 @@ struct VersionCheck {
version: String,
}

#[tokio::main]
async fn main() -> Result<()> {
let args = Args::parse();

let log_level = match args.verbose {
0 => "info",
1 => "info,spotify_launcher=debug",
2 => "debug",
_ => "trace",
};
env_logger::init_from_env(Env::default().default_filter_or(log_level));

async fn update(args: &Args, install_path: &Path) -> Result<()> {
let state = paths::load_state_file()?;
let should_update = if args.force_update || args.check_update {
true
Expand All @@ -61,20 +52,17 @@ async fn main() -> Result<()> {
let days_since = hours_since / 24;
let hours_since = hours_since % 24;

debug!("Last update check was {} days and {} hours ago", days_since, hours_since);
debug!(
"Last update check was {} days and {} hours ago",
days_since, hours_since
);
since_update >= Duration::from_secs(UPDATE_CHECK_INTERVAL)
} else {
true
};

let install_path = if let Some(path) = &args.install_dir {
path.clone()
} else {
paths::install_path()?
};

if should_update {
let update = if let Some(deb_path) = args.deb {
let update = if let Some(deb_path) = &args.deb {
let deb = fs::read(&deb_path)
.with_context(|| anyhow!("Failed to read .deb file from {:?}", deb_path))?;
VersionCheck {
Expand Down Expand Up @@ -109,7 +97,7 @@ async fn main() -> Result<()> {
let mut tar = &tar[..];
let mut tar = tar::Archive::new(&mut tar);

let new_install_path = if let Some(path) = args.install_dir {
let new_install_path = if let Some(path) = args.install_dir.clone() {
path
} else {
paths::new_install_path()?
Expand Down Expand Up @@ -145,20 +133,58 @@ async fn main() -> Result<()> {
} else {
info!("No update needed");
}
Ok(())
}

fn start(args: &Args, cf: &ConfigFile, install_path: &Path) -> Result<()> {
let bin = install_path.join("usr/bin/spotify");
let bin = CString::new(bin.to_string_lossy().as_bytes())?;

let mut exec_args = vec![CString::new("spotify")?];

for arg in cf.spotify.extra_arguments.iter().cloned() {
exec_args.push(CString::new(arg)?);
}

if let Some(uri) = &args.uri {
exec_args.push(CString::new(format!("--uri={}", uri))?);
}

debug!("Assembled command: {:?}", exec_args);

if args.no_exec {
info!("Skipping exec because --no-exec was used");
} else {
let bin = install_path.join("usr/bin/spotify");
let bin = CString::new(bin.to_string_lossy().as_bytes())?;

let mut exec_args = vec![CString::new("spotify")?];
if let Some(uri) = args.uri {
exec_args.push(CString::new(format!("--uri={}", uri))?);
}
nix::unistd::execv(&bin, &exec_args)
.with_context(|| anyhow!("Failed to exec {:?}", bin))?;
}

Ok(())
}

#[tokio::main]
async fn main() -> Result<()> {
let args = Args::parse();

let log_level = match args.verbose {
0 => "info",
1 => "info,spotify_launcher=debug",
2 => "debug",
_ => "trace",
};
env_logger::init_from_env(Env::default().default_filter_or(log_level));

let cf = ConfigFile::load().context("Failed to load configuration")?;

let install_path = if let Some(path) = &args.install_dir {
path.clone()
} else {
paths::install_path()?
};
debug!("Using install path: {:?}", install_path);

update(&args, &install_path).await?;
start(&args, &cf, &install_path)?;

Ok(())
}

0 comments on commit 9cc6c75

Please sign in to comment.