Compare commits

...

2 commits

8 changed files with 297 additions and 84 deletions

View file

@ -1,5 +0,0 @@
#PLAYER_BUS="org.mpris.MediaPlayer2.mpv"
USERNAME="ILoveLastDotFm"
PASSWORD="lastfm4lyfe"
API_KEY="t8mb4e0kn9it150amaeezbcfe3iusy8o"
API_SECRET="jd47iz6h1u2kbnbfm8c763lzrj1o89h3"

211
Cargo.lock generated
View file

@ -136,10 +136,25 @@ dependencies = [
]
[[package]]
name = "dotenvy"
version = "0.15.7"
name = "dirs"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.48.0",
]
[[package]]
name = "enum-kinds"
@ -152,26 +167,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "envconfig"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea81cc7e21f55a9d9b1efb6816904978d0bfbe31a50347cb24b2e75564bcac9b"
dependencies = [
"envconfig_derive",
]
[[package]]
name = "envconfig_derive"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dfca278e5f84b45519acaaff758ebfa01f18e96998bc24b8f1b722dd804b9bf"
dependencies = [
"proc-macro2",
"quote 1.0.36",
"syn 1.0.109",
]
[[package]]
name = "errno"
version = "0.3.9"
@ -179,7 +174,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys",
"windows-sys 0.52.0",
]
[[package]]
@ -249,6 +244,17 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "heck"
version = "0.3.3"
@ -296,10 +302,11 @@ name = "lastfmpris"
version = "0.1.0"
dependencies = [
"anyhow",
"dotenvy",
"envconfig",
"dirs",
"mpris",
"rustfm-scrobble-proxy",
"serde",
"serde_ini",
"tracing",
"tracing-subscriber",
]
@ -325,6 +332,16 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "libredox"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags",
"libc",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
@ -448,6 +465,12 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "overload"
version = "0.1.1"
@ -496,6 +519,23 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_users"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
dependencies = [
"getrandom",
"libredox",
"thiserror",
]
[[package]]
name = "result"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "194d8e591e405d1eecf28819740abed6d719d1a2db87fc0bcdedee9a26d55560"
[[package]]
name = "rustfm-scrobble-proxy"
version = "2.0.0"
@ -519,7 +559,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
"windows-sys 0.52.0",
]
[[package]]
@ -534,7 +574,7 @@ version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
dependencies = [
"windows-sys",
"windows-sys 0.52.0",
]
[[package]]
@ -562,24 +602,35 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.204"
version = "1.0.209"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.204"
version = "1.0.209"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
dependencies = [
"proc-macro2",
"quote 1.0.36",
"syn 2.0.72",
]
[[package]]
name = "serde_ini"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb236687e2bb073a7521c021949be944641e671b8505a94069ca37b656c81139"
dependencies = [
"result",
"serde",
"void",
]
[[package]]
name = "serde_json"
version = "1.0.122"
@ -677,7 +728,7 @@ dependencies = [
"fastrand",
"once_cell",
"rustix",
"windows-sys",
"windows-sys 0.52.0",
]
[[package]]
@ -838,6 +889,18 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
@ -860,13 +923,37 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
@ -875,28 +962,46 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
@ -909,24 +1014,48 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"

View file

@ -7,9 +7,10 @@ edition = "2021"
[dependencies]
anyhow = "1.0.86"
dotenvy = "0.15.7"
envconfig = "0.10.0"
dirs = "5.0.1"
mpris = "2.0.1"
rustfm-scrobble-proxy = "2.0.0"
serde = "1.0.209"
serde_ini = "0.2.0"
tracing = "0.1.40"
tracing-subscriber = "0.3.18"

View file

