8 Commits

9 changed files with 278 additions and 89 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
.hooks/*.sh linguist-vendored

View File

@@ -7,27 +7,29 @@ on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
fail_fast: fail_fast:
description: 'Fail fast strategy' description: "Use fail-fast strategy"
required: false required: false
default: 'true' default: "true"
verbose: verbose:
description: 'Verbose output' description: "Verbose output"
required: false required: false
default: 'false' default: "false"
push: push:
tags: tags:
- 'v*.*.*' - "v*.*.*"
paths-ignore: # We don't filter by paths here, because we filter by tags, which means we're releasing. All releases should be built.
- README.md
- .gitignore
- LICENSE
- run.sh
pull_request: pull_request:
paths-ignore: paths-ignore:
- .hooks/**
- CARGO_README.md
- README.md - README.md
- CHANGELOG.md
- INTEGRATION.md
- .gitignore - .gitignore
- LICENSE - .gitattributes
- LICENSE*
- run.sh - run.sh
- run.ps1
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
@@ -72,7 +74,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
with: with:
toolchain: 1.74 toolchain: 1.79
targets: ${{ matrix.target }} targets: ${{ matrix.target }}
- name: Cache Rust dependencies - name: Cache Rust dependencies
@@ -180,6 +182,7 @@ jobs:
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
with: with:
token: ${{ secrets.GH_RELEASE_TOKEN }}
files: | files: |
${{ env.ARCHIVE_DIR }}/${{ env.ARCHIVE }} ${{ env.ARCHIVE_DIR }}/${{ env.ARCHIVE }}
${{ env.ARCHIVE_DIR }}/${{ env.ARCHIVE }}.sig ${{ env.ARCHIVE_DIR }}/${{ env.ARCHIVE }}.sig

View File

@@ -4,18 +4,30 @@ on:
workflow_dispatch: workflow_dispatch:
push: push:
paths-ignore: paths-ignore:
- .hooks/**
- CARGO_README.md
- README.md - README.md
- CHANGELOG.md
- INTEGRATION.md
- .gitignore - .gitignore
- LICENSE - .gitattributes
- LICENSE*
- run.sh - run.sh
- run.ps1
pull_request: pull_request:
paths-ignore: paths-ignore:
- .hooks/**
- CARGO_README.md
- README.md - README.md
- CHANGELOG.md
- INTEGRATION.md
- .gitignore - .gitignore
- LICENSE - .gitattributes
- LICENSE*
- run.sh - run.sh
- run.ps1
schedule: schedule:
- cron: '30 14 * * 1' # every Monday, 9:30 AM CDT - cron: "30 14 * * 1" # every Monday, 9:30 AM CDT
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
@@ -33,8 +45,8 @@ jobs:
artifact: true artifact: true
- os: macos-13 - os: macos-13
target: x86_64-apple-darwin target: x86_64-apple-darwin
# - os: macos-latest # - os: macos-latest
# target: aarch64-apple-darwin # target: aarch64-apple-darwin
- os: windows-latest - os: windows-latest
target: x86_64-pc-windows-msvc target: x86_64-pc-windows-msvc
@@ -46,7 +58,7 @@ jobs:
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
with: with:
toolchain: 1.74 toolchain: 1.79
targets: ${{ matrix.target }} targets: ${{ matrix.target }}
- name: Install Linker Tools - name: Install Linker Tools

View File

@@ -1,3 +1,11 @@
> [!IMPORTANT]
> This application is not 'real'; I mostly created it to learn about building a CLI application in Rust with high-end CI/CD pipelines and easy to execute multi-platform scripts.
> Interestingly enough, I never really developed the program itself, just the scripts and the CI/CD pipeline.
> And by the time I was ready to write it, the `spotify-player` project had already solved the [primary issue](https://github.com/aome510/spotify-player/issues/201#issuecomment-2439565162) I was trying to address.
> Thus, I'm archiving this project, as it's no longer necessary.
>
> This project is still pretty cool though in it's own right, with multi-platform ephemeral binaries over `curl` commands, Windows support, Linux MUSL targets (smaller binaries), ARM64 MacOS support, binary deployment to GitHub pages, automatic GitHub release uploads, and a pretty decent README - this project has it all.
# spotify-quickauth # spotify-quickauth
[![Build Status](https://github.com/Xevion/spotify-quickauth/workflows/build/badge.svg)](https://github.com/Xevion/spotify-quickauth/actions) [![Build Status](https://github.com/Xevion/spotify-quickauth/workflows/build/badge.svg)](https://github.com/Xevion/spotify-quickauth/actions)
@@ -5,8 +13,8 @@
[![Crates.io](https://img.shields.io/crates/v/spotify-quickauth.svg)](https://crates.io/crates/spotify-quickauth) [![Crates.io](https://img.shields.io/crates/v/spotify-quickauth.svg)](https://crates.io/crates/spotify-quickauth)
![Crates.io MSRV](https://img.shields.io/crates/msrv/spotify-quickauth) ![Crates.io MSRV](https://img.shields.io/crates/msrv/spotify-quickauth)
![GitHub last commit](https://img.shields.io/github/last-commit/Xevion/spotify-quickauth) ![GitHub last commit](https://img.shields.io/github/last-commit/Xevion/spotify-quickauth)
<!-- TODO: Add testing status badge -->
<!-- TODO: Add testing status badge -->
A simple CLI-based application for creating a `credentials.json` file, used by `librespot` derived applications, such as [spotify-player][spotify-player], [spotifyd][spotifyd], and [raspotify][raspotify]. A simple CLI-based application for creating a `credentials.json` file, used by `librespot` derived applications, such as [spotify-player][spotify-player], [spotifyd][spotifyd], and [raspotify][raspotify].
@@ -24,6 +32,7 @@ curl -sSL https://xevion.github.io/spotify-quickauth/run.sh | sh -s --
The default invocation is likely fine for most users, it will try to understand the available paths for `credentials.json` to be written to, and allow you to select them. The default invocation is likely fine for most users, it will try to understand the available paths for `credentials.json` to be written to, and allow you to select them.
> [!NOTE]
> Automatic detection is dependent on the related software being installed and/or relevant configuration files being present. > Automatic detection is dependent on the related software being installed and/or relevant configuration files being present.
For **Windows**, you can paste this command into PowerShell: For **Windows**, you can paste this command into PowerShell:
@@ -36,6 +45,7 @@ iex (irm "https://xevion.github.io/spotify-quickauth/run.ps1")
This application is dead simple to use. Just run the command, and it'll tell you to connect to a fake 'device' in your Spotify interface. This application is dead simple to use. Just run the command, and it'll tell you to connect to a fake 'device' in your Spotify interface.
> [!NOTE]
> You must be connected to the same network running `spotify-quickauth`, as the `zeroconf` technology **does not work** across **networks** nor **proxies**. > You must be connected to the same network running `spotify-quickauth`, as the `zeroconf` technology **does not work** across **networks** nor **proxies**.
Once you connect, the credentials file will be created, and you'll be prompted to select which location(s) to place it in. Even if none of the relevant `librespot` applications are detected or installed, you can specify manual locations, or the current working directory. Once you connect, the credentials file will be created, and you'll be prompted to select which location(s) to place it in. Even if none of the relevant `librespot` applications are detected or installed, you can specify manual locations, or the current working directory.
@@ -44,14 +54,15 @@ Once you connect, the credentials file will be created, and you'll be prompted t
Installation is not necessary to use this application, but if you're having trouble, want to compile it yourself, or are using it frequently, you might want to install it. Installation is not necessary to use this application, but if you're having trouble, want to compile it yourself, or are using it frequently, you might want to install it.
>The scripts above can be given the `-K` or `--keep` flag to keep the downloaded binary. This will prevent repeated API calls to GitHub if you're using the script frequently within a short period. > [!NOTE]
> The scripts above can be given the `-K` or `--keep` flag to keep the downloaded binary. This will prevent repeated API calls to GitHub if you're using the script frequently within a short period.
### Pre-built Binaries ### Pre-built Binaries
Binaries are always available for download from the [releases page][latestRelease], and they're the same ones used by the shell scripts above. Binaries are always available for download from the [releases page][latestRelease], and they're the same ones used by the shell scripts above.
Currently, the following targets are available for download: Currently, the following targets are available for download:
- x64 Linux (MUSL) `x86_64-unknown-linux-musl` - x64 Linux (MUSL) `x86_64-unknown-linux-musl`
- ARM64 Linux (MUSL) `aarch64-unknown-linux-musl` - ARM64 Linux (MUSL) `aarch64-unknown-linux-musl`
- ARMv7 Linux `armv7-unknown-linux-musleabihf` - ARMv7 Linux `armv7-unknown-linux-musleabihf`
@@ -64,6 +75,7 @@ Please [file an issue][new-issue] if you are on a platform that is not supported
### Via `cargo-binstall` ### Via `cargo-binstall`
> [!NOTE]
> If the package cannot be found for your target or fails to be downloaded for any reason, `cargo-binstall` will automatically fall back to building the package from source. > If the package cannot be found for your target or fails to be downloaded for any reason, `cargo-binstall` will automatically fall back to building the package from source.
`cargo-binstall` is a tool that allows you to install binaries from crates.io without needing to compile them yourself. `cargo-binstall` is a tool that allows you to install binaries from crates.io without needing to compile them yourself.
@@ -74,7 +86,6 @@ cargo binstall spotify-quickauth
If you're curious where the binary comes from, `cargo-binstall` will likely pull the binary directly from the [latest release][latestRelease] by this repository, selecting the most appropriate target for your host. If you're curious where the binary comes from, `cargo-binstall` will likely pull the binary directly from the [latest release][latestRelease] by this repository, selecting the most appropriate target for your host.
### Manual Installation ### Manual Installation
If you'd like to use the shell script above to install the binary, you can use the `-S/--stop` flag to prevent the script from running the binary after downloading it. It implicitly applies the `--keep` flag too. If you'd like to use the shell script above to install the binary, you can use the `-S/--stop` flag to prevent the script from running the binary after downloading it. It implicitly applies the `--keep` flag too.
@@ -103,6 +114,7 @@ spotify-quickauth --help
``` ```
If you have any troubles building the project If you have any troubles building the project
- Make sure you're using a target that's supported by the project (see above). - Make sure you're using a target that's supported by the project (see above).
- Certain targets may require specfic linkers. For example, - Certain targets may require specfic linkers. For example,

View File

@@ -2,10 +2,36 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## v0.1.7
### Added
- Archival notice.
### Changed
- Bumped MSRV to 1.79 to access stabilized `std::path::absolute`
- Adjusted `github-linguist` statistics, ignoring `.hooks`
- Widened `paths-ignore` for `build` and `test` workflows.
- Switched release step token to use `secrets.GH_RELEASE_TOKEN`, so the author is bound to `Xevion` instead of `github-actions` (a bot).
- Switched changelog format to `keep-a-changelog` style.
### Removed
- Removed `paths-ignore` for `build` workflow on `push` trigger.
## v0.1.6 ## v0.1.6
### Added
- Began tracking changes in the `CHANGELOG.md` file. - Began tracking changes in the `CHANGELOG.md` file.
- Added testing workflow to the repository, targeting only major x64 platforms.
- Added testing badge to README
### Changed
- [breaking] Lowered MSRV to 1.74 to match `librespot`'s `0.5.0-dev` branch. - [breaking] Lowered MSRV to 1.74 to match `librespot`'s `0.5.0-dev` branch.
- Fixed `pre-commit` hook to properly process the `CARGO_README.md` file. - Fixed `pre-commit` hook to properly process the `CARGO_README.md` file.
- Added testing workflow to the repository, targeting only major x64 platforms.
- New testing badge in README

2
Cargo.lock generated
View File

@@ -1287,7 +1287,7 @@ dependencies = [
[[package]] [[package]]
name = "spotify-quickauth" name = "spotify-quickauth"
version = "0.1.6" version = "0.1.7"
dependencies = [ dependencies = [
"env_logger", "env_logger",
"futures", "futures",

View File

@@ -1,9 +1,9 @@
[package] [package]
name = "spotify-quickauth" name = "spotify-quickauth"
version = "0.1.6" version = "0.1.7"
edition = "2021" edition = "2021"
description = "Quickly authenticate librespot-based applications with Spotify" description = "Quickly authenticate librespot-based applications with Spotify"
rust-version = "1.74" rust-version = "1.79"
authors = ["Ryan Walters <xevion@xevion.dev>"] authors = ["Ryan Walters <xevion@xevion.dev>"]
homepage = "https://github.com/Xevion/spotify-quickauth" homepage = "https://github.com/Xevion/spotify-quickauth"
repository = "https://github.com/Xevion/spotify-quickauth" repository = "https://github.com/Xevion/spotify-quickauth"

View File

@@ -1,3 +1,11 @@
> [!IMPORTANT]
> This application is not 'real'; I mostly created it to learn about building a CLI application in Rust with high-end CI/CD pipelines and easy to execute multi-platform scripts.
> Interestingly enough, I never really developed the program itself, just the scripts and the CI/CD pipeline.
> And by the time I was ready to write it, the `spotify-player` project had already solved the [primary issue](https://github.com/aome510/spotify-player/issues/201#issuecomment-2439565162) I was trying to address.
> Thus, I'm archiving this project, as it's no longer necessary.
>
> This project is still pretty cool though in it's own right, with multi-platform ephemeral binaries over `curl` commands, Windows support, Linux MUSL targets (smaller binaries), ARM64 MacOS support, binary deployment to GitHub pages, automatic GitHub release uploads, and a pretty decent README - this project has it all.
# spotify-quickauth # spotify-quickauth
[![Build Status](https://github.com/Xevion/spotify-quickauth/workflows/build/badge.svg)](https://github.com/Xevion/spotify-quickauth/actions) [![Build Status](https://github.com/Xevion/spotify-quickauth/workflows/build/badge.svg)](https://github.com/Xevion/spotify-quickauth/actions)
@@ -5,8 +13,8 @@
[![Crates.io](https://img.shields.io/crates/v/spotify-quickauth.svg)](https://crates.io/crates/spotify-quickauth) [![Crates.io](https://img.shields.io/crates/v/spotify-quickauth.svg)](https://crates.io/crates/spotify-quickauth)
![Crates.io MSRV](https://img.shields.io/crates/msrv/spotify-quickauth) ![Crates.io MSRV](https://img.shields.io/crates/msrv/spotify-quickauth)
![GitHub last commit](https://img.shields.io/github/last-commit/Xevion/spotify-quickauth) ![GitHub last commit](https://img.shields.io/github/last-commit/Xevion/spotify-quickauth)
<!-- TODO: Add testing status badge -->
<!-- TODO: Add testing status badge -->
A simple CLI-based application for creating a `credentials.json` file, used by `librespot` derived applications, such as [spotify-player][spotify-player], [spotifyd][spotifyd], and [raspotify][raspotify]. A simple CLI-based application for creating a `credentials.json` file, used by `librespot` derived applications, such as [spotify-player][spotify-player], [spotifyd][spotifyd], and [raspotify][raspotify].
@@ -24,7 +32,7 @@ curl -sSL https://xevion.github.io/spotify-quickauth/run.sh | sh -s --
The default invocation is likely fine for most users, it will try to understand the available paths for `credentials.json` to be written to, and allow you to select them. The default invocation is likely fine for most users, it will try to understand the available paths for `credentials.json` to be written to, and allow you to select them.
>[!NOTE] > [!NOTE]
> Automatic detection is dependent on the related software being installed and/or relevant configuration files being present. > Automatic detection is dependent on the related software being installed and/or relevant configuration files being present.
For **Windows**, you can paste this command into PowerShell: For **Windows**, you can paste this command into PowerShell:
@@ -37,7 +45,7 @@ iex (irm "https://xevion.github.io/spotify-quickauth/run.ps1")
This application is dead simple to use. Just run the command, and it'll tell you to connect to a fake 'device' in your Spotify interface. This application is dead simple to use. Just run the command, and it'll tell you to connect to a fake 'device' in your Spotify interface.
>[!NOTE] > [!NOTE]
> You must be connected to the same network running `spotify-quickauth`, as the `zeroconf` technology **does not work** across **networks** nor **proxies**. > You must be connected to the same network running `spotify-quickauth`, as the `zeroconf` technology **does not work** across **networks** nor **proxies**.
Once you connect, the credentials file will be created, and you'll be prompted to select which location(s) to place it in. Even if none of the relevant `librespot` applications are detected or installed, you can specify manual locations, or the current working directory. Once you connect, the credentials file will be created, and you'll be prompted to select which location(s) to place it in. Even if none of the relevant `librespot` applications are detected or installed, you can specify manual locations, or the current working directory.
@@ -46,15 +54,15 @@ Once you connect, the credentials file will be created, and you'll be prompted t
Installation is not necessary to use this application, but if you're having trouble, want to compile it yourself, or are using it frequently, you might want to install it. Installation is not necessary to use this application, but if you're having trouble, want to compile it yourself, or are using it frequently, you might want to install it.
>[!NOTE] > [!NOTE]
>The scripts above can be given the `-K` or `--keep` flag to keep the downloaded binary. This will prevent repeated API calls to GitHub if you're using the script frequently within a short period. > The scripts above can be given the `-K` or `--keep` flag to keep the downloaded binary. This will prevent repeated API calls to GitHub if you're using the script frequently within a short period.
### Pre-built Binaries ### Pre-built Binaries
Binaries are always available for download from the [releases page][latestRelease], and they're the same ones used by the shell scripts above. Binaries are always available for download from the [releases page][latestRelease], and they're the same ones used by the shell scripts above.
Currently, the following targets are available for download: Currently, the following targets are available for download:
- x64 Linux (MUSL) `x86_64-unknown-linux-musl` - x64 Linux (MUSL) `x86_64-unknown-linux-musl`
- ARM64 Linux (MUSL) `aarch64-unknown-linux-musl` - ARM64 Linux (MUSL) `aarch64-unknown-linux-musl`
- ARMv7 Linux `armv7-unknown-linux-musleabihf` - ARMv7 Linux `armv7-unknown-linux-musleabihf`
@@ -67,7 +75,7 @@ Please [file an issue][new-issue] if you are on a platform that is not supported
### Via `cargo-binstall` ### Via `cargo-binstall`
>[!NOTE] > [!NOTE]
> If the package cannot be found for your target or fails to be downloaded for any reason, `cargo-binstall` will automatically fall back to building the package from source. > If the package cannot be found for your target or fails to be downloaded for any reason, `cargo-binstall` will automatically fall back to building the package from source.
`cargo-binstall` is a tool that allows you to install binaries from crates.io without needing to compile them yourself. `cargo-binstall` is a tool that allows you to install binaries from crates.io without needing to compile them yourself.
@@ -78,7 +86,6 @@ cargo binstall spotify-quickauth
If you're curious where the binary comes from, `cargo-binstall` will likely pull the binary directly from the [latest release][latestRelease] by this repository, selecting the most appropriate target for your host. If you're curious where the binary comes from, `cargo-binstall` will likely pull the binary directly from the [latest release][latestRelease] by this repository, selecting the most appropriate target for your host.
### Manual Installation ### Manual Installation
If you'd like to use the shell script above to install the binary, you can use the `-S/--stop` flag to prevent the script from running the binary after downloading it. It implicitly applies the `--keep` flag too. If you'd like to use the shell script above to install the binary, you can use the `-S/--stop` flag to prevent the script from running the binary after downloading it. It implicitly applies the `--keep` flag too.
@@ -107,6 +114,7 @@ spotify-quickauth --help
``` ```
If you have any troubles building the project If you have any troubles building the project
- Make sure you're using a target that's supported by the project (see above). - Make sure you're using a target that's supported by the project (see above).
- Certain targets may require specfic linkers. For example, - Certain targets may require specfic linkers. For example,

View File

@@ -1,42 +1,174 @@
use std::{env, fs::File, process::exit}; use std::{env, fs::File, path::PathBuf, process::exit};
use futures::StreamExt; use futures::StreamExt;
use librespot_core::config::DeviceType; use librespot_core::config::DeviceType;
use librespot_discovery::Discovery; use librespot_discovery::Discovery;
use log::{info, warn}; use log::{debug, error, info, warn};
use sha1::{Digest, Sha1}; use sha1::{Digest, Sha1};
use std::io::Write; use std::io::Write;
struct Arguments {
force: bool,
path: PathBuf,
}
fn parse_arguments(args: &Vec<String>) -> Result<Arguments, String> {
let mut force: Option<bool> = None;
let mut path: Option<PathBuf> = None;
let mut skip = 1;
// If '--' is provided, all arguments before it are skipped
if let Some(index) = args.iter().position(|arg| arg == "--") {
skip = index + 1;
}
let args = &args[skip..];
debug!("Arguments: {:?}", args);
for arg in args.iter() {
match arg.as_str() {
"-f" | "--force" => {
if force.is_some() {
return Err("Force flag provided multiple times".to_string());
}
force = Some(true)
}
_ => {
if path.is_some() {
return Err("Path provided multiple times".to_string());
}
// Parse the path, validate that the argument looks like a path
let parsed = PathBuf::from(arg);
debug!("Parsed path: {}", parsed.display());
if parsed.exists() {
if parsed.is_dir() {
path = Some(parsed.join("credentials.json"));
} else if parsed.is_file() {
if path.is_some() {
return Err("Path provided multiple times".to_string());
}
path = Some(parsed);
} else {
return Err("Path is not a file or directory".to_string());
}
} else {
// File does not exist, check if it looks like a directory
if parsed.ends_with("/") || parsed.ends_with("\\") {
// If the parent directory exists, it's okay to create a directory then the file in it
if parsed.parent().is_some_and(|p| p.exists()) {
path = Some(parsed.join("credentials.json"));
} else {
return Err(
"Cannot create more than one folder for output path".to_string()
);
}
} else {
// No need to create a directory, just create the file
if parsed.parent().is_some_and(|p| p.exists()) {
path = Some(parsed);
} else {
return Err(
"Cannot create a file in a non-existent directory".to_string()
);
}
}
}
}
}
}
// If no path was provided, default to credentials.json in the current directory
let path = match path {
Some(p) => match std::path::absolute(p) {
Ok(p) => p,
Err(e) => return Err(format!("Invalid path: {}", e)),
},
None => {
// No path provided, try to identify a default in the current directory
let pwd = env::current_dir();
if pwd.is_err() {
// For some reason the current directory
return Err("Current directory is invalid or indeterminate, please provide an explicit output path".to_string());
}
// Default to credentials.json in the current directory
pwd.unwrap().join("credentials.json")
}
};
// If the *file* already exists, check if the force flag was provided
if path.exists() && path.is_file() && !force.unwrap_or(false) {
return Err(format!(
"Output file already exists, use -f to overwrite ({})",
path.display()
));
}
Ok(Arguments {
force: force.unwrap_or(false),
path,
})
}
#[tokio::main(flavor = "current_thread")] #[tokio::main(flavor = "current_thread")]
async fn main() { async fn main() {
// Initialize the logger
if env::var("RUST_LOG").is_err() { if env::var("RUST_LOG").is_err() {
env::set_var("RUST_LOG", "info") env::set_var("RUST_LOG", "info")
} }
env_logger::builder().init(); env_logger::builder().init();
let credentials_file = match home::home_dir() { // Parse the arguments
// ~/.cache/spotify_player/credentials.json let args = match parse_arguments(&env::args().collect()) {
Some(path) => path.join(".cache/spotify_player/credentials.json"), Ok(a) => a,
None => { Err(e) => {
warn!("Cannot determine home directory for credentials file."); error!("Error parsing arguments: {}", e);
exit(1); exit(1);
} }
}; };
info!("Credentials file: {}", &credentials_file.display());
// TODO: If credentials file exists, confirm overwrite info!("Credentials file: {}", &args.path.display());
if credentials_file.exists() {
warn!("Credentials file already exists: {}", &credentials_file.display());
exit(1);
}
// TODO: If spotifyd is running, ask if shutdown is desired // TODO: If spotifyd is running, ask if shutdown is desired
let username = match env::consts::OS { // Figure out the username
let mut username = match env::consts::OS {
"windows" => env::var("USERNAME"), "windows" => env::var("USERNAME"),
_ => env::var("USER"), _ => env::var("USER"),
}.unwrap_or_else(|_| "unknown".to_string()); }
// Trim whitespace from the username
.map(|u| u.trim().to_string())
.unwrap_or("unknown".to_string());
// Default the username to 'unknown' if it doesn't fit the expected format
if username != "unknown" {
let valid_characters = r"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
if match &username {
u if u.is_empty() => {
warn!("Cannot determine username, defaulting to 'unknown'");
true
}
u if u.len() > 20 => {
warn!("Username is too long, defaulting to 'unknown'");
true
}
u if u.len() < 2 => {
warn!("Username is too short, defaulting to 'unknown'");
true
}
u if u.contains(|c| !valid_characters.contains(c)) => {
warn!("Username contains invalid characters, defaulting to 'unknown'");
true
}
_ => false,
} {
username = "unknown".to_string();
}
}
// Create the device metadata
let device_name = format!("spotify-quickauth-{}", username); let device_name = format!("spotify-quickauth-{}", username);
let device_id = hex::encode(Sha1::digest(device_name.as_bytes())); let device_id = hex::encode(Sha1::digest(device_name.as_bytes()));
let device_type = DeviceType::Computer; let device_type = DeviceType::Computer;
@@ -47,35 +179,30 @@ async fn main() {
.launch() .launch()
.unwrap(); .unwrap();
println!("Open Spotify and select output device: {}", device_name); info!("Open Spotify and select output device: {}", device_name);
let mut written = false;
while let Some(credentials) = server.next().await { while let Some(credentials) = server.next().await {
let result = File::create("./credentials.json").and_then(|mut file| { // Check if file exists
if args.path.exists() && !args.force {
warn!("Output file already exists (appeared after startup), use -f to overwrite");
exit(1);
}
// Write the credentials to the file
let result = File::create(&args.path).and_then(|mut file| {
let data = serde_json::to_string(&credentials)?; let data = serde_json::to_string(&credentials)?;
write!(file, "{data}") write!(file, "{data}")
}); });
written = true;
// Check if the file was created successfully
if let Err(e) = result { if let Err(e) = result {
warn!("Cannot save credentials to cache: {}", e); warn!("Cannot save credentials to cache: {}", e);
exit(1); exit(1);
} else { } else {
println!("Credentials saved: {}", &credentials_file.display()); info!("Credentials saved: {}", &args.path.display());
exit(0); exit(0);
} }
} }
if !written {
warn!("No credentials were written.");
exit(1);
}
} }
mod tests {}
mod tests {
#[test]
fn test_nothing() {
assert_eq!(1, 1);
}
}