Compare commits
17 commits
Author | SHA1 | Date | |
---|---|---|---|
87f401f5cf | |||
caa7d4c5a7 | |||
dff668c760 | |||
38b50ccf4d | |||
216688e63e | |||
1eca609a2d | |||
d104354966 | |||
674e3b77ed | |||
bab000657b | |||
9bebd5b648 | |||
d83116efc3 | |||
4c7c27c26e | |||
9c854f0c5b | |||
e95168ccf4 | |||
613b2d725e | |||
9c567ac312 | |||
8b04b3b28d |
20 changed files with 1252 additions and 49 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
|||
use flake
|
54
.github/workflows/build.yml
vendored
Normal file
54
.github/workflows/build.yml
vendored
Normal 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
2
.gitignore
vendored
|
@ -1,2 +1,2 @@
|
|||
/target
|
||||
/Cargo.lock
|
||||
/.direnv
|
||||
|
|
1005
Cargo.lock
generated
Normal file
1005
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "mpv-rpc"
|
||||
version = "1.0.0"
|
||||
version = "1.1.1"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
- Rusty! 🦀
|
||||
|
||||
# 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
|
||||
|
|
Binary file not shown.
65
flake.lock
Normal file
65
flake.lock
Normal 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
32
flake.nix
Normal 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 {};
|
||||
});
|
||||
};
|
||||
}
|
|
@ -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!"
|
||||
|
||||
if [ ! -f "$mpv_home/rpc.json" ]; then
|
||||
echo -n "Copying default config..."
|
||||
cp ./config/rpc.json "$mpv_home"
|
||||
echo "Done!"
|
||||
fi
|
||||
|
||||
if ! grep -q "$script_binding" "$mpv_home/input.conf"; then
|
||||
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!"
|
||||
fi
|
4
rust-toolchain.toml
Normal file
4
rust-toolchain.toml
Normal 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
12
shell.nix
Normal 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 ];
|
||||
}
|
|
@ -26,13 +26,6 @@ const fn cover_art_default() -> bool {
|
|||
}
|
||||
|
||||
impl Config {
|
||||
pub fn default() -> Self {
|
||||
Self {
|
||||
active: active_default(),
|
||||
cover_art: cover_art_default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_config_file(logger: &Logger) -> Self {
|
||||
let path = Config::get_config_path();
|
||||
logging::info!(logger, "Config path {path}");
|
||||
|
@ -84,3 +77,12 @@ impl Config {
|
|||
"/etc/mpv/".to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
active: active_default(),
|
||||
cover_art: cover_art_default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -22,5 +22,5 @@ fn mpv_open_cplugin(handle: *mut mpv_handle) -> std::os::raw::c_int {
|
|||
};
|
||||
|
||||
plugin.run();
|
||||
return 0;
|
||||
0
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use std::env;
|
||||
|
||||
pub mod macros;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub use macros::{error, warning, info};
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ impl MpvEventQueue {
|
|||
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> {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
@ -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}");
|
||||
|
|
Loading…
Reference in a new issue