Compare commits

..

15 commits
1.0.1 ... main

Author SHA1 Message Date
87f401f5cf
Make clippy happy
Some checks failed
build-plugin / build (dev) (push) Has been cancelled
build-plugin / build (release) (push) Has been cancelled
2024-07-15 02:43:20 +03:00
caa7d4c5a7
Fix hash for rust-toolchain.toml
Some checks are pending
build-plugin / build (dev) (push) Waiting to run
build-plugin / build (release) (push) Waiting to run
2024-07-15 02:35:26 +03:00
dff668c760
Add build workflow 2024-07-15 02:34:04 +03:00
38b50ccf4d
Update Nix flake 2024-07-15 02:22:14 +03:00
216688e63e
Add Nix dev shell and direnv support 2024-05-24 15:48:51 +03:00
1eca609a2d
Add Cargo.lock 2023-09-04 23:14:47 +03:00
d104354966
Calm down clippy 2023-03-10 06:04:40 +03:00
674e3b77ed
Bump to version to 1.1.1 2023-02-24 18:00:50 +03:00
bab000657b
Properly wait for new event: set timeout -1.0 to wait infinitely 2023-02-24 18:00:50 +03:00
9bebd5b648
Do not replace existing rpc.conf 2023-02-24 18:00:50 +03:00
d83116efc3
Add annotation of supporting only Linux 2023-02-19 18:55:29 +03:00
4c7c27c26e
Bump version to 1.1.0 2023-02-12 15:04:58 +03:00
9c854f0c5b
Added ability to pull cover art from album artist 2023-02-12 15:02:18 +03:00
e95168ccf4
Add keybinding only if doesn't exist 2023-02-12 15:00:06 +03:00
613b2d725e Bump version to 1.0.1 2023-02-11 15:16:46 +03:00
19 changed files with 1243 additions and 42 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

54
.github/workflows/build.yml vendored Normal file
View file

@ -0,0 +1,54 @@
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
/Cargo.lock
/.direnv

1005
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

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

View file

@ -24,7 +24,8 @@
- Displays cover art from MusicBrainz archive
- 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
2. Run the installer script
3. Keybindings can be changed in input.conf
@ -45,4 +46,4 @@ All issues and pull requests are welcome! Feel free to open an issue if you've g
<br/>
GNU GPL © Ryze 2023
</strong></sub>
</p>
</p>

Binary file not shown.

65
flake.lock Normal file
View file

@ -0,0 +1,65 @@
{
"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
}

32
flake.nix Normal file
View file

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

4
rust-toolchain.toml Normal file
View file

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

12
shell.nix Normal file
View file

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

View file

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

View file

@ -2,32 +2,52 @@ use musicbrainz_rs::entity::release_group::{ReleaseGroup, ReleaseGroupSearchQuer
use musicbrainz_rs::entity::CoverartResponse;
use musicbrainz_rs::{Search, FetchCoverart};
pub fn get_cover_art_url(title: &Option<String>, album: &Option<String>, artist: &Option<String>) -> Option<String>{
pub fn get_cover_art_url(title: &Option<String>, album: &Option<String>, artist: &Option<String>, album_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();
if let Some(ref title) = title {
builder.release_group(title);
}
if let Some(ref album) = album {
builder.or().release_group(album);
}
if let Some(ref artist) = artist {
// Some artist fields might contain + characters
// 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);
}
}
let query = builder.build();
get_cover_art_from_query(builder.build())
}
fn get_cover_art_from_query(query: String) -> Option<String> {
let result = match ReleaseGroup::search(query).execute() {
Ok(res) => res,
Err(_) => return None
};
let release = match result.entities.get(0) {
let release = match result.entities.first() {
Some(group) => group,
None => return None
};

View file

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

View file

@ -1,6 +1,8 @@
use std::env;
pub mod macros;
#[allow(unused_imports)]
pub use macros::{error, warning, info};
#[allow(dead_code)]
@ -40,7 +42,7 @@ impl Logger {
pub fn from_env() -> Self {
let level = env::var("MPV_RPC_LOG").unwrap_or_default();
let level: u32 = level.parse().unwrap_or(LogLevel::Error as u32);
let log_level = LogLevel::from(level);
Self {
log_level
@ -49,19 +51,19 @@ impl Logger {
pub fn info(&self, message: &str) {
if self.log_level >= LogLevel::Info {
println!("[mpv-rpc (INFO)] {}", message);
println!("[mpv-rpc (INFO)] {message}");
}
}
pub fn warning(&self, message: &str) {
if self.log_level >= LogLevel::Warn {
println!("[mpv-rpc (WARN)] {}", message);
println!("[mpv-rpc (WARN)] {message}");
}
}
pub fn error(&self, message: &str) {
if self.log_level >= LogLevel::Error {
println!("[mpv-rpc (ERROR)] {}", message);
println!("[mpv-rpc (ERROR)] {message}");
}
}
}

View file

@ -15,17 +15,17 @@ pub struct MpvEventQueue {
}
impl MpvEventQueue {
pub fn new(mpv: Handle,logger: Rc<Logger>) -> Result<Self, &'static str> {
pub fn new(mpv: Handle,logger: Rc<Logger>) -> Result<Self, &'static str> {
let new_self = Self {
mpv,
logger,
};
new_self.initialize()?;
new_self.initialize()?;
Ok(new_self)
}
pub fn from_ptr<'a>(handle: *mut mpv_handle, logger: Rc<Logger>) -> Result<Self, &'static str> {
pub fn from_ptr(handle: *mut mpv_handle, logger: Rc<Logger>) -> Result<Self, &'static str> {
MpvEventQueue::new(Handle::from_ptr(handle), logger)
}
@ -41,9 +41,8 @@ impl MpvEventQueue {
}
pub fn next_event(&mut self) -> Option<MpvEvent> {
let event = self.mpv.wait_event(0.0);
let mpv_event = self.convert_event(event);
mpv_event
let event = self.mpv.wait_event(-1.0);
self.convert_event(event)
}
pub fn handle_request(&self, request: MpvRequest) -> Result<(), &'static str> {
@ -51,7 +50,7 @@ impl MpvEventQueue {
MpvRequest::OSDMessage(message) => self.display_osd_message(message)
}
}
pub fn display_osd_message(&self, message: &str) -> Result<(), &'static str> {
match self.mpv.osd_message(message, Duration::from_secs(1)) {
Ok(()) => Ok(()),
@ -78,12 +77,14 @@ impl MpvEventQueue {
fn get_file_info_event(&self) -> Option<MpvEvent> {
let filename = self.mpv.get_property("filename").unwrap();
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 title = self.mpv.get_property("metadata/by-key/title").ok();
let track = self.mpv.get_property("metadata/by-key/track").ok();
let metadata = FileMetadata {
artist,
album_artist,
album,
title,
track
@ -110,7 +111,7 @@ impl MpvEventQueue {
let time = self.get_remaining_time();
match pause {
false => Some(MpvEvent::Play(time)),
true => Some(MpvEvent::Pause(time))
true => Some(MpvEvent::Pause)
}
}
@ -132,7 +133,7 @@ impl MpvEventQueue {
fn get_toggle_event(&self, message: ClientMessage) -> Option<MpvEvent> {
let command = message.args().join(" ");
logging::info!(self.logger, "Client message: {command}");
if command.starts_with("key-binding toggle-rpc d-") {
Some(MpvEvent::Toggle)
}

View file

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

View file

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