diff --git a/Makefile b/Makefile
index 3546650..89c4792 100644
--- a/Makefile
+++ b/Makefile
@@ -27,4 +27,7 @@ firefox:
clean:
@rm -f open-in-mpv Firefox.zip Chrome.crx
-.PHONY: all release debug install uninstall firefox clean
\ No newline at end of file
+fmt:
+ clang-format -i src/*.{hpp,cpp}
+
+.PHONY: all release debug install uninstall firefox clean fmt
\ No newline at end of file
diff --git a/README.md b/README.md
index 5384e99..c6a34ed 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,14 @@
-# open-in-mpv
+
open-in-mpv
+
This is a simple web extension (for Chrome and Firefox) which helps open any video in the currently open tab in the [mpv player](https://mpv.io).
The extension itself shares a lot of code with the one from the awesome [iina](https://github.com/iina/iina), while the (bare) backend is written in C++20 (this is a rewrite from Rust).
+- [Installation](#installation)
+ - [The `mpv://` protocol](#the-mpv-protocol)
+ - [Playlist and `enqueue` functionality](#playlist-and-enqueue-functionality)
+ - [Player support](#player-support)
+
## Installation
> Compiled binaries and packed extensions can be found in the [releases page](https://github.com/Baldomo/open-in-mpv/releases).
@@ -15,7 +21,13 @@ sudo make install
### The `mpv://` protocol
`open-in-mpv install-protocol` will create a custom `xdg-open` desktop file with a scheme handler for the `mpv://` protocol. This lets `xdg-open` call `open-in-mpv` with an encoded URI, so that it can be parsed and the information can be relayed to `mpv` - this logic follows how `iina` parses and opens custom URIs with the `iina://` protocol on Mac. `install-protocol.sh` has the same functionality.
-The table below is a simple documentation of the URL query keys and values used to let the `open-in-mpv` executable what to do:
+Please note that this specification is enforced quite strictly, as the program will error out when:
+
+- The protocol is not `mpv://`
+- The method/path is not `/open`
+- The query is empty
+
+The table below is a simple documentation of the URL query keys and values used to let the `open-in-mpv` executable what to do.
| Key | Example value | Description |
|---------------|----------------------------------------|---------------|
@@ -24,7 +36,7 @@ The table below is a simple documentation of the URL query keys and values used
| `pip` | `1` | Simulates a picture-in-picture mode (only works with mpv for now) |
| `enqueue` | `1` | Adds a video to the queue (see below) |
| `new_window` | `1` | Forcibly starts a video in a new window even if one is already open |
-| `player` | `celluloid` | Starts any arbitrary video player (only mpv-based ones are recommended, such as [Celluloid](https://celluloid-player.github.io/)) |
+| `player` | `celluloid` | Starts any supported video player (see [Player support](#player-support)) |
| `flags` | `--vo%3Dgpu` | Custom command options and flags to be passed to the video player, URL-encoded |
### Playlist and `enqueue` functionality
@@ -32,4 +44,7 @@ For `enqueue` to work properly with any mpv-based player (provided it supports m
```conf
input-ipc-server=/tmp/mpvsocket
-```
\ No newline at end of file
+```
+
+### Player support
+Supported players are defined in `src/players.hpp`, where the struct `player` defines supported functionality and command line flag overrides. To request support for a player you're welcome to open a new issue or a pull request.
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index ecfe8be..955796c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -61,7 +61,7 @@ int main(int argc, char const *argv[]) {
return 0;
}
- std::cout << "Error writing to socket, opening new instance"
+ std::cerr << "Error writing to socket, opening new instance"
<< std::endl;
}
std::system(mo->build_cmd().c_str());
diff --git a/src/options.hpp b/src/options.hpp
index 7c6e506..d71093a 100644
--- a/src/options.hpp
+++ b/src/options.hpp
@@ -3,12 +3,35 @@
#include "players.hpp"
#include "url.hpp"
+#include
+#include
#include
#include
#include
+#include
using std::string;
+namespace {
+// Replace all occurrences of `from` with `to` in `str`
+string replace_all(string str, const string &from, const string &to) {
+ size_t start_pos = 0;
+ while ((start_pos = str.find(from, start_pos)) != string::npos) {
+ str.replace(start_pos, from.length(), to);
+ // Handles case where 'to' is a substring of 'from'
+ start_pos += to.length();
+ }
+ return str;
+}
+
+// Remove all characters equals to `c` at the beginning of the string
+string lstrip(string str, char c) {
+ while (str.at(0) == c)
+ str.replace(0, 1, "");
+ return str;
+}
+} // namespace
+
namespace oim {
/*
@@ -27,6 +50,11 @@ class options {
bool enqueue_;
bool new_window_;
+ /*
+ * Parses flag overrides and returns the final flags
+ */
+ string override_flags();
+
public:
/*
* Constructor for oim::options
@@ -73,12 +101,16 @@ string options::build_cmd() {
}
ret << player_info_->executable << " ";
- if (fullscreen_)
+ if (fullscreen_ && !player_info_->fullscreen.empty())
ret << player_info_->fullscreen << " ";
- if (pip_)
+ if (pip_ && !player_info_->pip.empty())
ret << player_info_->pip << " ";
- if (!flags_.empty())
- ret << flags_ << " ";
+ if (!flags_.empty()) {
+ if (!player_info_->flag_overrides.empty())
+ ret << override_flags() << " ";
+ else
+ ret << flags_ << " ";
+ }
ret << url_;
return ret.str();
@@ -99,6 +131,41 @@ string options::build_ipc() {
return ret.str();
}
+string options::override_flags() {
+ // Return immediatly in case there are no overrides
+ if (player_info_->flag_overrides.empty())
+ return flags_;
+
+ bool star = false;
+ std::ostringstream ret;
+
+ // Check whether there's a global override
+ auto star_pair = player_info_->flag_overrides.find("*");
+ if (star_pair != player_info_->flag_overrides.end())
+ star = true;
+
+ // Turn flags_ into a stream to tokenize somewhat idiomatically
+ auto flagstream = std::istringstream{ flags_ };
+ string tmp;
+
+ while (flagstream >> tmp) {
+ if (star) {
+ // Remove all dashes at the beginning of the flag
+ string stripped = ::lstrip(tmp, '-');
+ ret << ::replace_all((*star_pair).second, "%s", stripped);
+ } else {
+ // Search for the flag currently being processed
+ auto fo = player_info_->flag_overrides.find(tmp);
+ if (fo == player_info_->flag_overrides.end())
+ continue;
+
+ ret << ::replace_all((*fo).second, "%s", tmp);
+ }
+ }
+
+ return ret.str();
+}
+
void options::parse(const char *url_s) {
oim::url u(url_s);
@@ -119,15 +186,12 @@ void options::parse(const char *url_s) {
if (player_info_ == nullptr)
throw string("Unsupported player: ") + player_;
- fullscreen_ = u.query_value("fullscreen") == "1";
+ fullscreen_ = u.query_value("full_screen") == "1";
pip_ = u.query_value("pip") == "1";
enqueue_ = u.query_value("enqueue") == "1";
new_window_ = u.query_value("new_window") == "1";
}
-bool options::needs_ipc() {
- // For now this is needed only when queuing videos
- return enqueue_;
-}
+bool options::needs_ipc() { return player_info_->needs_ipc; }
} // namespace oim
\ No newline at end of file
diff --git a/src/players.hpp b/src/players.hpp
index dc37bd5..24409f7 100644
--- a/src/players.hpp
+++ b/src/players.hpp
@@ -27,17 +27,20 @@ struct player {
bool needs_ipc;
/*
+ * Overrides for any extra command line flag
+ *
* Override syntax:
* `"*"`: matches anything and will take precedence over any other
- * override e.g. the pair `{"*", ""}` will void all flags
+ * override
+ * e.g. the pair `{"*", ""}` will void all flags
* `"flag"`: matches the flag `--flag`
- * e.g. the pair `{"foo", "bar"}` will replace `--foo` with
- * `--bar`
+ * e.g. the pair `{"--foo", "--bar"}` will replace `--foo` with
+ * `--bar`
* `"%s"`: is replaced with the original flag without the leading `--`
- * e.g. the pair `{"foo", "--%s-bar"}` will replace `--foo` with
- * `--foo-bar`
+ * e.g. the pair `{"--foo", "--%s-bar"}` will replace `--foo` with
+ * `--foo-bar`
*
- * Note: command line options with parameters such as --foo=bar are
+ * Note: command line options with parameters such as `--foo=bar` are
* considered a flags as a whole
*/
unordered_map flag_overrides;
@@ -61,7 +64,7 @@ unordered_map player_info = {
.enqueue = "--enqueue",
.new_window = "--new-window",
.needs_ipc = false,
- .flag_overrides = { { "*", "--mpv-options=%s" } } } }
+ .flag_overrides = { { "*", "--mpv-options=%s" } } } },
};
player *get_player_info(string name) {