Skip to content

Commit

Permalink
feat: allow to specify required props
Browse files Browse the repository at this point in the history
  • Loading branch information
Curve committed Oct 20, 2023
1 parent 8e92121 commit 05ee3e1
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 58 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ _venmic_ can be used as node-module or as a local rest-server.
The node-module is intended for internal usage by [Vesktop](https://github.com/Vencord/Vesktop).

The Rest-Server exposes three simple endpoints
* (GET) `/list`
> List all available applications to share
* (POST) `/list`
> List all available applications to share.
> You can optionally define a JSON-Body containing all the props the listed nodes should have (i.e. `["node.name"]`).
* (POST) `/link`
> Expects a JSON-Body containing the target application, i.e. `{"key": "node.name", "value": "Firefox", "mode": "include"}`
Expand Down
52 changes: 46 additions & 6 deletions addon/addon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,33 @@ struct patchbay : public Napi::ObjectWrap<patchbay>
{
auto env = info.Env();

auto list = vencord::patchbay::get().list();
std::set<std::string> props{};

if (info.Length() == 1)
{
if (!info[0].IsArray())
{
Napi::Error::New(env, "[venmic] expected array").ThrowAsJavaScriptException();
return {};
}

auto array = info[0].As<Napi::Array>();

for (auto i = 0u; array.Length() > i; i++)
{
auto item = array.Get(i);

if (!item.IsString())
{
Napi::Error::New(env, "[venmic] expected item to be string").ThrowAsJavaScriptException();
return {};
}

props.emplace(item.ToString());
}
}

auto list = vencord::patchbay::get().list(props);
auto rtn = Napi::Array::New(env, list.size());

auto convert = [&](const auto &item)
Expand Down Expand Up @@ -56,15 +82,29 @@ struct patchbay : public Napi::ObjectWrap<patchbay>
{
auto env = info.Env();

if (info.Length() != 3 || !info[0].IsString() || !info[1].IsString() || !info[2].IsString())
if (info.Length() != 1 || !info[0].IsObject())
{
Napi::Error::New(env, "[venmic] expected link object").ThrowAsJavaScriptException();
return Napi::Boolean::New(env, false);
}

auto data = info[0].ToObject();

if (!data.Has("key") || !data.Has("value") || !data.Has("mode"))
{
Napi::Error::New(env, "[venmic] expected keys 'key', 'value' and 'mode'").ThrowAsJavaScriptException();
return Napi::Boolean::New(env, false);
}

if (!data.Get("key").IsString() || !data.Get("value").IsString() || !data.Get("mode").IsString())
{
Napi::Error::New(env, "[venmic] expected three string arguments").ThrowAsJavaScriptException();
Napi::Error::New(env, "[venmic] expected values to be strings").ThrowAsJavaScriptException();
return Napi::Boolean::New(env, false);
}

auto key = static_cast<std::string>(info[0].ToString());
auto value = static_cast<std::string>(info[1].ToString());
auto mode = static_cast<std::string>(info[2].ToString());
auto key = static_cast<std::string>(data.Get("key").ToString());
auto value = static_cast<std::string>(data.Get("value").ToString());
auto mode = static_cast<std::string>(data.Get("mode").ToString());

if (mode != "include" && mode != "exclude")
{
Expand Down
2 changes: 1 addition & 1 deletion include/vencord/patchbay.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ namespace vencord
void unlink();

public:
[[nodiscard]] std::set<node> list();
[[nodiscard]] std::set<node> list(std::set<std::string> props);

public:
[[nodiscard]] static patchbay &get();
Expand Down
13 changes: 4 additions & 9 deletions lib/module.d.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
export interface Props
{
"application.process.binary": string;
"application.process.id": string;
"node.name": string;
}
type DefaultProps = 'node.name' | 'application.name';

export class PatchBay
{
list(): Props[];

unlink(): void;
link(key: keyof Props, value: string, mode: "include" | "exclude"): boolean;

list<T extends string = DefaultProps>(props?: T[]): Record<T, string>;
link(data: {key: string, value: string, mode: "include" | "exclude"}): boolean;

static hasPipeWire(): boolean;
}
1 change: 1 addition & 0 deletions private/message.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace vencord
{
struct list_nodes
{
std::set<std::string> props;
};

struct unset_target
Expand Down
2 changes: 1 addition & 1 deletion private/patchbay.impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ namespace vencord

private:
template <typename T>
void receive(cr_recipe::sender, T);
void receive(cr_recipe::sender &, const T &);

private:
void start(pw_recipe::receiver, cr_recipe::sender);
Expand Down
14 changes: 8 additions & 6 deletions server/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,14 @@ int main(int argc, char **args)

httplib::Server server;

server.Get("/list",
[](const auto &, auto &response)
{
auto data = glz::write_json(patchbay::get().list());
response.set_content(data, "application/json");
});
server.Post("/list",
[](const auto &req, auto &response)
{
auto props = glz::read_json<std::set<std::string>>(req.body);
auto data = glz::write_json(patchbay::get().list(props.value_or(std::set<std::string>{})));

response.set_content(data, "application/json");
});

server.Post("/link",
[](const auto &req, auto &response)
Expand Down
4 changes: 2 additions & 2 deletions src/patchbay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ namespace vencord
m_impl->sender->send(unset_target{});
}

std::set<node> patchbay::list()
std::set<node> patchbay::list(std::set<std::string> props)
{
m_impl->sender->send(list_nodes{});
m_impl->sender->send(list_nodes{std::move(props)});
return m_impl->receiver->recv_as<std::set<node>>();
}

Expand Down
40 changes: 13 additions & 27 deletions src/patchbay.impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,7 @@ namespace vencord
{
auto matching_channel = [&](auto &item)
{
if (target_ports.size() == 1)
{
return true;
}

return item.props["audio.channel"] == port.props["audio.channel"];
return target_ports.size() == 1 || item.props["audio.channel"] == port.props["audio.channel"];
};

auto others = source_ports | ranges::views::filter(matching_channel);
Expand Down Expand Up @@ -209,7 +204,7 @@ namespace vencord
auto parent = std::stoull(props["node.id"]);
nodes[parent].ports.emplace_back(port->info());

//? Yes this belongs here, as the node is created **before** the ports.
//? Yes. This belongs here, as the node is created **before** the ports.
on_node(parent);
}

Expand Down Expand Up @@ -288,28 +283,22 @@ namespace vencord
}

template <>
void patchbay::impl::receive(cr_recipe::sender sender, [[maybe_unused]] list_nodes)
void patchbay::impl::receive(cr_recipe::sender &sender, const list_nodes &req)
{
static std::set<std::string> desired_props{"application.process.binary", "application.process.id", "node.name"};
static const std::set<std::string> required{"application.name", "node.name"};
const auto &props = req.props.empty() ? required : req.props;

auto desireable = [&](auto &item)
{
return ranges::all_of(desired_props, [&](const auto &key) { return item.second.info.props.contains(key); });
return ranges::all_of(props, [&](const auto &key) { return item.second.info.props.contains(key); });
};
auto can_output = [](const auto &item)
{
return item.second.info.output.max > 0;
};
auto to_node = [](auto &item)
auto to_node = [&](auto &item)
{
node rtn;

for (const auto &key : desired_props)
{
rtn[key] = item.second.info.props[key];
}

return rtn;
return node{item.second.info.props};
};

core->update();
Expand All @@ -324,16 +313,15 @@ namespace vencord
}

template <>
// NOLINTNEXTLINE(*-value-param)
void patchbay::impl::receive([[maybe_unused]] cr_recipe::sender, vencord::target req)
void patchbay::impl::receive([[maybe_unused]] cr_recipe::sender &, const vencord::target &req)
{
if (!mic)
{
create_mic();
}

created.clear();
target.emplace(std::move(req));
target.emplace(req);

for (const auto &[id, info] : nodes)
{
Expand All @@ -347,8 +335,7 @@ namespace vencord
}

template <>
// NOLINTNEXTLINE(*-value-param)
void patchbay::impl::receive([[maybe_unused]] cr_recipe::sender, [[maybe_unused]] unset_target)
void patchbay::impl::receive([[maybe_unused]] cr_recipe::sender &, [[maybe_unused]] const unset_target &)
{
target.reset();
created.clear();
Expand All @@ -357,8 +344,7 @@ namespace vencord
}

template <>
// NOLINTNEXTLINE(*-value-param)
void patchbay::impl::receive([[maybe_unused]] cr_recipe::sender, [[maybe_unused]] quit)
void patchbay::impl::receive([[maybe_unused]] cr_recipe::sender &, [[maybe_unused]] const quit &)
{
core->context()->loop()->quit();
}
Expand All @@ -380,7 +366,7 @@ namespace vencord
return;
}

receiver.attach(loop, [this, sender]<typename T>(T &&message) { receive(sender, std::forward<T>(message)); });
receiver.attach(loop, [this, &sender]<typename T>(T &&message) { receive(sender, std::forward<T>(message)); });

auto listener = registry->listen();
listener.on<pw::registry_event::global_removed>([this](std::uint32_t id) { global_removed(id); });
Expand Down
12 changes: 8 additions & 4 deletions tests/node/api.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ try
const patchbay = new venmic.PatchBay();

assert(Array.isArray(patchbay.list()));
assert(Array.isArray(patchbay.list(["node.name"])));

assert.throws(() => patchbay.link(10), /expected three string/ig);
assert.throws(() => patchbay.link(10, 10), /expected three string/ig);
assert.throws(() => patchbay.link("node.name", "Firefox", "gibberish"), /expected mode/ig);
assert.throws(() => patchbay.list({}), /expected array/ig);
assert.throws(() => patchbay.list([10]), /expected item to be string/ig);

assert.doesNotThrow(() => patchbay.link("node.name", "Firefox", "include"));
assert.throws(() => patchbay.link(10), /expected link object/ig);
assert.throws(() => patchbay.link({ a: "A", b: "B", c: "C" }), /expected keys/ig);
assert.throws(() => patchbay.link({ key: "node.name", value: "Firefox", mode: "gibberish" }), /expected mode/ig);

assert.doesNotThrow(() => patchbay.link({ key: "node.name", value: "Firefox", mode: "include" }));
assert.doesNotThrow(() => patchbay.unlink());
}
catch (error)
Expand Down

0 comments on commit 05ee3e1

Please sign in to comment.