diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2ebc5ea
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/target
+/Cargo.lock
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..447ee44
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "ff2mpv-rust"
+version = "1.0.0"
+edition = "2021"
+
+[dependencies]
+serde = { version = "1.0.152", features = ["derive"] }
+serde_json = "1.0.93"
+
+[profile.release-full]
+inherits = "release"
+strip = "symbols"
+lto = "fat"
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c367e76
--- /dev/null
+++ b/README.md
@@ -0,0 +1,25 @@
+
+
+# ff2mpv-rust
+
+
+
+Native messaging host for ff2mpv written in Rust.
+
+This is a native compiled binary, so it runs much faster and doesn't require external dependencies by itself, unlike Python and Ruby scripts.
+
+# What is this?
+This is a script for hooking mpv and [ff2mpv](https://github.com/woodruffw/ff2mpv), that allows opening any video link in mpv straight from the browser.
+
+# Contributing
+
+All issues and pull requests are welcome! Feel free to open an issue if you've got an idea or a problem. You can open a pull request if you are able to implement it yourself.
+
+---
+
+
+ Made with ponies and love!
+
+ GNU GPL © Ryze 2023
+
+
\ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..be25167
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,42 @@
+use std::io::{self, Read, Write};
+use serde::Deserialize;
+
+#[derive(Deserialize)]
+pub struct MpvMessage {
+ pub url: String
+}
+
+pub fn get_mpv_message() -> Result {
+ let message = match get_browser_message() {
+ Ok(msg) => msg,
+ Err(e) => return Err(format!("IO Error: {}", e))
+ };
+
+ match serde_json::from_str(&message) {
+ Ok(msg) => Ok(msg),
+ Err(e) => Err(format!("JSON Error: {}", e))
+ }
+}
+
+pub fn get_browser_message() -> io::Result {
+ let mut stdin = io::stdin();
+ let mut buf: [u8; 4] = [0; 4];
+ stdin.read_exact(&mut buf)?;
+
+ let length = u32::from_ne_bytes(buf);
+ let mut reader = io::BufReader::new(stdin.take(length as u64));
+
+ let mut string = String::with_capacity(length as usize);
+ reader.read_to_string(&mut string)?;
+ Ok(string)
+}
+
+pub fn send_browser_message(message: &str) -> io::Result<()> {
+ let length = (message.len() as u32).to_ne_bytes();
+ let message = message.as_bytes();
+
+ let mut stdout = io::stdout();
+ stdout.write(&length)?;
+ stdout.write(&message)?;
+ Ok(())
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..763e1e7
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,27 @@
+use std::process::{Command, self};
+use ff2mpv_rust::{get_mpv_message, send_browser_message};
+
+fn main() {
+ let message = match get_mpv_message() {
+ Ok(msg) => msg,
+ Err(e) => {
+ eprintln!("{}", e);
+ process::exit(-1)
+ }
+ };
+
+ let mpv = Command::new("mpv")
+ .arg(message.url)
+ .spawn();
+
+ if let Err(e) = mpv {
+ eprintln!("{}", e);
+ process::exit(-1);
+ }
+
+ if let Err(e) = send_browser_message("ok") {
+ eprintln!("{}", e);
+ process::exit(-1);
+ }
+}
+