@ -6,34 +6,52 @@ a rust application to scrobble your currently playing song on last.fm with mpris
unfortunately, because of how most mpris players work, the [Track_Id](https://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html#Simple-Type:Track_Id) (unique identifier for every song in the tracklist) object is very rarely supported, or, if supported, implemented incorrectly (looking at you, [Cider](https://cider.sh/)) meaning it is impossible to tell if the track has been played twice in a row. this means you cannot have succssive scrobbles of the same song, sorry! besides this, i'd say the program is feature complete.
also you can't configure the program through nix yet because i'm lazy, i'll do that sometime!!
## configuration
copy the values from `.env.example` into a `.env` file and configure your api keys, and the api keys are available [here](https://www.last.fm/login?next=/api/account/create)
either run the program once and navigate to `.config/lastmpris/config.ini` or create the file and copy it from the `config.example.ini` file provided in this directory. once you do that, configure your authentication methods. the api keys are available [here](https://www.last.fm/login?next=/api/account/create)
you also have the option to define your mpris bus name, which will instead wait for your specified player instead of waiting for the first active player
## how to install
*if you're on a setup that uses nix, you can just use `nix build` or the provided devshell to build the application.*
you have two methods of installation
first, clone repository:
1. install with cargo + your package manager of choice
1. obtain dependencies (this will vary on your package manager)
```sh
sudo apk add install pkgconf openssl dbus
```
2. run/build
```sh
cargo build --release
```
2. install using nix
```sh
git clone https://git.reidlab.pink/reidlab/lastfmpris && cd lastfmpris
```
simply run it with nix:
second, obtain dependencies (this will vary on your package manager, here, alpine is used)
```sh
nix run git+https://git.reidlab.pink/reidlab/lastfmpris
```
```sh
sudo apk add install pkgconf openssl dbus
```
or put it into your flake as such:
finally, run/build
```nix
# add this into your flake inputs:
lastfmpris.url = "git+https://git.reidlab.pink/reidlab/lastfmpris";
# add this to your `homeConfiguration`:
imports = [
inputs.lastfmpris.nixosModules.lastfmpris
];
```
```sh
cargo run
```
```sh
cargo build --release
```
and then just add this to use it as a home-manager option:
```nix
# add this to your home-manager configuration:
services.lastfmpris = {
enable = true;
}
```

5
config.example.ini Normal file
View file

@ -0,0 +1,5 @@
; player_bus = org.mpris.MediaPlayer2.mpv
username = ILoveLastDotFm
password = lastfm4lyfe
api_key = t8mb4e0kn9it150amaeezbcfe3iusy8o
api_secret = jd47iz6h1u2kbnbfm8c763lzrj1o89h3

View file

@ -30,7 +30,7 @@
# uncomment this and let the build fail, then get the current hash
# very scuffed but endorsed!
#cargoSha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
cargoSha256 = "sha256-2bDHxZc5Zg7jmA8BenSbTF3fBJEB7QDEVBQLkI8qyRc=";
cargoSha256 = "sha256-KOMEvg3iGjmMEFxsrIqD4SaDw63ojiNNNqCfzRlbYLw=";
buildInputs = with pkgs; [ pkg-config ];
@ -54,5 +54,37 @@
RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library";
};
});
}) // {
homeManagerModules = {
lastfmpris = { config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.lastfmpris;
in {
options.services.lastfmpris = {
enable = mkEnableOption "enables the lastmpris scrobbler";
package = mkOption {
type = types.package;
default = self.defaultPackage.${pkgs.system};
};
};
config = mkIf cfg.enable {
home.packages = [ cfg.package ];
systemd.user.services.lastfmpris = {
Unit = {
Description = "Lastfmpris scrobbling daemon";
After = [ "graphical-session-pre.target" ];
};
Service = {
Type = "Simple";
ExecStart = "${lib.getExe cfg.package}";
Restart = "on-failure";
};
};
};
};
};
};
}

View file

@ -1,19 +1,55 @@
use envconfig::Envconfig;
use anyhow::{Context, Result};
use serde::Deserialize;
use std::fs;
use std::fs::{create_dir_all, OpenOptions};
use std::io::Write;
use std::process;
use tracing::info;
#[derive(Envconfig)]
#[derive(Deserialize)]
pub struct Config {
#[envconfig(from = "PLAYER_BUS")]
pub player_bus: Option<String>,
#[envconfig(from = "USERNAME")]
pub username: String,
#[envconfig(from = "PASSWORD")]
pub password: String,
#[envconfig(from = "API_KEY")]
pub api_key: String,
#[envconfig(from = "API_SECRET")]
pub api_secret: String
}
pub fn load_config() -> Result<Config> {
let path = dirs::config_dir()
.context("failed to get config directory")?
.join("lastfmpris")
.join("config.ini");
// create file if it doesnt exist and exit
if !path.exists() {
info!("configuration file doesn't exist, creating boilerplate");
// create underline paths and files
if let Some(parent) = path.parent() {
create_dir_all(parent)
.context("failed to create config file folder(s)")?;
}
let mut file = OpenOptions::new()
.write(true)
.create(true)
.open(&path)
.context("failed to create config file")?;
// write into the config file with the example configuration
// we put inside the base working directory for the project
file.write_all(include_str!("../config.example.ini").as_bytes())
.context("failed to write boilerplate config")?;
info!("configuration boilerplate generation completed: see file: {}", path.display());
// success, exit
process::exit(0)
}
let content = fs::read_to_string(path)
.context("failed to read config content")?;
let config: Config = serde_ini::from_str(&content)
.context("failed to deserialize ini into a readable format")?;
Ok(config)
}

View file

@ -6,14 +6,11 @@ mod main_loop;
mod player;
mod track;
use crate::config::Config;
use crate::lastfm::get_scrobbler;
use anyhow::{Context, Result};
use envconfig::Envconfig;
use tracing::{debug, Level};
fn main() -> Result<()> {
dotenvy::dotenv()?;
tracing_subscriber::fmt()
.with_max_level(if cfg!(debug_assertions) { Level::DEBUG } else { Level::INFO })
.init();
@ -24,8 +21,8 @@ fn main() -> Result<()> {
env!("CARGO_PKG_DESCRIPTION")
);
let config = Config::init_from_env()
.context("failed to initiate configuration from .env")?;
let config = config::load_config()
.context("failed to initiate configuration object!")?;
let scrobbler = get_scrobbler(&config)
.context("failed to create scrobbler")?;