Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.4.0 #71

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"extends": [
"eslint:recommended",
"plugin:react/recommended"
],
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"env": {
"browser": true
},
"globals": {
"browser": true,
"chrome": true
},
"rules": {
"react/prop-types": "ignore"
}
}
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ You can find us on [Matrix](https://matrix.to/#/#pipewire-screenaudio:matrix.org
./configuration.nix
];
};
}
}
}

# configuration.nix
Expand Down Expand Up @@ -56,7 +56,7 @@ bash install.sh
- Optional: Grant extension with access permissions to all sites
- Join a WebRTC call, click the extension icon, select an audio node and share
- Stream, your transmission should contain both audio and video

## Known Problems
- You can't stream firefox WebRTC calls at all while using `All Desktop Audio`, they are excluded by default
### resistFingerprinting
Expand All @@ -66,6 +66,5 @@ bash install.sh
- ~~Firefox recently implemented a feature for spawning WebRTC audio nodes, and while it works, it has a lot of pitching and de-sync issues. We created a [bug report](https://bugzilla.mozilla.org/show_bug.cgi?id=1844181) on bugzilla and we're hoping for the best!~~

## Planned Features
- Multiple nodes selection
- More customization options (node matching, watcher behavior etc.)
- Chromium support
34 changes: 0 additions & 34 deletions extension/assets/web/html/popup.html

This file was deleted.

42 changes: 0 additions & 42 deletions extension/assets/web/html/settings.html

This file was deleted.

4 changes: 2 additions & 2 deletions extension/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
},

"action": {
"default_popup": "assets/web/html/popup.html"
"default_popup": "react/dist/index.html"
},

"icons": {
Expand Down Expand Up @@ -43,7 +43,7 @@
"web_accessible_resources": [
{
"matches": ["<all_urls>"],
"resources": [ "scripts/index.js" ]
"resources": [ "scripts/override-gdm.js" ]
}
]

Expand Down
1 change: 1 addition & 0 deletions extension/react/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/dist
8 changes: 8 additions & 0 deletions extension/react/build-watcher.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash

scriptRoot="$( cd -- "$(dirname "$0")" > /dev/null 2>&1 ; pwd -P )"

cd $scriptRoot

# Watch for file changes
npx nodemon -e js,jsx --ignore ./dist/ --exec "npx vite build && notify-send -i $scriptRoot/../assets/icons/icon.svg 'Pipewire Screenaudio' 'Extension rebuilt'"
147 changes: 147 additions & 0 deletions extension/react/components/nodes-table.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import React, { useEffect, useState } from "react";

import Table from "@mui/material/Table";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableBody from "@mui/material/TableBody";
import TableRow from "@mui/material/TableRow";
import TableCell from "@mui/material/TableCell";

import Paper from "@mui/material/Paper";
import Checkbox from "@mui/material/Checkbox";

import {
ALL_DESKTOP,
SELECTED_ROWS,
readLocalStorage,
updateLocalStorage,
} from "../lib/local-storage";

import matchNode from "../lib/match-node";

export default function NodesTable({
hasError,
allDesktopAudio,
nodes,
shareNodes,
}) {
const [allChecked, setAllChecked] = useState(false);
const [rows, setRows] = useState(nodes);

function onCheckboxChanged(event, id) {
const isChecked = event.target.checked;
if (id !== null) {
setRows(
rows.map((row, idx) =>
idx === id ? { ...row, checked: isChecked } : row,
),
);
} else {
setRows(rows.map((row) => ({ ...row, checked: isChecked })));
}
}

useEffect(() => {
const rowsMap = Object.fromEntries(rows.map((r) => [r.serial, r]));

const saved = readLocalStorage(SELECTED_ROWS);
if (saved) {
saved.forEach((s) => {
const row = rowsMap[s.serial];
if (row && matchNode(s, row)) {
row.checked = !!s.checked;
}
});
}

setRows(Object.values(rowsMap));
}, []);

useEffect(() => {
setAllChecked(rows.map(({ checked }) => checked).every(Boolean));
updateLocalStorage(SELECTED_ROWS, rows);
shareNodes(rows, readLocalStorage(ALL_DESKTOP));
}, [rows]);

return (
<TableContainer
component={Paper}
sx={{
maxWidth: 500,
overflow: "scroll",
minHeight: 100,
maxHeight: 275,
borderRadius: 0,
}}
>
<Table
sx={{ minWidth: 500, maxWidth: 500 }}
size="small"
disabled={hasError}
>
<TableHead
sx={{
position: "sticky",
top: 0,
zIndex: 10,
background: "#1e1e1e",
borderBottom: "solid",
borderColor: "#515151",
}}
>
<TableRow>
<TableCell>
<Checkbox
disabled={allDesktopAudio || hasError}
onChange={(event) => onCheckboxChanged(event, null)}
checked={allChecked}
/>
</TableCell>
<TableCell>Media</TableCell>
<TableCell>Application</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row, id) => (
<TableRow
key={row.mediaName}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell>
<Checkbox
onChange={(event) => onCheckboxChanged(event, id)}
disabled={allDesktopAudio || hasError}
checked={row.checked}
/>
</TableCell>
<TableCell component="th" scope="row">
<div
style={{
overflow: "hidden",
width: 200,
textOverflow: "ellipsis",
whiteSpace: "nowrap",
}}
>
{row.mediaName}
</div>
</TableCell>
<TableCell>
<div
style={{
overflow: "hidden",
width: 160,
textOverflow: "ellipsis",
whiteSpace: "nowrap",
}}
>
{row.applicationName}
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
12 changes: 12 additions & 0 deletions extension/react/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/index.jsx"></script>
</body>
</html>
56 changes: 56 additions & 0 deletions extension/react/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { createRoot } from "react-dom/client";
import {
createBrowserRouter,
useSearchParams,
RouterProvider,
} from "react-router-dom";

import "@fontsource/roboto/300.css";
import "@fontsource/roboto/400.css";
import "@fontsource/roboto/500.css";
import "@fontsource/roboto/700.css";

import { ThemeProvider, createTheme } from "@mui/material/styles";
import CssBaseline from "@mui/material/CssBaseline";

import Popup from "./routes/popup";
import Settings from "./routes/settings";

const darkTheme = createTheme({
palette: {
mode: "dark",
},
});

const router = createBrowserRouter([
{
path: "/react/dist/index.html",
element: <PageWrapper />,
},
]);

function PageWrapper() {
const [search] = useSearchParams();
console.log(search);

if (!search.has("page")) {
return <Popup />;
}

if (search.get("page") === "settings") {
return <Settings />;
}
}

function App() {
return (
<ThemeProvider theme={darkTheme}>
<CssBaseline />
<RouterProvider router={router} basename="/react/dist/index.html" />
</ThemeProvider>
);
}

const rootEl = document.getElementById("root");
const root = createRoot(rootEl);
root.render(<App />);
Loading