Compare commits

..

No commits in common. "main" and "1.0.0" have entirely different histories.
main ... 1.0.0

20 changed files with 49 additions and 1252 deletions

1
.envrc
View file

@ -1 +0,0 @@
use flake

View file

@ -1,54 +0,0 @@
name: build-plugin
on:
workflow_dispatch:
push:
branches: [ main ]
paths:
- "src/**"
- Cargo.lock
- rust-toolchain.toml
- flake.nix
- flake.lock
- shell.nix
pull_request:
branches: [ main ]
paths:
- "src/**"
- Cargo.lock
- rust-toolchain.toml
- flake.nix
- flake.lock
- shell.nix
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
profile: [ "dev", "release" ]
env:
BUILD_NAME: ${{ matrix.profile == 'dev' && 'debug' || matrix.profile }}
steps:
- uses: actions/checkout@v4
- name: Install Nix
uses: nixbuild/nix-quick-install-action@v28
- name: Check
run: nix develop --command cargo clippy --profile ${{ matrix.profile }} -- -D warnings
- name: Build
run: nix develop --command cargo build --profile ${{ matrix.profile }}
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: mpv-rpc_${{ env.BUILD_NAME }}
path: target/${{ env.BUILD_NAME }}/libmpv_rpc.so

2
.gitignore vendored
View file

@ -1,2 +1,2 @@
/target /target
/.direnv /Cargo.lock

1005
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[package] [package]
name = "mpv-rpc" name = "mpv-rpc"
version = "1.1.1" version = "1.0.0"
edition = "2021" edition = "2021"
[lib] [lib]

View file

