Compare commits

..

21 commits
1.1.1 ... main

Author SHA1 Message Date
38f5182d7a
Bump version: 1.1.4 -> 1.1.5 2024-05-25 00:14:43 +03:00
8d7de6c2f5
Add support for ARM64 2024-05-23 22:24:01 +03:00
eNV25
b3369ed9f3 Make absolutely sure subprocess is not killed 2024-05-20 19:06:27 +00:00
eNV25
4ed5078aa8 cargo fmt 2024-05-20 19:06:27 +00:00
063acaaf03
Bump dependencies and version 1.1.3 -> 1.1.4 2024-04-27 22:04:52 +03:00
f295724227
Fix chromium manifest: add required trailing slash 2024-04-27 22:04:35 +03:00
2e352b62d5
Bump dependencies and version 1.1.2 -> 1.1.3 2024-02-16 12:27:54 +03:00
1ff2a7a1ea
Optimize binary size more 2024-02-16 12:26:43 +03:00
eNV25
dbc4e5c8f0
Refactor {send,read}_message() (#11)
* Refactor {send,read}_message()

* Use binding for Take

---------

Co-authored-by: Ryze <50497128+ryze312@users.noreply.github.com>
2024-02-16 08:53:21 +00:00
eNV25
ce98b95008
Respect options passed by ff2mpv extension (#12)
* Respect options passed by ff2mpv extension

The upstream ff2mpv extension started passing `options`. Respect
those by passing them after our `config.player_args`.

* Restore the order of imports

---------

Co-authored-by: Ryze <50497128+ryze312@users.noreply.github.com>
2024-02-16 08:33:12 +00:00
a971c7c35b Pass --no-terminal to mpv (#10) 2024-02-14 18:44:34 +03:00
738015c947 Add manifest_chromium to README and help message 2024-02-14 16:52:45 +03:00
a10e8706b8 Bump version and dependencies 2024-02-14 16:34:35 +03:00
a3388efb97 Add command for Chromium manifest 2024-02-14 16:30:22 +03:00
1c47fe8c98 Use cargo config to build multiple targets 2024-02-14 14:30:17 +03:00
550bd3a818 Use -- to separate args and URL
Fixes #10
2024-02-14 10:52:24 +03:00
44cf917ecf Specify static lifetime in CONFIG_FILENAME 2024-02-14 09:32:00 +03:00
3566df75e7
Config improvements from #8
- Use #[serde(default)] on the whole struct instead of defaults for separate fields
- Use unwrap_or_default() instead of matching
- Wrap config filename in constant

Co-authored-by: Markus Pettersson <mpettersson@tutanota.com>
2023-11-28 22:29:03 +03:00
392c7937b1
Change release profile directly and add dev profile with optimizations. Fixes #9 2023-10-31 02:14:33 +03:00
4fcc2ac31f Spawn player with CREATE_BREAKAWAY_FROM_JOB on Windows 2023-10-21 21:39:12 +03:00
4aa4ca5d8c
Add Cargo.lock. Fixes #4 2023-09-04 23:10:55 +03:00
10 changed files with 328 additions and 58 deletions

2
.cargo/config.toml Normal file
View file

@ -0,0 +1,2 @@
[build]
target = ["x86_64-unknown-linux-gnu", "x86_64-pc-windows-gnu", "aarch64-unknown-linux-gnu"]

1
.gitignore vendored
View file

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

230
Cargo.lock generated Normal file
View file

@ -0,0 +1,230 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "ff2mpv-rust"
version = "1.1.4"
dependencies = [
"serde",
"serde_json",
"windows",
]
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "indexmap"
version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "proc-macro2"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
[[package]]
name = "serde"
version = "1.0.199"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.199"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.116"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "2.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "windows"
version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132"
dependencies = [
"windows-core",
"windows-targets",
]
[[package]]
name = "windows-core"
version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6"
dependencies = [
"windows-implement",
"windows-interface",
"windows-result",
"windows-targets",
]
[[package]]
name = "windows-implement"
version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-result"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"

View file

@ -1,13 +1,21 @@
[package] [package]
name = "ff2mpv-rust" name = "ff2mpv-rust"
version = "1.1.0" version = "1.1.5"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
serde = { version = "1.0.152", features = ["derive"] } serde = { version = "1.0.*", features = ["derive"] }
serde_json = { version = "1.0.93", features = ["preserve_order"] } serde_json = { version = "1.0.*", features = ["preserve_order"] }
[profile.release-full] [target.'cfg(windows)'.dependencies]
inherits = "release" windows = { version = "0.56", features = ["Win32_System_Threading"] }
[profile.dev-optimized]
inherits = "dev"
opt-level = 3
[profile.release]
strip = "symbols" strip = "symbols"
lto = "fat" lto = "fat"
panic = "abort"
codegen-units = 1

View file

@ -18,6 +18,11 @@ After that get native messasing host manifest:
``` ```
ff2mpv-rust manifest ff2mpv-rust manifest
``` ```
Or for Chromium/Chrome:
```
ff2mpv-rust manifest_chromium
```
Install it following manual installation instructions on [ff2mpv wiki](https://github.com/woodruffw/ff2mpv/wiki). Install it following manual installation instructions on [ff2mpv wiki](https://github.com/woodruffw/ff2mpv/wiki).
# Configuration # Configuration
@ -35,7 +40,8 @@ See [example configuration](ff2mpv-rust.json).
ff2mpv-rust provides command line interface with following commands: ff2mpv-rust provides command line interface with following commands:
``` ```
help: prints help message help: prints help message
manifest: prints manifest for browser configuration manifest: prints manifest for Firefox configuration
manifest_chromium: prints manifest for Chromium/Chrome configuration
validate: checks configration file for validity validate: checks configration file for validity
``` ```
Note that it won't fail on invalid commands, but instead assume it is called from browser, blocking the input. Note that it won't fail on invalid commands, but instead assume it is called from browser, blocking the input.

View file

@ -1,13 +1,13 @@
use std::io::{self, Read, Write};
use serde::Deserialize; use serde::Deserialize;
use std::io;
use std::io::BufReader;
use std::io::{Read, Write};
use crate::error::FF2MpvError; use crate::error::FF2MpvError;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct FF2MpvMessage { pub struct FF2MpvMessage {
pub url: String, pub url: String,
pub options: Vec<String>,
} }
pub fn send_reply() -> Result<(), io::Error> { pub fn send_reply() -> Result<(), io::Error> {
@ -22,23 +22,23 @@ pub fn get_mpv_message() -> Result<FF2MpvMessage, FF2MpvError> {
} }
fn read_message() -> Result<String, io::Error> { fn read_message() -> Result<String, io::Error> {
let mut stdin = io::stdin(); let mut stdin = io::stdin().lock();
let mut buf: [u8; 4] = [0; 4];
stdin.read_exact(&mut buf)?;
let length = u32::from_ne_bytes(buf); let mut len = 0_u32.to_ne_bytes();
let mut reader = BufReader::new(stdin.take(length as u64)); stdin.read_exact(&mut len)?;
let len = u32::from_ne_bytes(len);
let mut string = String::with_capacity(length as usize); let mut reader = stdin.take(len as u64);
reader.read_to_string(&mut string)?; let mut msg = String::with_capacity(len as usize);
Ok(string) reader.read_to_string(&mut msg)?;
Ok(msg)
} }
fn send_message(message: &str) -> Result<(), io::Error> { fn send_message(message: &str) -> Result<(), io::Error> {
let length = (message.len() as u32).to_ne_bytes(); let length = (message.len() as u32).to_ne_bytes();
let message = message.as_bytes(); let message = message.as_bytes();
let mut stdout = io::stdout(); let mut stdout = io::stdout().lock();
stdout.write_all(&length)?; stdout.write_all(&length)?;
stdout.write_all(message)?; stdout.write_all(message)?;
Ok(()) Ok(())

View file

@ -1,7 +1,8 @@
use std::env; use std::env;
use std::io; use std::io;
use std::process; use std::process;
use serde_json::{self, json};
use serde_json::json;
use crate::browser; use crate::browser;
use crate::config::Config; use crate::config::Config;
@ -10,18 +11,19 @@ use crate::error::FF2MpvError;
pub enum Command { pub enum Command {
ShowHelp, ShowHelp,
ShowManifest, ShowManifest,
ShowManifestChromium,
ValidateConfig, ValidateConfig,
FF2Mpv FF2Mpv,
} }
impl Command { impl Command {
pub fn execute(&self) -> Result<(), FF2MpvError> { pub fn execute(&self) -> Result<(), FF2MpvError> {
match self { match self {
Command::ShowHelp => Self::show_help(), Command::ShowHelp => Self::show_help(),
Command::ShowManifest => Self::show_manifest(), Command::ShowManifest => Self::show_manifest(false),
Command::ShowManifestChromium => Self::show_manifest(true),
Command::ValidateConfig => Self::validate_config(), Command::ValidateConfig => Self::validate_config(),
Command::FF2Mpv => Self::ff2mpv() Command::FF2Mpv => Self::ff2mpv(),
} }
} }
@ -29,7 +31,8 @@ impl Command {
println!("Usage: ff2mpv-rust <command>"); println!("Usage: ff2mpv-rust <command>");
println!("Commands:"); println!("Commands:");
println!(" help: prints help message"); println!(" help: prints help message");
println!(" manifest: prints manifest for browser configuration"); println!(" manifest: prints manifest for Firefox configuration");
println!(" manifest_chromium: prints manifest for Chromium/Chrome configuration");
println!(" validate: checks configration file for validity"); println!(" validate: checks configration file for validity");
println!("Note: Invalid commands won't fail"); println!("Note: Invalid commands won't fail");
println!("Note: It will assume that binary is called from browser, blocking for input"); println!("Note: It will assume that binary is called from browser, blocking for input");
@ -37,14 +40,23 @@ impl Command {
Ok(()) Ok(())
} }
fn show_manifest() -> Result<(), FF2MpvError>{ fn show_manifest(chromium: bool) -> Result<(), FF2MpvError> {
let executable_path = env::current_exe()?; let executable_path = env::current_exe()?;
let allowed_keyvalue = if chromium {
(
"allowed_origins",
"chrome-extension://ephjcajbkgplkjmelpglennepbpmdpjg/",
)
} else {
("allowed_extensions", "ff2mpv@yossarian.net")
};
let manifest = json!({ let manifest = json!({
"name": "ff2mpv", "name": "ff2mpv",
"description": "ff2mpv's external manifest", "description": "ff2mpv's external manifest",
"path": executable_path, "path": executable_path,
"type": "stdio", "type": "stdio",
"allowed_extensions": ["ff2mpv@yossarian.net"] allowed_keyvalue.0: [allowed_keyvalue.1]
}); });
let manifest = serde_json::to_string_pretty(&manifest)?; let manifest = serde_json::to_string_pretty(&manifest)?;
@ -63,20 +75,43 @@ impl Command {
fn ff2mpv() -> Result<(), FF2MpvError> { fn ff2mpv() -> Result<(), FF2MpvError> {
let config = Config::build(); let config = Config::build();
let ff2mpv_message = browser::get_mpv_message()?; let ff2mpv_message = browser::get_mpv_message()?;
Command::launch_mpv(config.player_command, config.player_args, &ff2mpv_message.url)?; let args = [config.player_args, ff2mpv_message.options].concat();
Command::launch_mpv(config.player_command, args, &ff2mpv_message.url)?;
browser::send_reply()?; browser::send_reply()?;
Ok(()) Ok(())
} }
fn launch_mpv(command: String, args: Vec<String>, url: &str) -> Result<(), io::Error> { fn launch_mpv(command: String, args: Vec<String>, url: &str) -> Result<(), io::Error> {
process::Command::new(command) let mut command = process::Command::new(command);
.stdout(process::Stdio::null())
.stderr(process::Stdio::null()) command.stdout(process::Stdio::null());
.args(args) command.stderr(process::Stdio::null());
.arg(url) command.arg("--no-terminal");
.spawn()?; command.args(args);
command.arg("--");
command.arg(url);
Command::detach_mpv(&mut command);
command.spawn()?;
Ok(()) Ok(())
} }
// NOTE: Make sure the subprocess is not killed.
// See https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging#closing_the_native_app
#[cfg(unix)]
fn detach_mpv(command: &mut process::Command) {
use std::os::unix::process::CommandExt;
command.process_group(0);
}
#[cfg(windows)]
fn detach_mpv(command: &mut process::Command) {
use std::os::windows::process::CommandExt;
use windows::Win32::System::Threading::CREATE_BREAKAWAY_FROM_JOB;
command.creation_flags(CREATE_BREAKAWAY_FROM_JOB.0);
}
} }

View file

@ -1,35 +1,32 @@
use std::env; use std::env;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use serde::Deserialize; use serde::Deserialize;
use crate::error::FF2MpvError; use crate::error::FF2MpvError;
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(default)]
pub struct Config { pub struct Config {
#[serde(default = "default_player_command")]
pub player_command: String, pub player_command: String,
#[serde(default = "default_player_args")]
pub player_args: Vec<String>, pub player_args: Vec<String>,
} }
impl Default for Config { impl Default for Config {
fn default() -> Self { fn default() -> Self {
Self { Self {
player_command: default_player_command(), player_command: "mpv".to_owned(),
player_args: default_player_args(), player_args: vec![],
} }
} }
} }
impl Config { impl Config {
const CONFIG_FILENAME: &'static str = "ff2mpv-rust.json";
pub fn build() -> Self { pub fn build() -> Self {
if let Ok(config) = Config::parse_config_file() { Config::parse_config_file().unwrap_or_default()
config
} else {
Config::default()
}
} }
pub fn parse_config_file() -> Result<Self, FF2MpvError> { pub fn parse_config_file() -> Result<Self, FF2MpvError> {
@ -44,7 +41,7 @@ impl Config {
Ok(config) Ok(config)
} }
#[cfg(target_family = "unix")] #[cfg(unix)]
fn get_config_location() -> PathBuf { fn get_config_location() -> PathBuf {
let mut path = PathBuf::new(); let mut path = PathBuf::new();
@ -57,25 +54,17 @@ impl Config {
path.push("/etc"); path.push("/etc");
} }
path.push("ff2mpv-rust.json"); path.push(Self::CONFIG_FILENAME);
path path
} }
#[cfg(target_family = "windows")] #[cfg(windows)]
fn get_config_location() -> PathBuf { fn get_config_location() -> PathBuf {
let mut path = PathBuf::new(); let mut path = PathBuf::new();
let appdata = env::var("APPDATA").unwrap(); let appdata = env::var("APPDATA").unwrap();
path.push(appdata); path.push(appdata);
path.push("ff2mpv-rust.json"); path.push(Self::CONFIG_FILENAME);
path path
} }
} }
fn default_player_command() -> String {
"mpv".to_owned()
}
fn default_player_args() -> Vec<String> {
vec![]
}

View file

@ -1,6 +1,6 @@
use std::io;
use std::fmt; use std::fmt;
use std::fmt::Display; use std::fmt::Display;
use std::io;
pub enum FF2MpvError { pub enum FF2MpvError {
NoConfig, NoConfig,

View file

@ -19,6 +19,7 @@ fn get_command(name: &str) -> Command {
match name { match name {
"help" => Command::ShowHelp, "help" => Command::ShowHelp,
"manifest" => Command::ShowManifest, "manifest" => Command::ShowManifest,
"manifest_chromium" => Command::ShowManifestChromium,
"validate" => Command::ValidateConfig, "validate" => Command::ValidateConfig,
_ => Command::FF2Mpv, _ => Command::FF2Mpv,
} }