@ -25,7 +25,6 @@
- Rusty! 🦀 - Rusty! 🦀
# Installation # Installation
*Only Linux is supported at the moment, see MPV docs, regarding C plugins*
1. Download [latest release](https://github.com/ryze312/mpv-rpc/releases/latest) and unzip it 1. Download [latest release](https://github.com/ryze312/mpv-rpc/releases/latest) and unzip it
2. Run the installer script 2. Run the installer script
3. Keybindings can be changed in input.conf 3. Keybindings can be changed in input.conf

Binary file not shown.

View file

@ -1,65 +0,0 @@
{
"nodes": {
"fenix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1720938532,
"narHash": "sha256-Q2ldXS7ZUJWDqdF+GjgRQPlDfdM6NyJ1aw7xUdzjY+4=",
"owner": "nix-community",
"repo": "fenix",
"rev": "e761b522381d124fdc3ba20cecb2be2abec1d8cf",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1720955038,
"narHash": "sha256-GaliJqfFwyYxReFywxAa8orCO+EnDq2NK2F+5aSc8vo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "aa247c0c90ecf4ae7a032c54fdc21b91ca274062",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixpkgs-unstable",
"type": "indirect"
}
},
"root": {
"inputs": {
"fenix": "fenix",
"nixpkgs": "nixpkgs"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1720717809,
"narHash": "sha256-6I+fm+nTLF/iaj7ffiFGlSY7POmubwUaPA/Wq0Bm53M=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "ffbc5ad993d5cd2f3b8bcf9a511165470944ab91",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View file

@ -1,32 +0,0 @@
{
inputs = {
nixpkgs.url = "nixpkgs/nixpkgs-unstable";
fenix = {
url = "github:nix-community/fenix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, fenix }:
let
overlays = [ fenix.overlays.default ];
getPkgsFor = (system: import nixpkgs {
inherit system overlays;
});
forEachSystem = func: nixpkgs.lib.genAttrs [
"x86_64-linux"
"aarch64-linux"
"aarch64-darwin"
"x86_64-darwin"
] (system: func (getPkgsFor system));
in {
devShells = forEachSystem (pkgs: {
default = pkgs.callPackage ./shell.nix {};
});
};
}

View file

@ -1,7 +1,5 @@
#!/bin/bash #!/bin/bash
key='D'
script_binding='script-binding "libmpv_rpc/toggle-rpc"'
mpv_home="${MPV_HOME:-${XDG_CONFIG_HOME:-${HOME}/.config}/mpv}" mpv_home="${MPV_HOME:-${XDG_CONFIG_HOME:-${HOME}/.config}/mpv}"
scripts_dir="$mpv_home/scripts" scripts_dir="$mpv_home/scripts"
@ -13,14 +11,10 @@ echo -n "Copying script..."
cp ./bin/libmpv_rpc.so "$scripts_dir" cp ./bin/libmpv_rpc.so "$scripts_dir"
echo "Done!" echo "Done!"
if [ ! -f "$mpv_home/rpc.json" ]; then echo -n "Copying default config..."
echo -n "Copying default config..." cp ./config/rpc.json "$mpv_home"
cp ./config/rpc.json "$mpv_home" echo "Done!"
echo "Done!"
fi
if ! grep -q "$script_binding" "$mpv_home/input.conf"; then echo -n "Adding keybinding entry to input.conf..."
echo -n "Adding keybinding entry to input.conf..." echo 'D script-binding "libmpv_rpc/toggle-rpc"' > "$mpv_home/input.conf"
echo "$key $script_binding" >> "$mpv_home/input.conf" echo "Done!"
echo "Done!"
fi

View file

@ -1,4 +0,0 @@
[toolchain]
channel = "stable"
profile = "default"
targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"]

View file

@ -1,12 +0,0 @@
{ mkShell, fenix }:
let
toolchain = fenix.fromToolchainFile {
file = ./rust-toolchain.toml;
sha256 = "Ngiz76YP4HTY75GGdH2P+APE/DEIx2R/Dn+BwwOyzZU=";
};
in
mkShell {
packages = [ toolchain ];
}

View file

@ -26,6 +26,13 @@ const fn cover_art_default() -> bool {
} }
impl Config { impl Config {
pub fn default() -> Self {
Self {
active: active_default(),
cover_art: cover_art_default()
}
}
pub fn from_config_file(logger: &Logger) -> Self { pub fn from_config_file(logger: &Logger) -> Self {
let path = Config::get_config_path(); let path = Config::get_config_path();
logging::info!(logger, "Config path {path}"); logging::info!(logger, "Config path {path}");
@ -77,12 +84,3 @@ impl Config {
"/etc/mpv/".to_owned() "/etc/mpv/".to_owned()
} }
} }
impl Default for Config {
fn default() -> Self {
Self {
active: active_default(),
cover_art: cover_art_default()
}
}
}

View file

@ -157,7 +157,7 @@ impl DiscordClient {
return ("logo".to_string(), "mpv".to_string()) return ("logo".to_string(), "mpv".to_string())
} }
let cover_art_url = music_brainz::get_cover_art_url(&metadata.title, &metadata.album, &metadata.artist, &metadata.album_artist); let cover_art_url = music_brainz::get_cover_art_url(&metadata.title, &metadata.album, &metadata.artist);
let large_image = match cover_art_url { let large_image = match cover_art_url {
Some(url) => url, Some(url) => url,
None => "logo".to_string() None => "logo".to_string()
@ -265,7 +265,7 @@ impl MpvEventHandler for DiscordClient {
MpvEvent::FileLoaded(file_info) => self.set_presence(file_info), MpvEvent::FileLoaded(file_info) => self.set_presence(file_info),
MpvEvent::Seek(remaining_time) => self.set_timestamps(remaining_time), MpvEvent::Seek(remaining_time) => self.set_timestamps(remaining_time),
MpvEvent::Play(remaining_time) => self.set_timestamps(remaining_time), MpvEvent::Play(remaining_time) => self.set_timestamps(remaining_time),
MpvEvent::Pause => self.clear_timestamps(), MpvEvent::Pause(_) => self.clear_timestamps(),
MpvEvent::Buffering => self.clear_timestamps(), MpvEvent::Buffering => self.clear_timestamps(),
MpvEvent::Toggle => self.toggle_activity(), MpvEvent::Toggle => self.toggle_activity(),
MpvEvent::Exit => self.close(), MpvEvent::Exit => self.close(),

View file

@ -2,52 +2,32 @@ use musicbrainz_rs::entity::release_group::{ReleaseGroup, ReleaseGroupSearchQuer
use musicbrainz_rs::entity::CoverartResponse; use musicbrainz_rs::entity::CoverartResponse;
use musicbrainz_rs::{Search, FetchCoverart}; use musicbrainz_rs::{Search, FetchCoverart};
pub fn get_cover_art_url(title: &Option<String>, album: &Option<String>, artist: &Option<String>, album_artist: &Option<String>) -> Option<String>{ pub fn get_cover_art_url(title: &Option<String>, album: &Option<String>, artist: &Option<String>) -> Option<String>{
get_track_cover_art(artist, title).or(
get_album_cover_art(album_artist, album)
)
}
fn get_track_cover_art(artist: &Option<String>, title: &Option<String>) -> Option<String> {
let mut builder = ReleaseGroupSearchQuery::query_builder(); let mut builder = ReleaseGroupSearchQuery::query_builder();
if let Some(ref title) = title { if let Some(ref title) = title {
builder.release_group(title); builder.release_group(title);
} }
if let Some(ref album) = album {
builder.or().release_group(album);
}
if let Some(ref artist) = artist { if let Some(ref artist) = artist {
// Some artist fields might contain + characters // Some artist fields might contain + characters
// Pointing at multiple artists // Pointing at multiple artists
for part in artist.split('+') { for part in artist.split("+") {
builder.and().artist(part);
}
}
get_cover_art_from_query(builder.build())
}
fn get_album_cover_art(album_artist: &Option<String>, album: &Option<String>) -> Option<String> {
let mut builder = ReleaseGroupSearchQuery::query_builder();
if let Some(ref album) = album {
builder.release_group(album);
}
if let Some(ref album_artist) = album_artist {
// Some artist fields might contain + characters
// Pointing at multiple artists
for part in album_artist.split('+') {
builder.and().artist(part); builder.and().artist(part);
} }
} }
get_cover_art_from_query(builder.build()) let query = builder.build();
}
fn get_cover_art_from_query(query: String) -> Option<String> {
let result = match ReleaseGroup::search(query).execute() { let result = match ReleaseGroup::search(query).execute() {
Ok(res) => res, Ok(res) => res,
Err(_) => return None Err(_) => return None
}; };
let release = match result.entities.first() { let release = match result.entities.get(0) {
Some(group) => group, Some(group) => group,
None => return None None => return None
}; };

View file

@ -22,5 +22,5 @@ fn mpv_open_cplugin(handle: *mut mpv_handle) -> std::os::raw::c_int {
}; };
plugin.run(); plugin.run();
0 return 0;
} }

View file

@ -1,8 +1,6 @@
use std::env; use std::env;
pub mod macros; pub mod macros;
#[allow(unused_imports)]
pub use macros::{error, warning, info}; pub use macros::{error, warning, info};
#[allow(dead_code)] #[allow(dead_code)]
@ -51,19 +49,19 @@ impl Logger {
pub fn info(&self, message: &str) { pub fn info(&self, message: &str) {
if self.log_level >= LogLevel::Info { if self.log_level >= LogLevel::Info {
println!("[mpv-rpc (INFO)] {message}"); println!("[mpv-rpc (INFO)] {}", message);
} }
} }
pub fn warning(&self, message: &str) { pub fn warning(&self, message: &str) {
if self.log_level >= LogLevel::Warn { if self.log_level >= LogLevel::Warn {
println!("[mpv-rpc (WARN)] {message}"); println!("[mpv-rpc (WARN)] {}", message);
} }
} }
pub fn error(&self, message: &str) { pub fn error(&self, message: &str) {
if self.log_level >= LogLevel::Error { if self.log_level >= LogLevel::Error {
println!("[mpv-rpc (ERROR)] {message}"); println!("[mpv-rpc (ERROR)] {}", message);
} }
} }
} }

View file

@ -25,7 +25,7 @@ impl MpvEventQueue {
Ok(new_self) Ok(new_self)
} }
pub fn from_ptr(handle: *mut mpv_handle, logger: Rc<Logger>) -> Result<Self, &'static str> { pub fn from_ptr<'a>(handle: *mut mpv_handle, logger: Rc<Logger>) -> Result<Self, &'static str> {
MpvEventQueue::new(Handle::from_ptr(handle), logger) MpvEventQueue::new(Handle::from_ptr(handle), logger)
} }
@ -41,8 +41,9 @@ impl MpvEventQueue {
} }
pub fn next_event(&mut self) -> Option<MpvEvent> { pub fn next_event(&mut self) -> Option<MpvEvent> {
let event = self.mpv.wait_event(-1.0); let event = self.mpv.wait_event(0.0);
self.convert_event(event) let mpv_event = self.convert_event(event);
mpv_event
} }
pub fn handle_request(&self, request: MpvRequest) -> Result<(), &'static str> { pub fn handle_request(&self, request: MpvRequest) -> Result<(), &'static str> {
@ -77,14 +78,12 @@ impl MpvEventQueue {
fn get_file_info_event(&self) -> Option<MpvEvent> { fn get_file_info_event(&self) -> Option<MpvEvent> {
let filename = self.mpv.get_property("filename").unwrap(); let filename = self.mpv.get_property("filename").unwrap();
let artist = self.mpv.get_property("metadata/by-key/artist").ok(); let artist = self.mpv.get_property("metadata/by-key/artist").ok();
let album_artist = self.mpv.get_property("metadata/by-key/album_artist").ok();
let album = self.mpv.get_property("metadata/by-key/album").ok(); let album = self.mpv.get_property("metadata/by-key/album").ok();
let title = self.mpv.get_property("metadata/by-key/title").ok(); let title = self.mpv.get_property("metadata/by-key/title").ok();
let track = self.mpv.get_property("metadata/by-key/track").ok(); let track = self.mpv.get_property("metadata/by-key/track").ok();
let metadata = FileMetadata { let metadata = FileMetadata {
artist, artist,
album_artist,
album, album,
title, title,
track track
@ -111,7 +110,7 @@ impl MpvEventQueue {
let time = self.get_remaining_time(); let time = self.get_remaining_time();
match pause { match pause {
false => Some(MpvEvent::Play(time)), false => Some(MpvEvent::Play(time)),
true => Some(MpvEvent::Pause) true => Some(MpvEvent::Pause(time))
} }
} }

View file

@ -5,7 +5,6 @@ pub struct FileInfo {
pub struct FileMetadata { pub struct FileMetadata {
pub artist: Option<String>, pub artist: Option<String>,
pub album_artist: Option<String>,
pub album: Option<String>, pub album: Option<String>,
pub title: Option<String>, pub title: Option<String>,
pub track: Option<String> pub track: Option<String>
@ -17,7 +16,7 @@ pub enum MpvEvent {
Exit, Exit,
FileLoaded(FileInfo), FileLoaded(FileInfo),
Play(i64), Play(i64),
Pause, Pause(i64),
Seek(i64) Seek(i64)
} }
@ -30,5 +29,5 @@ pub trait MpvEventHandler {
} }
pub trait MpvRequester { pub trait MpvRequester {
fn next_request(&mut self) -> Option<MpvRequest>; fn next_request<'a>(&mut self) -> Option<MpvRequest>;
} }

View file

@ -48,7 +48,10 @@ impl RPCPlugin {
} }
fn handle_event(&mut self, event: MpvEvent) -> bool { fn handle_event(&mut self, event: MpvEvent) -> bool {
let exit = matches!(event, MpvEvent::Exit); let exit = match event {
MpvEvent::Exit => true,
_ => false
};
if let Err(e) = self.discord.handle_event(event) { if let Err(e) = self.discord.handle_event(event) {
logging::error!(self.logger, "Failed to handle event: {e}"); logging::error!(self.logger, "Failed to handle event: {e}");