├── .github
└── workflows
│ └── rust.yml
├── .gitignore
├── LICENSE
├── README.md
├── Tuxphones.service
├── bd
├── package-lock.json
├── package.json
├── release
│ └── Tuxphones.plugin.js
└── src
│ └── Tuxphones
│ ├── config.json
│ └── index.jsx
├── daemon
├── Cargo.lock
├── Cargo.toml
└── src
│ ├── gstreamer.rs
│ ├── lib.rs
│ ├── main.rs
│ ├── pulse.rs
│ ├── socket.rs
│ └── x.rs
├── install.sh
└── updateDaemon.sh
/.github/workflows/rust.yml:
--------------------------------------------------------------------------------
1 | name: Rust
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | branches: [ "main" ]
8 |
9 | env:
10 | CARGO_TERM_COLOR: always
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 | defaults:
17 | run:
18 | working-directory: daemon
19 |
20 | steps:
21 | - uses: actions/checkout@v3
22 | - name: Dependencies
23 | run: sudo apt-get update && sudo apt-get install -y libunwind-dev && sudo apt-get install -y libpulse-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio
24 | - name: Build
25 | run: cargo build --verbose
26 | - name: Run tests
27 | run: cargo test --verbose
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | daemon/target/
2 | bd/node_modules/
3 | .idea
4 | .vscode
5 | bundled.js
6 | *.log
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Jack Hogan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tuxphones
2 |
3 | Discord screensharing audio for Linux.
4 |
5 | Development is ongoing and therefore some features do not work fully or are broken. If you find a bug, open an issue!
6 |
7 | ## State of the Project
8 | ### Project Activity
9 | We are very busy, so progress will be slow for the forseeable future.
10 |
11 | ### General Support
12 | We are currently working on supporting PulseAudio and X11. Once these become stable, we will start working on PipeWire and Wayland.
13 |
14 | ### BetterDiscord Plugin
15 | After the big Discord update in September 2022, the functionality of the plugin was completely broken. While we have tried to fix it as much as possible, it is still slightly inconsistent in reporting and integration so you may have to refresh Discord (Ctrl/Cmd+R) a few times to get it to load properly.
16 |
17 | ### Daemon
18 | While most of the daemon works properly, we can only transmit video to Discord at the moment (through WebRTC). We are undergoing a transition from WebRTC to UDP transmission, so nothing transmits right now. To get a better look at the actual transmission code, look at our sister project [here](https://github.com/ImTheSquid/gst-discordsender).
19 |
20 | ### Contributions
21 | We are open to contributions, however the project is still not stable so things will break. We plan to make an API for extending to PipeWire and Wayland in the future once we have a MVP.
22 |
23 | ## Installation
24 | ### Prerequisites
25 | - BetterDiscord
26 | - Rust
27 | - Cargo
28 | - Systemd
29 | - PulseAudio Dev Libraries
30 | - All GStreamer Dev Libraries
31 |
32 | ### Tuxphones is still in-development. Follow these temporary instructions to try it:
33 | Clone the repo, then copy the plugin file from `bd/release` to your BD plugins folder. Then run `cargo run` from the `daemon` directory. Finally, enable the BD plugin.
34 |
35 | ### The below instructions do not work yet! The Crates package is very outdated and will not be updated until the project works.
36 | ### Manual
37 | Run:
38 | ```
39 | ./install.sh
40 | ```
41 | This will install the daemon then copy the BetterDiscord plugin to your plugins folder.
42 |
43 | ### Updating
44 | The client-side plugin updates through Discord.
45 |
46 | To update the daemon, run:
47 | ```
48 | ./updateDaemon.sh
49 | ```
50 |
--------------------------------------------------------------------------------
/Tuxphones.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Tuxphones Daemon
3 | [Service]
4 | Type=simple
5 | StandardOutput=journal
6 | ExecStart=%h/.cargo/bin/tuxphones
7 | [Install]
8 | WantedBy=default.target
--------------------------------------------------------------------------------
/bd/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tuxphones",
3 | "version": "0.1.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "tuxphones",
9 | "version": "0.1.0",
10 | "license": "MIT",
11 | "dependencies": {
12 | "esbuild": "^0.15.16",
13 | "zerespluginlibrary": "^2.0.6"
14 | }
15 | },
16 | "node_modules/@esbuild/android-arm": {
17 | "version": "0.15.16",
18 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.16.tgz",
19 | "integrity": "sha512-nyB6CH++2mSgx3GbnrJsZSxzne5K0HMyNIWafDHqYy7IwxFc4fd/CgHVZXr8Eh+Q3KbIAcAe3vGyqIPhGblvMQ==",
20 | "cpu": [
21 | "arm"
22 | ],
23 | "optional": true,
24 | "os": [
25 | "android"
26 | ],
27 | "engines": {
28 | "node": ">=12"
29 | }
30 | },
31 | "node_modules/@esbuild/linux-loong64": {
32 | "version": "0.15.16",
33 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.16.tgz",
34 | "integrity": "sha512-SDLfP1uoB0HZ14CdVYgagllgrG7Mdxhkt4jDJOKl/MldKrkQ6vDJMZKl2+5XsEY/Lzz37fjgLQoJBGuAw/x8kQ==",
35 | "cpu": [
36 | "loong64"
37 | ],
38 | "optional": true,
39 | "os": [
40 | "linux"
41 | ],
42 | "engines": {
43 | "node": ">=12"
44 | }
45 | },
46 | "node_modules/esbuild": {
47 | "version": "0.15.16",
48 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.16.tgz",
49 | "integrity": "sha512-o6iS9zxdHrrojjlj6pNGC2NAg86ECZqIETswTM5KmJitq+R1YmahhWtMumeQp9lHqJaROGnsBi2RLawGnfo5ZQ==",
50 | "hasInstallScript": true,
51 | "bin": {
52 | "esbuild": "bin/esbuild"
53 | },
54 | "engines": {
55 | "node": ">=12"
56 | },
57 | "optionalDependencies": {
58 | "@esbuild/android-arm": "0.15.16",
59 | "@esbuild/linux-loong64": "0.15.16",
60 | "esbuild-android-64": "0.15.16",
61 | "esbuild-android-arm64": "0.15.16",
62 | "esbuild-darwin-64": "0.15.16",
63 | "esbuild-darwin-arm64": "0.15.16",
64 | "esbuild-freebsd-64": "0.15.16",
65 | "esbuild-freebsd-arm64": "0.15.16",
66 | "esbuild-linux-32": "0.15.16",
67 | "esbuild-linux-64": "0.15.16",
68 | "esbuild-linux-arm": "0.15.16",
69 | "esbuild-linux-arm64": "0.15.16",
70 | "esbuild-linux-mips64le": "0.15.16",
71 | "esbuild-linux-ppc64le": "0.15.16",
72 | "esbuild-linux-riscv64": "0.15.16",
73 | "esbuild-linux-s390x": "0.15.16",
74 | "esbuild-netbsd-64": "0.15.16",
75 | "esbuild-openbsd-64": "0.15.16",
76 | "esbuild-sunos-64": "0.15.16",
77 | "esbuild-windows-32": "0.15.16",
78 | "esbuild-windows-64": "0.15.16",
79 | "esbuild-windows-arm64": "0.15.16"
80 | }
81 | },
82 | "node_modules/esbuild-android-64": {
83 | "version": "0.15.16",
84 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.16.tgz",
85 | "integrity": "sha512-Vwkv/sT0zMSgPSVO3Jlt1pUbnZuOgtOQJkJkyyJFAlLe7BiT8e9ESzo0zQSx4c3wW4T6kGChmKDPMbWTgtliQA==",
86 | "cpu": [
87 | "x64"
88 | ],
89 | "optional": true,
90 | "os": [
91 | "android"
92 | ],
93 | "engines": {
94 | "node": ">=12"
95 | }
96 | },
97 | "node_modules/esbuild-android-arm64": {
98 | "version": "0.15.16",
99 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.16.tgz",
100 | "integrity": "sha512-lqfKuofMExL5niNV3gnhMUYacSXfsvzTa/58sDlBET/hCOG99Zmeh+lz6kvdgvGOsImeo6J9SW21rFCogNPLxg==",
101 | "cpu": [
102 | "arm64"
103 | ],
104 | "optional": true,
105 | "os": [
106 | "android"
107 | ],
108 | "engines": {
109 | "node": ">=12"
110 | }
111 | },
112 | "node_modules/esbuild-darwin-64": {
113 | "version": "0.15.16",
114 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.16.tgz",
115 | "integrity": "sha512-wo2VWk/n/9V2TmqUZ/KpzRjCEcr00n7yahEdmtzlrfQ3lfMCf3Wa+0sqHAbjk3C6CKkR3WKK/whkMq5Gj4Da9g==",
116 | "cpu": [
117 | "x64"
118 | ],
119 | "optional": true,
120 | "os": [
121 | "darwin"
122 | ],
123 | "engines": {
124 | "node": ">=12"
125 | }
126 | },
127 | "node_modules/esbuild-darwin-arm64": {
128 | "version": "0.15.16",
129 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.16.tgz",
130 | "integrity": "sha512-fMXaUr5ou0M4WnewBKsspMtX++C1yIa3nJ5R2LSbLCfJT3uFdcRoU/NZjoM4kOMKyOD9Sa/2vlgN8G07K3SJnw==",
131 | "cpu": [
132 | "arm64"
133 | ],
134 | "optional": true,
135 | "os": [
136 | "darwin"
137 | ],
138 | "engines": {
139 | "node": ">=12"
140 | }
141 | },
142 | "node_modules/esbuild-freebsd-64": {
143 | "version": "0.15.16",
144 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.16.tgz",
145 | "integrity": "sha512-UzIc0xlRx5x9kRuMr+E3+hlSOxa/aRqfuMfiYBXu2jJ8Mzej4lGL7+o6F5hzhLqWfWm1GWHNakIdlqg1ayaTNQ==",
146 | "cpu": [
147 | "x64"
148 | ],
149 | "optional": true,
150 | "os": [
151 | "freebsd"
152 | ],
153 | "engines": {
154 | "node": ">=12"
155 | }
156 | },
157 | "node_modules/esbuild-freebsd-arm64": {
158 | "version": "0.15.16",
159 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.16.tgz",
160 | "integrity": "sha512-8xyiYuGc0DLZphFQIiYaLHlfoP+hAN9RHbE+Ibh8EUcDNHAqbQgUrQg7pE7Bo00rXmQ5Ap6KFgcR0b4ALZls1g==",
161 | "cpu": [
162 | "arm64"
163 | ],
164 | "optional": true,
165 | "os": [
166 | "freebsd"
167 | ],
168 | "engines": {
169 | "node": ">=12"
170 | }
171 | },
172 | "node_modules/esbuild-linux-32": {
173 | "version": "0.15.16",
174 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.16.tgz",
175 | "integrity": "sha512-iGijUTV+0kIMyUVoynK0v+32Oi8yyp0xwMzX69GX+5+AniNy/C/AL1MjFTsozRp/3xQPl7jVux/PLe2ds10/2w==",
176 | "cpu": [
177 | "ia32"
178 | ],
179 | "optional": true,
180 | "os": [
181 | "linux"
182 | ],
183 | "engines": {
184 | "node": ">=12"
185 | }
186 | },
187 | "node_modules/esbuild-linux-64": {
188 | "version": "0.15.16",
189 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.16.tgz",
190 | "integrity": "sha512-tuSOjXdLw7VzaUj89fIdAaQT7zFGbKBcz4YxbWrOiXkwscYgE7HtTxUavreBbnRkGxKwr9iT/gmeJWNm4djy/g==",
191 | "cpu": [
192 | "x64"
193 | ],
194 | "optional": true,
195 | "os": [
196 | "linux"
197 | ],
198 | "engines": {
199 | "node": ">=12"
200 | }
201 | },
202 | "node_modules/esbuild-linux-arm": {
203 | "version": "0.15.16",
204 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.16.tgz",
205 | "integrity": "sha512-XKcrxCEXDTOuoRj5l12tJnkvuxXBMKwEC5j0JISw3ziLf0j4zIwXbKbTmUrKFWbo6ZgvNpa7Y5dnbsjVvH39bQ==",
206 | "cpu": [
207 | "arm"
208 | ],
209 | "optional": true,
210 | "os": [
211 | "linux"
212 | ],
213 | "engines": {
214 | "node": ">=12"
215 | }
216 | },
217 | "node_modules/esbuild-linux-arm64": {
218 | "version": "0.15.16",
219 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.16.tgz",
220 | "integrity": "sha512-mPYksnfHnemNrvjrDhZyixL/AfbJN0Xn9S34ZOHYdh6/jJcNd8iTsv3JwJoEvTJqjMggjMhGUPJAdjnFBHoH8A==",
221 | "cpu": [
222 | "arm64"
223 | ],
224 | "optional": true,
225 | "os": [
226 | "linux"
227 | ],
228 | "engines": {
229 | "node": ">=12"
230 | }
231 | },
232 | "node_modules/esbuild-linux-mips64le": {
233 | "version": "0.15.16",
234 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.16.tgz",
235 | "integrity": "sha512-kSJO2PXaxfm0pWY39+YX+QtpFqyyrcp0ZeI8QPTrcFVQoWEPiPVtOfTZeS3ZKedfH+Ga38c4DSzmKMQJocQv6A==",
236 | "cpu": [
237 | "mips64el"
238 | ],
239 | "optional": true,
240 | "os": [
241 | "linux"
242 | ],
243 | "engines": {
244 | "node": ">=12"
245 | }
246 | },
247 | "node_modules/esbuild-linux-ppc64le": {
248 | "version": "0.15.16",
249 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.16.tgz",
250 | "integrity": "sha512-NimPikwkBY0yGABw6SlhKrtT35sU4O23xkhlrTT/O6lSxv3Pm5iSc6OYaqVAHWkLdVf31bF4UDVFO+D990WpAA==",
251 | "cpu": [
252 | "ppc64"
253 | ],
254 | "optional": true,
255 | "os": [
256 | "linux"
257 | ],
258 | "engines": {
259 | "node": ">=12"
260 | }
261 | },
262 | "node_modules/esbuild-linux-riscv64": {
263 | "version": "0.15.16",
264 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.16.tgz",
265 | "integrity": "sha512-ty2YUHZlwFOwp7pR+J87M4CVrXJIf5ZZtU/umpxgVJBXvWjhziSLEQxvl30SYfUPq0nzeWKBGw5i/DieiHeKfw==",
266 | "cpu": [
267 | "riscv64"
268 | ],
269 | "optional": true,
270 | "os": [
271 | "linux"
272 | ],
273 | "engines": {
274 | "node": ">=12"
275 | }
276 | },
277 | "node_modules/esbuild-linux-s390x": {
278 | "version": "0.15.16",
279 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.16.tgz",
280 | "integrity": "sha512-VkZaGssvPDQtx4fvVdZ9czezmyWyzpQhEbSNsHZZN0BHvxRLOYAQ7sjay8nMQwYswP6O2KlZluRMNPYefFRs+w==",
281 | "cpu": [
282 | "s390x"
283 | ],
284 | "optional": true,
285 | "os": [
286 | "linux"
287 | ],
288 | "engines": {
289 | "node": ">=12"
290 | }
291 | },
292 | "node_modules/esbuild-netbsd-64": {
293 | "version": "0.15.16",
294 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.16.tgz",
295 | "integrity": "sha512-ElQ9rhdY51et6MJTWrCPbqOd/YuPowD7Cxx3ee8wlmXQQVW7UvQI6nSprJ9uVFQISqSF5e5EWpwWqXZsECLvXg==",
296 | "cpu": [
297 | "x64"
298 | ],
299 | "optional": true,
300 | "os": [
301 | "netbsd"
302 | ],
303 | "engines": {
304 | "node": ">=12"
305 | }
306 | },
307 | "node_modules/esbuild-openbsd-64": {
308 | "version": "0.15.16",
309 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.16.tgz",
310 | "integrity": "sha512-KgxMHyxMCT+NdLQE1zVJEsLSt2QQBAvJfmUGDmgEq8Fvjrf6vSKB00dVHUEDKcJwMID6CdgCpvYNt999tIYhqA==",
311 | "cpu": [
312 | "x64"
313 | ],
314 | "optional": true,
315 | "os": [
316 | "openbsd"
317 | ],
318 | "engines": {
319 | "node": ">=12"
320 | }
321 | },
322 | "node_modules/esbuild-sunos-64": {
323 | "version": "0.15.16",
324 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.16.tgz",
325 | "integrity": "sha512-exSAx8Phj7QylXHlMfIyEfNrmqnLxFqLxdQF6MBHPdHAjT7fsKaX6XIJn+aQEFiOcE4X8e7VvdMCJ+WDZxjSRQ==",
326 | "cpu": [
327 | "x64"
328 | ],
329 | "optional": true,
330 | "os": [
331 | "sunos"
332 | ],
333 | "engines": {
334 | "node": ">=12"
335 | }
336 | },
337 | "node_modules/esbuild-windows-32": {
338 | "version": "0.15.16",
339 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.16.tgz",
340 | "integrity": "sha512-zQgWpY5pUCSTOwqKQ6/vOCJfRssTvxFuEkpB4f2VUGPBpdddZfdj8hbZuFRdZRPIVHvN7juGcpgCA/XCF37mAQ==",
341 | "cpu": [
342 | "ia32"
343 | ],
344 | "optional": true,
345 | "os": [
346 | "win32"
347 | ],
348 | "engines": {
349 | "node": ">=12"
350 | }
351 | },
352 | "node_modules/esbuild-windows-64": {
353 | "version": "0.15.16",
354 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.16.tgz",
355 | "integrity": "sha512-HjW1hHRLSncnM3MBCP7iquatHVJq9l0S2xxsHHj4yzf4nm9TU4Z7k4NkeMlD/dHQ4jPlQQhwcMvwbJiOefSuZw==",
356 | "cpu": [
357 | "x64"
358 | ],
359 | "optional": true,
360 | "os": [
361 | "win32"
362 | ],
363 | "engines": {
364 | "node": ">=12"
365 | }
366 | },
367 | "node_modules/esbuild-windows-arm64": {
368 | "version": "0.15.16",
369 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.16.tgz",
370 | "integrity": "sha512-oCcUKrJaMn04Vxy9Ekd8x23O8LoU01+4NOkQ2iBToKgnGj5eo1vU9i27NQZ9qC8NFZgnQQZg5oZWAejmbsppNA==",
371 | "cpu": [
372 | "arm64"
373 | ],
374 | "optional": true,
375 | "os": [
376 | "win32"
377 | ],
378 | "engines": {
379 | "node": ">=12"
380 | }
381 | },
382 | "node_modules/zerespluginlibrary": {
383 | "version": "2.0.6",
384 | "resolved": "https://registry.npmjs.org/zerespluginlibrary/-/zerespluginlibrary-2.0.6.tgz",
385 | "integrity": "sha512-xFJGAKWWHVhCPCX6Qq1tsVJzOzTHRF7xnz2bu+3DKGaXsNCP7hPxy0LJ3mXtHLeSlH3v9ARZ0kzDs4cj5bCVnw==",
386 | "bin": {
387 | "zpl": "bin/zpl.js"
388 | }
389 | }
390 | },
391 | "dependencies": {
392 | "@esbuild/android-arm": {
393 | "version": "0.15.16",
394 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.16.tgz",
395 | "integrity": "sha512-nyB6CH++2mSgx3GbnrJsZSxzne5K0HMyNIWafDHqYy7IwxFc4fd/CgHVZXr8Eh+Q3KbIAcAe3vGyqIPhGblvMQ==",
396 | "optional": true
397 | },
398 | "@esbuild/linux-loong64": {
399 | "version": "0.15.16",
400 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.16.tgz",
401 | "integrity": "sha512-SDLfP1uoB0HZ14CdVYgagllgrG7Mdxhkt4jDJOKl/MldKrkQ6vDJMZKl2+5XsEY/Lzz37fjgLQoJBGuAw/x8kQ==",
402 | "optional": true
403 | },
404 | "esbuild": {
405 | "version": "0.15.16",
406 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.16.tgz",
407 | "integrity": "sha512-o6iS9zxdHrrojjlj6pNGC2NAg86ECZqIETswTM5KmJitq+R1YmahhWtMumeQp9lHqJaROGnsBi2RLawGnfo5ZQ==",
408 | "requires": {
409 | "@esbuild/android-arm": "0.15.16",
410 | "@esbuild/linux-loong64": "0.15.16",
411 | "esbuild-android-64": "0.15.16",
412 | "esbuild-android-arm64": "0.15.16",
413 | "esbuild-darwin-64": "0.15.16",
414 | "esbuild-darwin-arm64": "0.15.16",
415 | "esbuild-freebsd-64": "0.15.16",
416 | "esbuild-freebsd-arm64": "0.15.16",
417 | "esbuild-linux-32": "0.15.16",
418 | "esbuild-linux-64": "0.15.16",
419 | "esbuild-linux-arm": "0.15.16",
420 | "esbuild-linux-arm64": "0.15.16",
421 | "esbuild-linux-mips64le": "0.15.16",
422 | "esbuild-linux-ppc64le": "0.15.16",
423 | "esbuild-linux-riscv64": "0.15.16",
424 | "esbuild-linux-s390x": "0.15.16",
425 | "esbuild-netbsd-64": "0.15.16",
426 | "esbuild-openbsd-64": "0.15.16",
427 | "esbuild-sunos-64": "0.15.16",
428 | "esbuild-windows-32": "0.15.16",
429 | "esbuild-windows-64": "0.15.16",
430 | "esbuild-windows-arm64": "0.15.16"
431 | }
432 | },
433 | "esbuild-android-64": {
434 | "version": "0.15.16",
435 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.16.tgz",
436 | "integrity": "sha512-Vwkv/sT0zMSgPSVO3Jlt1pUbnZuOgtOQJkJkyyJFAlLe7BiT8e9ESzo0zQSx4c3wW4T6kGChmKDPMbWTgtliQA==",
437 | "optional": true
438 | },
439 | "esbuild-android-arm64": {
440 | "version": "0.15.16",
441 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.16.tgz",
442 | "integrity": "sha512-lqfKuofMExL5niNV3gnhMUYacSXfsvzTa/58sDlBET/hCOG99Zmeh+lz6kvdgvGOsImeo6J9SW21rFCogNPLxg==",
443 | "optional": true
444 | },
445 | "esbuild-darwin-64": {
446 | "version": "0.15.16",
447 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.16.tgz",
448 | "integrity": "sha512-wo2VWk/n/9V2TmqUZ/KpzRjCEcr00n7yahEdmtzlrfQ3lfMCf3Wa+0sqHAbjk3C6CKkR3WKK/whkMq5Gj4Da9g==",
449 | "optional": true
450 | },
451 | "esbuild-darwin-arm64": {
452 | "version": "0.15.16",
453 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.16.tgz",
454 | "integrity": "sha512-fMXaUr5ou0M4WnewBKsspMtX++C1yIa3nJ5R2LSbLCfJT3uFdcRoU/NZjoM4kOMKyOD9Sa/2vlgN8G07K3SJnw==",
455 | "optional": true
456 | },
457 | "esbuild-freebsd-64": {
458 | "version": "0.15.16",
459 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.16.tgz",
460 | "integrity": "sha512-UzIc0xlRx5x9kRuMr+E3+hlSOxa/aRqfuMfiYBXu2jJ8Mzej4lGL7+o6F5hzhLqWfWm1GWHNakIdlqg1ayaTNQ==",
461 | "optional": true
462 | },
463 | "esbuild-freebsd-arm64": {
464 | "version": "0.15.16",
465 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.16.tgz",
466 | "integrity": "sha512-8xyiYuGc0DLZphFQIiYaLHlfoP+hAN9RHbE+Ibh8EUcDNHAqbQgUrQg7pE7Bo00rXmQ5Ap6KFgcR0b4ALZls1g==",
467 | "optional": true
468 | },
469 | "esbuild-linux-32": {
470 | "version": "0.15.16",
471 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.16.tgz",
472 | "integrity": "sha512-iGijUTV+0kIMyUVoynK0v+32Oi8yyp0xwMzX69GX+5+AniNy/C/AL1MjFTsozRp/3xQPl7jVux/PLe2ds10/2w==",
473 | "optional": true
474 | },
475 | "esbuild-linux-64": {
476 | "version": "0.15.16",
477 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.16.tgz",
478 | "integrity": "sha512-tuSOjXdLw7VzaUj89fIdAaQT7zFGbKBcz4YxbWrOiXkwscYgE7HtTxUavreBbnRkGxKwr9iT/gmeJWNm4djy/g==",
479 | "optional": true
480 | },
481 | "esbuild-linux-arm": {
482 | "version": "0.15.16",
483 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.16.tgz",
484 | "integrity": "sha512-XKcrxCEXDTOuoRj5l12tJnkvuxXBMKwEC5j0JISw3ziLf0j4zIwXbKbTmUrKFWbo6ZgvNpa7Y5dnbsjVvH39bQ==",
485 | "optional": true
486 | },
487 | "esbuild-linux-arm64": {
488 | "version": "0.15.16",
489 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.16.tgz",
490 | "integrity": "sha512-mPYksnfHnemNrvjrDhZyixL/AfbJN0Xn9S34ZOHYdh6/jJcNd8iTsv3JwJoEvTJqjMggjMhGUPJAdjnFBHoH8A==",
491 | "optional": true
492 | },
493 | "esbuild-linux-mips64le": {
494 | "version": "0.15.16",
495 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.16.tgz",
496 | "integrity": "sha512-kSJO2PXaxfm0pWY39+YX+QtpFqyyrcp0ZeI8QPTrcFVQoWEPiPVtOfTZeS3ZKedfH+Ga38c4DSzmKMQJocQv6A==",
497 | "optional": true
498 | },
499 | "esbuild-linux-ppc64le": {
500 | "version": "0.15.16",
501 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.16.tgz",
502 | "integrity": "sha512-NimPikwkBY0yGABw6SlhKrtT35sU4O23xkhlrTT/O6lSxv3Pm5iSc6OYaqVAHWkLdVf31bF4UDVFO+D990WpAA==",
503 | "optional": true
504 | },
505 | "esbuild-linux-riscv64": {
506 | "version": "0.15.16",
507 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.16.tgz",
508 | "integrity": "sha512-ty2YUHZlwFOwp7pR+J87M4CVrXJIf5ZZtU/umpxgVJBXvWjhziSLEQxvl30SYfUPq0nzeWKBGw5i/DieiHeKfw==",
509 | "optional": true
510 | },
511 | "esbuild-linux-s390x": {
512 | "version": "0.15.16",
513 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.16.tgz",
514 | "integrity": "sha512-VkZaGssvPDQtx4fvVdZ9czezmyWyzpQhEbSNsHZZN0BHvxRLOYAQ7sjay8nMQwYswP6O2KlZluRMNPYefFRs+w==",
515 | "optional": true
516 | },
517 | "esbuild-netbsd-64": {
518 | "version": "0.15.16",
519 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.16.tgz",
520 | "integrity": "sha512-ElQ9rhdY51et6MJTWrCPbqOd/YuPowD7Cxx3ee8wlmXQQVW7UvQI6nSprJ9uVFQISqSF5e5EWpwWqXZsECLvXg==",
521 | "optional": true
522 | },
523 | "esbuild-openbsd-64": {
524 | "version": "0.15.16",
525 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.16.tgz",
526 | "integrity": "sha512-KgxMHyxMCT+NdLQE1zVJEsLSt2QQBAvJfmUGDmgEq8Fvjrf6vSKB00dVHUEDKcJwMID6CdgCpvYNt999tIYhqA==",
527 | "optional": true
528 | },
529 | "esbuild-sunos-64": {
530 | "version": "0.15.16",
531 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.16.tgz",
532 | "integrity": "sha512-exSAx8Phj7QylXHlMfIyEfNrmqnLxFqLxdQF6MBHPdHAjT7fsKaX6XIJn+aQEFiOcE4X8e7VvdMCJ+WDZxjSRQ==",
533 | "optional": true
534 | },
535 | "esbuild-windows-32": {
536 | "version": "0.15.16",
537 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.16.tgz",
538 | "integrity": "sha512-zQgWpY5pUCSTOwqKQ6/vOCJfRssTvxFuEkpB4f2VUGPBpdddZfdj8hbZuFRdZRPIVHvN7juGcpgCA/XCF37mAQ==",
539 | "optional": true
540 | },
541 | "esbuild-windows-64": {
542 | "version": "0.15.16",
543 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.16.tgz",
544 | "integrity": "sha512-HjW1hHRLSncnM3MBCP7iquatHVJq9l0S2xxsHHj4yzf4nm9TU4Z7k4NkeMlD/dHQ4jPlQQhwcMvwbJiOefSuZw==",
545 | "optional": true
546 | },
547 | "esbuild-windows-arm64": {
548 | "version": "0.15.16",
549 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.16.tgz",
550 | "integrity": "sha512-oCcUKrJaMn04Vxy9Ekd8x23O8LoU01+4NOkQ2iBToKgnGj5eo1vU9i27NQZ9qC8NFZgnQQZg5oZWAejmbsppNA==",
551 | "optional": true
552 | },
553 | "zerespluginlibrary": {
554 | "version": "2.0.6",
555 | "resolved": "https://registry.npmjs.org/zerespluginlibrary/-/zerespluginlibrary-2.0.6.tgz",
556 | "integrity": "sha512-xFJGAKWWHVhCPCX6Qq1tsVJzOzTHRF7xnz2bu+3DKGaXsNCP7hPxy0LJ3mXtHLeSlH3v9ARZ0kzDs4cj5bCVnw=="
557 | }
558 | }
559 | }
560 |
--------------------------------------------------------------------------------
/bd/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tuxphones",
3 | "version": "0.1.0",
4 | "description": "Discord soundshare for Linux",
5 | "main": "index.jsx",
6 | "scripts": {
7 | "build": "esbuild src/Tuxphones/index.jsx --bundle --outfile=src/Tuxphones/bundled.js --platform=node && zpl build Tuxphones",
8 | "init": "zpl init"
9 | },
10 | "author": "Jack Hogan",
11 | "license": "MIT",
12 | "dependencies": {
13 | "esbuild": "^0.15.16",
14 | "zerespluginlibrary": "^2.0.6"
15 | },
16 | "zplConfig": {
17 | "base": "./src",
18 | "out": "./release",
19 | "copyToBD": true,
20 | "addInstallScript": true
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/bd/release/Tuxphones.plugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @name Tuxphones
3 | * @description Tuxphones
4 | * @version 0.1.0
5 | * @author ImTheSquid
6 | * @authorId 262055523896131584
7 | * @website https://github.com/ImTheSquid/Tuxphones
8 | * @source https://raw.githubusercontent.com/ImTheSquid/Tuxphones/main/plugin/Tuxphones.plugin.js
9 | */
10 | /*@cc_on
11 | @if (@_jscript)
12 |
13 | // Offer to self-install for clueless users that try to run this directly.
14 | var shell = WScript.CreateObject("WScript.Shell");
15 | var fs = new ActiveXObject("Scripting.FileSystemObject");
16 | var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\\BetterDiscord\\plugins");
17 | var pathSelf = WScript.ScriptFullName;
18 | // Put the user at ease by addressing them in the first person
19 | shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30);
20 | if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {
21 | shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40);
22 | } else if (!fs.FolderExists(pathPlugins)) {
23 | shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10);
24 | } else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) {
25 | fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);
26 | // Show the user where to put plugins in the future
27 | shell.Exec("explorer " + pathPlugins);
28 | shell.Popup("I'm installed!", 0, "Successfully installed", 0x40);
29 | }
30 | WScript.Quit();
31 |
32 | @else@*/
33 | const config = {
34 | info: {
35 | name: "Tuxphones",
36 | authors: [
37 | {
38 | name: "ImTheSquid",
39 | discord_id: "262055523896131584",
40 | github_username: "ImTheSquid",
41 | twitter_username: "ImTheSquid11"
42 | }
43 | ],
44 | version: "0.1.0",
45 | description: "Tuxphones",
46 | github: "https://github.com/ImTheSquid/Tuxphones",
47 | github_raw: "https://raw.githubusercontent.com/ImTheSquid/Tuxphones/main/plugin/Tuxphones.plugin.js"
48 | },
49 | main: "bundled.js"
50 | };
51 | class Dummy {
52 | constructor() {this._config = config;}
53 | start() {}
54 | stop() {}
55 | }
56 |
57 | if (!global.ZeresPluginLibrary) {
58 | BdApi.showConfirmationModal("Library Missing", `The library plugin needed for ${config.name ?? config.info.name} is missing. Please click Download Now to install it.`, {
59 | confirmText: "Download Now",
60 | cancelText: "Cancel",
61 | onConfirm: () => {
62 | require("request").get("https://betterdiscord.app/gh-redirect?id=9", async (err, resp, body) => {
63 | if (err) return require("electron").shell.openExternal("https://betterdiscord.app/Download?id=9");
64 | if (resp.statusCode === 302) {
65 | require("request").get(resp.headers.location, async (error, response, content) => {
66 | if (error) return require("electron").shell.openExternal("https://betterdiscord.app/Download?id=9");
67 | await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), content, r));
68 | });
69 | }
70 | else {
71 | await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r));
72 | }
73 | });
74 | }
75 | });
76 | }
77 |
78 | module.exports = !global.ZeresPluginLibrary ? Dummy : (([Plugin, Api]) => {
79 | const plugin = (Plugin, Library) => {
80 | const { Logger, Patcher, WebpackModules, DiscordModules, ContextMenu } = Library;
81 | const { Dispatcher, SelectedChannelStore, ButtonData, UserStore } = DiscordModules;
82 | const React = BdApi.React;
83 | const AuthenticationStore = Object.values(ZLibrary.WebpackModules.getAllModules()).find((m) => m.exports?.default?.getToken).exports.default;
84 | const RTCConnectionStore = BdApi.Webpack.getModule(BdApi.Webpack.Filters.byProps("getRTCConnectionId", "getWasEverRtcConnected"));
85 | const ChunkedRequests = BdApi.findModuleByProps("makeChunkedRequest");
86 | const WebSocketControl = BdApi.Webpack.getModule(BdApi.Webpack.Filters.byProps("lastTimeConnectedChanged")).getSocket();
87 | const GoLiveModal = BdApi.Webpack.getModule((m) => m.default?.toString().includes("GO_LIVE_MODAL"));
88 | const GetDesktopSources = BdApi.Webpack.getModule(BdApi.Webpack.Filters.byStrings("Can't get desktop sources outside of native app"), { defaultExport: false });
89 | function getFunctionNameFromString(obj, search) {
90 | for (const [k, v] of Object.entries(obj)) {
91 | if (search.every((str) => v?.toString().match(str))) {
92 | return k;
93 | }
94 | }
95 | return null;
96 | }
97 | return class extends Plugin {
98 | onStart() {
99 | this.webSocket = new WebSocket("ws://127.0.0.1:9000");
100 | this.webSocket.onmessage = this.parseData;
101 | this.webSocket.onerror = (_) => {
102 | BdApi.showConfirmationModal("Tuxphones Daemon Error", [
103 | "The Tuxphones daemon was not detected.\n",
104 | "If you don't know what this means or installed just the plugin and not the daemon, get help installing the daemon by going to the GitHub page:",
105 | /* @__PURE__ */ React.createElement("a", {
106 | href: "https://github.com/ImTheSquid/Tuxphones",
107 | target: "_blank"
108 | }, "Tuxphones Github"),
109 | " \n",
110 | `If you're sure you already installed the daemon, make sure it's running then click "Reload Discord".`
111 | ], {
112 | danger: true,
113 | confirmText: "Reload Discord",
114 | cancelText: "Stop Tuxphones",
115 | onConfirm: () => {
116 | location.reload();
117 | }
118 | });
119 | };
120 | this.webSocket.onopen = (_) => this.onOpen();
121 | this.interceptNextStreamServerUpdate = false;
122 | this.currentSoundProfile = null;
123 | this.selectedFPS = null;
124 | this.selectedResolution = null;
125 | this.serverId = null;
126 | this.wsOnMessage = this.wsOnMessage.bind(this);
127 | this._onmessage = null;
128 | this._ws = null;
129 | this.voice_ssrc = null;
130 | }
131 | onOpen() {
132 | Patcher.before(WebSocket.prototype, "send", (that, args) => {
133 | const arg = args[0];
134 | if (typeof arg !== "string" || !that.url.includes("discord") || this._ws && this._ws !== that)
135 | return;
136 | const json = JSON.parse(arg);
137 | console.log("%cWS SEND FRAME ================================", "color: green; font-size: large; margin-top: 20px;");
138 | if (json.op === 0 && json.d.streams.length > 0 && json.d.streams[0].type === "screen" && json.d.user_id === UserStore.getCurrentUser().id) {
139 | console.log("%cHOOKING SOCKET", "color: blue; font-size: xx-large;");
140 | if (this._ws) {
141 | this.resetVars();
142 | }
143 | this._ws = that;
144 | this._onmessage = that.onmessage;
145 | that.onmessage = this.wsOnMessage;
146 | } else if (json.op == 1 && this._ws === that) {
147 | json.d.data.mode = "xsalsa20_poly1305_lite";
148 | json.d.mode = "xsalsa20_poly1305_lite";
149 | args[0] = JSON.stringify(json);
150 | } else if (json.op == 5) {
151 | if (this.voice_ssrc) {
152 | this.audio_ssrc = json.d.ssrc;
153 | json.d.speaking = 1;
154 | args[0] = JSON.stringify(json);
155 | } else {
156 | this.voice_ssrc = json.d.ssrc;
157 | }
158 | }
159 | Logger.log(json);
160 | console.log("%cWS END SEND FRAME ============================", "color: green; font-size: large; margin-bottom: 20px;");
161 | });
162 | Patcher.before(WebSocket.prototype, "close", (that, [arg]) => {
163 | Logger.log("TUXPHONES CLOSE!");
164 | Logger.log(that);
165 | Logger.log(arg);
166 | if (this._ws === that) {
167 | console.log("%cSCREENSHARE CLOSED! Unlocking log...", "color: red; font-size: x-large;");
168 | if (this._ws) {
169 | this.resetVars();
170 | }
171 | }
172 | });
173 | Patcher.instead(Dispatcher, "dispatch", (_, [arg], original) => {
174 | if (this.interceptNextStreamServerUpdate && arg.type === "STREAM_SERVER_UPDATE") {
175 | Logger.log("STREAM SERVER UPDATE INTERCEPTED");
176 | Logger.log(arg);
177 | if (arg.streamKey) {
178 | this.streamKey = arg.streamKey;
179 | }
180 | WebSocketControl.streamSetPaused(this.streamKey, false);
181 | Logger.log(this.streamKey);
182 | }
183 | return original(arg);
184 | });
185 | this.showTuxOk = false;
186 | if (GoLiveModal)
187 | this.patchGoLive(GoLiveModal);
188 | else {
189 | new Promise((resolve) => {
190 | const cancel = WebpackModules.addListener((module2) => {
191 | if (!module2.default?.toString().includes("GO_LIVE_MODAL"))
192 | return;
193 | resolve(module2);
194 | cancel();
195 | });
196 | }).then((m) => {
197 | this.patchGoLive(m);
198 | });
199 | }
200 | this.observer = new MutationObserver((mutations) => {
201 | if (mutations.filter((mut) => mut.addedNodes.length === 0 && mut.target.hasChildNodes()).length == 0)
202 | return;
203 | const res = mutations.flatMap((mut) => Array.from(mut.target.childNodes.values())).filter((node) => node.childNodes.length === 1).flatMap((node) => Array.from(node.childNodes.values())).filter((node) => node.nodeName === "DIV" && Array.from(node.childNodes.values()).some((node2) => node2.matches && node2.matches("[class*=flex]")))[0];
204 | if (res) {
205 | res.querySelector("[class*=flex]").innerText = this.showTuxOk ? "Tuxphones sound enabled!" : "Tuxphones not available.";
206 | }
207 | });
208 | this.observer.observe(document.querySelector("div > [class^=layerContainer]"), { childList: true, subtree: true });
209 | Patcher.after(GetDesktopSources, getFunctionNameFromString(GetDesktopSources, [/getDesktopCaptureSources/]), (_, __, ret) => {
210 | return ret.then((vals) => new Promise((res) => {
211 | const f = function dispatch(e) {
212 | Dispatcher.unsubscribe("TUX_APPS", dispatch);
213 | Logger.log("Found Sources:");
214 | Logger.log(vals);
215 | Logger.log("Found Sound Apps:");
216 | Logger.log(e.apps);
217 | res(vals.map((v) => {
218 | let found = e.apps.find((el) => el.xid === parseInt(v.id.split(":")[1]));
219 | if (v.id.startsWith("window") && found) {
220 | Logger.log(`Associating ${v.id} with sound profile for ${found.name}`);
221 | v.sound = found;
222 | } else {
223 | v.sound = null;
224 | }
225 | return v;
226 | }));
227 | };
228 | Dispatcher.subscribe("TUX_APPS", f);
229 | this.getInfo(vals.filter((v) => v.id.startsWith("window")).map((v) => parseInt(v.id.split(":")[1])));
230 | }));
231 | });
232 | }
233 | wsOnMessage(m) {
234 | const json = JSON.parse(m.data);
235 | console.log("%cWS RECV FRAME ================================", "color: orange; font-size: large; margin-top: 20px;");
236 | if (json.op === 4) {
237 | console.log("%cRECEIVED CODEC AND ENCRYPTION INFORMATION", "color: aqua; font-size: xx-large;");
238 | Logger.log("Audio Codec:");
239 | Logger.log(json.d.audio_codec);
240 | Logger.log("Encryption Mode:");
241 | Logger.log(json.d.mode);
242 | Logger.log("Secret key:");
243 | Logger.log(json.d.secret_key);
244 | this.secret_key = json.d.secret_key;
245 | const op12 = {
246 | "op": 12,
247 | "d": {
248 | "audio_ssrc": this.audio_ssrc,
249 | "video_ssrc": this.video_ssrc,
250 | "rtx_ssrc": this.video_ssrc + 1,
251 | "streams": [
252 | {
253 | "type": "video",
254 | "rid": "100",
255 | "ssrc": this.video_ssrc,
256 | "active": true,
257 | "quality": 100,
258 | "rtx_ssrc": this.video_ssrc + 1,
259 | "max_bitrate": 8e6,
260 | "max_framerate": this.selectedFPS,
261 | "max_resolution": {
262 | "type": "fixed",
263 | "width": this.selectedResolution.width,
264 | "height": this.selectedResolution.height
265 | }
266 | }
267 | ]
268 | }
269 | };
270 | this._ws.send(JSON.stringify(op12));
271 | this.startStream(this.currentSoundProfile.pid, this.currentSoundProfile.xid, this.selectedResolution, this.selectedFPS, this.ip, this.port, this.secret_key, this.voice_ssrc, this.base_ssrc, this.audio_ssrc);
272 | return;
273 | } else if (json.op == 2) {
274 | this.base_ssrc = json.d.ssrc;
275 | this.ip = json.d.ip;
276 | this.port = json.d.port;
277 | }
278 | Logger.log(json);
279 | console.log("%cWS END RECV FRAME ============================", "color: orange; font-size: large; margin-bottom: 20px;");
280 | this._onmessage(m);
281 | }
282 | resetVars() {
283 | this.endStream();
284 | this._ws.onmessage = this._onmessage;
285 | this._ws = null;
286 | this._onmessage = null;
287 | this.currentSoundProfile = null;
288 | this.interceptNextStreamServerUpdate = false;
289 | this.base_ssrc = null;
290 | this.voice_ssrc = null;
291 | this.audio_ssrc = null;
292 | }
293 | patchGoLive(m) {
294 | Patcher.after(m, "default", (_, __, ret) => {
295 | Logger.log(ret);
296 | if (ret.props.children.props.children[2].props.children[1].props.activeSlide == 2) {
297 | if (ret.props.children.props.children[2].props.children[1].props.children[2].props.children.props.children.props.selectedSource.sound) {
298 | this.showTuxOk = true;
299 | ret.props.children.props.children[2].props.children[2].props.children[0] = /* @__PURE__ */ React.createElement("div", {
300 | style: { "margin-right": "8px" }
301 | }, React.createElement(ButtonData, {
302 | onClick: () => {
303 | const streamInfo = ret.props.children.props.children[2].props.children[1].props.children[2].props.children.props.children.props;
304 | this.currentSoundProfile = streamInfo.selectedSource.sound;
305 | this.selectedFPS = streamInfo.selectedFPS;
306 | this.selectedResolution = streamInfo.selectedResolution;
307 | Logger.log("Creating Sound Stream");
308 | this.createStream(streamInfo.guildId, SelectedChannelStore.getVoiceChannelId());
309 | },
310 | size: ButtonData.Sizes.SMALL
311 | }, "Go Live with Sound"));
312 | } else {
313 | this.showTuxOk = false;
314 | }
315 | }
316 | });
317 | }
318 | createStream(guild_id, channel_id) {
319 | this.interceptNextStreamServerUpdate = true;
320 | WebSocketControl.streamCreate(guild_id === null ? "call" : "guild", guild_id, channel_id, null);
321 | }
322 | parseData(msg) {
323 | let obj = JSON.parse(msg.data);
324 | Logger.log(obj);
325 | switch (obj.type) {
326 | case "ApplicationList":
327 | Dispatcher.dispatch({
328 | type: "TUX_APPS",
329 | apps: obj.apps
330 | });
331 | break;
332 | case "StreamPreview":
333 | Logger.log(this.streamKey);
334 | ChunkedRequests.makeChunkedRequest(`/streams/${this.streamKey}/preview`, {
335 | thumbnail: `data:image/jpeg;base64,${obj.jpg}`
336 | }, {
337 | method: "POST",
338 | token: AuthenticationStore.getToken()
339 | });
340 | break;
341 | default:
342 | Logger.err(`Received unknown command type: ${obj.type}`);
343 | }
344 | }
345 | startStream(pid, xid, selectedResolution, framerate, ip, port, secret_key, voice_ssrc, base_ssrc, audio_ssrc) {
346 | let resolution = null;
347 | switch (selectedResolution) {
348 | case 720:
349 | resolution = {
350 | width: 1280,
351 | height: 720,
352 | is_fixed: true
353 | };
354 | break;
355 | case 1080:
356 | resolution = {
357 | width: 1920,
358 | height: 1080,
359 | is_fixed: true
360 | };
361 | break;
362 | default:
363 | resolution = {
364 | width: 0,
365 | height: 0,
366 | is_fixed: false
367 | };
368 | break;
369 | }
370 | this.webSocket.send(JSON.stringify({
371 | type: "StartStream",
372 | pid,
373 | xid,
374 | resolution,
375 | framerate,
376 | rtc_connection_id: RTCConnectionStore.getRTCConnectionId(),
377 | secret_key,
378 | voice_ssrc,
379 | base_ssrc,
380 | ip,
381 | port,
382 | audio_ssrc
383 | }));
384 | }
385 | endStream() {
386 | this.webSocket.send(JSON.stringify({
387 | type: "StopStream"
388 | }));
389 | }
390 | getInfo(xids) {
391 | this.webSocket.send(JSON.stringify({
392 | type: "GetInfo",
393 | xids
394 | }));
395 | }
396 | onStop() {
397 | this.webSocket.close();
398 | if (this._ws) {
399 | this.resetVars();
400 | }
401 | Patcher.unpatchAll();
402 | if (this.observer)
403 | this.observer.disconnect();
404 | }
405 | };
406 | };
407 | return plugin(Plugin, Api);
408 | })(global.ZeresPluginLibrary.buildPlugin(config));
409 | /*@end@*/
--------------------------------------------------------------------------------
/bd/src/Tuxphones/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "info": {
3 | "name": "Tuxphones",
4 | "authors": [{
5 | "name": "ImTheSquid",
6 | "discord_id": "262055523896131584",
7 | "github_username": "ImTheSquid",
8 | "twitter_username": "ImTheSquid11"
9 | }],
10 | "version": "0.1.0",
11 | "description": "Tuxphones",
12 | "github": "https://github.com/ImTheSquid/Tuxphones",
13 | "github_raw": "https://raw.githubusercontent.com/ImTheSquid/Tuxphones/main/plugin/Tuxphones.plugin.js"
14 | },
15 | "main": "bundled.js"
16 | }
--------------------------------------------------------------------------------
/bd/src/Tuxphones/index.jsx:
--------------------------------------------------------------------------------
1 | module.exports = (Plugin, Library) => {
2 | const {Logger, Patcher, WebpackModules, DiscordModules, ContextMenu} = Library;
3 | const { Dispatcher, SelectedChannelStore, ButtonData, UserStore } = DiscordModules;
4 | const React = BdApi.React;
5 |
6 | // Useful modules maybe: ApplicationStreamingSettingsStore, ApplicationStreamingStore
7 | const AuthenticationStore = Object.values(ZLibrary.WebpackModules.getAllModules()).find(m => m.exports?.default?.getToken).exports.default; // Works (should be replaced with custom solution eventually)
8 | const RTCConnectionStore = BdApi.Webpack.getModule(BdApi.Webpack.Filters.byProps("getRTCConnectionId", "getWasEverRtcConnected"));
9 | //const WebRequests = BdApi.Webpack.getModule(BdApi.Webpack.Filters.byProps("getXHR"));
10 | const ChunkedRequests = BdApi.findModuleByProps("makeChunkedRequest");
11 | //const RTCControlSocket = BdApi.Webpack.getModule(m => m.Z?.prototype?.connect);
12 | const WebSocketControl = BdApi.Webpack.getModule(BdApi.Webpack.Filters.byProps("lastTimeConnectedChanged")).getSocket();
13 | const GoLiveModal = BdApi.Webpack.getModule(m => m.default?.toString().includes("GO_LIVE_MODAL"));
14 | // const DesktopSourcesChecker = BdApi.Webpack.getModule(BdApi.Webpack.Filters.byProps("installedLogHooks")).prototype;
15 | const GetDesktopSources = BdApi.Webpack.getModule(BdApi.Webpack.Filters.byStrings("Can't get desktop sources outside of native app"), {defaultExport: false});
16 |
17 | function getFunctionNameFromString(obj, search) {
18 | for (const [k, v] of Object.entries(obj)) {
19 | if (search.every(str => v?.toString().match(str))) {
20 | return k;
21 | }
22 | }
23 | return null;
24 | }
25 |
26 | return class extends Plugin {
27 | onStart() {
28 | this.webSocket = new WebSocket("ws://127.0.0.1:9000");
29 | this.webSocket.onmessage = this.parseData;
30 | this.webSocket.onerror = _ => {
31 | BdApi.showConfirmationModal('Tuxphones Daemon Error', [
32 | 'The Tuxphones daemon was not detected.\n',
33 | 'If you don\'t know what this means or installed just the plugin and not the daemon, get help installing the daemon by going to the GitHub page:',
34 | Tuxphones Github,
35 | ' \n',
36 | 'If you\'re sure you already installed the daemon, make sure it\'s running then click "Reload Discord".'
37 | ], {
38 | danger: true,
39 | confirmText: 'Reload Discord',
40 | cancelText: 'Stop Tuxphones',
41 | onConfirm: () => {
42 | location.reload();
43 | }
44 | })
45 | }
46 |
47 | this.webSocket.onopen = _ => this.onOpen();
48 |
49 | // Hook Dispatcher for when to intercept
50 | this.interceptNextStreamServerUpdate = false;
51 | this.currentSoundProfile = null;
52 | this.selectedFPS = null;
53 | this.selectedResolution = null;
54 | this.serverId = null;
55 |
56 | this.wsOnMessage = this.wsOnMessage.bind(this);
57 | this._onmessage = null;
58 | this._ws = null;
59 |
60 | this.voice_ssrc = null;
61 | }
62 |
63 | onOpen() {
64 | Patcher.before(WebSocket.prototype, 'send', (that, args) => {
65 | const arg = args[0];
66 | if (typeof(arg) !== 'string' || !that.url.includes('discord') || (this._ws && this._ws !== that)) return;
67 |
68 | const json = JSON.parse(arg);
69 |
70 | console.log('%cWS SEND FRAME ================================', 'color: green; font-size: large; margin-top: 20px;');
71 |
72 | // Condition for voice stream: json.op === 0 && json.d.streams.length > 0 && json.d.streams[0].type === 'video' && json.d.user_id === UserStore.getCurrentUser().id
73 |
74 | // Check if stream has started, if so then hook onmessage
75 | if (json.op === 0 && json.d.streams.length > 0 && json.d.streams[0].type === 'screen' && json.d.user_id === UserStore.getCurrentUser().id) {
76 | console.log('%cHOOKING SOCKET', 'color: blue; font-size: xx-large;');
77 | if (this._ws) {
78 | this.resetVars();
79 | }
80 | this._ws = that;
81 | this._onmessage = that.onmessage;
82 | that.onmessage = this.wsOnMessage;
83 | // this.token = json.d.token;
84 | } else if (json.op == 1 && this._ws === that) {
85 | json.d.data.mode = 'xsalsa20_poly1305_lite';
86 | json.d.mode = 'xsalsa20_poly1305_lite';
87 | args[0] = JSON.stringify(json);
88 | } else if (json.op == 5) {
89 | // WARNING WARNING WARNING ==========================================================================
90 | // This is a hack, it may not always work!
91 | // Still need to test this in multi-person VC
92 | if (this.voice_ssrc) {
93 | this.audio_ssrc = json.d.ssrc;
94 | json.d.speaking = 1;
95 | args[0] = JSON.stringify(json);
96 | } else {
97 | this.voice_ssrc = json.d.ssrc;
98 | }
99 | }
100 | // else if (json.op === 12 && json.d.video_ssrc !== 0 && json.d.rtx_ssrc !== 0) {
101 | // console.log('%cRECEIVED SSRC INFORMATION', 'color: aqua; font-size: xx-large;');
102 | // Logger.log('Video SSRC:');
103 | // Logger.log(json.d.video_ssrc);
104 | // Logger.log('RTX SSRC:');
105 | // Logger.log(json.d.rtx_ssrc);
106 |
107 | // this.ssrc = json.d.video_ssrc;
108 | // const res = json.d.streams[0].max_resolution;
109 | // this.resolution = {
110 | // width: res.width,
111 | // height: res.height,
112 | // is_fixed: res.type === 'fixed'
113 | // };
114 | // }
115 |
116 | Logger.log(json);
117 | console.log('%cWS END SEND FRAME ============================', 'color: green; font-size: large; margin-bottom: 20px;');
118 | });
119 |
120 | Patcher.before(WebSocket.prototype, 'close', (that, [arg]) => {
121 | Logger.log('TUXPHONES CLOSE!');
122 | Logger.log(that);
123 | Logger.log(arg);
124 | if (this._ws === that) {
125 | console.log('%cSCREENSHARE CLOSED! Unlocking log...', 'color: red; font-size: x-large;');
126 | if (this._ws) {
127 | this.resetVars();
128 | }
129 | }
130 | });
131 |
132 | Patcher.instead(Dispatcher, 'dispatch', (_, [arg], original) => {
133 | if (this.interceptNextStreamServerUpdate && arg.type === 'STREAM_SERVER_UPDATE') {
134 | Logger.log("STREAM SERVER UPDATE INTERCEPTED");
135 | Logger.log(arg)
136 | // let res = null;
137 | // switch (this.selectedResolution) {
138 | // case 720: res = {
139 | // width: 1280,
140 | // height: 720,
141 | // is_fixed: true
142 | // };
143 | // break;
144 | // case 1080: res = {
145 | // width: 1920,
146 | // height: 1080,
147 | // is_fixed: true
148 | // };
149 | // break;
150 | // default: res = {
151 | // width: 0,
152 | // height: 0,
153 | // is_fixed: false
154 | // };
155 | // break;
156 | // }
157 |
158 | if (arg.streamKey) {
159 | this.streamKey = arg.streamKey;
160 | }
161 | WebSocketControl.streamSetPaused(this.streamKey, false);
162 | Logger.log(this.streamKey)
163 | // this.startStream(this.currentSoundProfile.pid, this.currentSoundProfile.xid, this.selectedResolution, this.selectedFPS, this.ip, this.port, this.secret_key, this.voice_ssrc, this.base_ssrc);
164 |
165 | // this.startStream(this.currentSoundProfile.pid, this.currentSoundProfile.xid, res, this.selectedFPS, this.serverId, arg.token, arg.endpoint);
166 | // return new Promise(res => res());
167 | }
168 | // } else if (this.currentSoundProfile) {
169 | // // Hide the stream's existence from Discord until ready to test Tuxphones/Discord interaction
170 | // switch (arg.type) {
171 | // case 'STREAM_CREATE':
172 | // Logger.log("SOUND SC PROFILE");
173 | // Logger.log(arg);
174 | // this.serverId = arg.rtcServerId;
175 | // break;
176 | // // return new Promise(res => res());
177 | // case 'STREAM_UPDATE':
178 | // Logger.log("SOUND SU PROFILE");
179 | // Logger.log(arg);
180 | // // this.streamKey = arg.streamKey;
181 | // break;
182 | // // return new Promise(res => res());
183 | // case 'VOICE_STATE_UPDATES':
184 | // Logger.log("SOUND VSU PROFILE");
185 | // Logger.log(arg);
186 | // arg.voiceStates[0].selfStream = false;
187 | // break;
188 | // }
189 | // } else if (arg.type.match(/(STREAM.*_UPDATE|STREAM_CREATE)/)) {
190 | // Logger.log("STREAM CREATE OR UPDATE");
191 | // Logger.log(arg);
192 | // }else {
193 | // // Logger.log(arg)
194 | // }
195 | return original(arg);
196 | });
197 |
198 | this.showTuxOk = false;
199 |
200 | if (GoLiveModal) this.patchGoLive(GoLiveModal)
201 | else {
202 | new Promise(resolve => {
203 | const cancel = WebpackModules.addListener(module => {
204 | if (!module.default?.toString().includes("GO_LIVE_MODAL")) return;
205 | resolve(module);
206 | cancel();
207 | });
208 | }).then(m => {
209 | this.patchGoLive(m);
210 | });
211 | }
212 |
213 | this.observer = new MutationObserver(mutations => {
214 | if (mutations.filter(mut => mut.addedNodes.length === 0 && mut.target.hasChildNodes()).length == 0) return;
215 |
216 | const res = mutations
217 | .flatMap(mut => Array.from(mut.target.childNodes.values()))
218 | .filter(node => node.childNodes.length === 1)
219 | .flatMap(node => Array.from(node.childNodes.values()))
220 | .filter(node => node.nodeName === "DIV" && Array.from(node.childNodes.values())
221 | .some(node => node.matches && node.matches("[class*=flex]")))[0];
222 |
223 | if (res) {
224 | res.querySelector("[class*=flex]").innerText = this.showTuxOk ? "Tuxphones sound enabled!" : "Tuxphones not available.";
225 | }
226 | });
227 |
228 | this.observer.observe(document.querySelector("div > [class^=layerContainer]"), {childList: true, subtree: true});
229 |
230 | // Add extra info to desktop sources list
231 | Patcher.after(GetDesktopSources, getFunctionNameFromString(GetDesktopSources, [/getDesktopCaptureSources/]), (_, __, ret) => {
232 | return ret.then(vals => new Promise(res => {
233 | const f = function dispatch(e) {
234 | Dispatcher.unsubscribe('TUX_APPS', dispatch);
235 |
236 | // Check against window IDs to see if comaptible with sound
237 | Logger.log("Found Sources:")
238 | Logger.log(vals);
239 | Logger.log("Found Sound Apps:")
240 | Logger.log(e.apps);
241 | res(vals.map(v => {
242 | let found = e.apps.find(el => el.xid === parseInt(v.id.split(':')[1]));
243 | if (v.id.startsWith('window') && found) {
244 | Logger.log(`Associating ${v.id} with sound profile for ${found.name}`)
245 | v.sound = found;
246 | } else {
247 | v.sound = null;
248 | }
249 | return v;
250 | }));
251 | }
252 |
253 | Dispatcher.subscribe('TUX_APPS', f);
254 | this.getInfo(vals.filter(v => v.id.startsWith('window')).map(v => parseInt(v.id.split(':')[1])));
255 | }));
256 | });
257 |
258 | // Patch stream to get IP address
259 | // Patcher.after(RTCControlSocket.Z.prototype, '_handleReady', (that, _, __) => {
260 | // Logger.log("handling ready")
261 | // that._connection.on("connected", (___, info) => {
262 | // Logger.log(info)
263 | // this.ip = info.address;
264 | // });
265 | // });
266 | }
267 |
268 | wsOnMessage(m) {
269 | const json = JSON.parse(m.data);
270 |
271 | console.log('%cWS RECV FRAME ================================', 'color: orange; font-size: large; margin-top: 20px;');
272 |
273 | if (json.op === 4) {
274 | console.log('%cRECEIVED CODEC AND ENCRYPTION INFORMATION', 'color: aqua; font-size: xx-large;');
275 | Logger.log('Audio Codec:');
276 | Logger.log(json.d.audio_codec);
277 | Logger.log('Encryption Mode:');
278 | Logger.log(json.d.mode);
279 | Logger.log('Secret key:');
280 | Logger.log(json.d.secret_key);
281 | this.secret_key = json.d.secret_key;
282 |
283 | // Send video stream op
284 | const op12 = {
285 | "op":12,
286 | "d":{
287 | "audio_ssrc":this.audio_ssrc,
288 | "video_ssrc":this.video_ssrc,
289 | "rtx_ssrc":this.video_ssrc + 1,
290 | "streams":[
291 | {
292 | "type":"video",
293 | "rid":"100",
294 | "ssrc":this.video_ssrc,
295 | "active":true,
296 | "quality":100,
297 | "rtx_ssrc":this.video_ssrc + 1,
298 | "max_bitrate":8000000,
299 | "max_framerate": this.selectedFPS,
300 | "max_resolution":{
301 | "type":"fixed",
302 | "width": this.selectedResolution.width,
303 | "height": this.selectedResolution.height,
304 | }
305 | }
306 | ]
307 | }
308 | }
309 | this._ws.send(JSON.stringify(op12));
310 |
311 | this.startStream(this.currentSoundProfile.pid, this.currentSoundProfile.xid, this.selectedResolution, this.selectedFPS, this.ip, this.port, this.secret_key, this.voice_ssrc, this.base_ssrc, this.audio_ssrc);
312 | return; // Disallow encryption information, stopping the stream from being created
313 | } else if (json.op == 2) {
314 | this.base_ssrc = json.d.ssrc;
315 | this.ip = json.d.ip;
316 | this.port = json.d.port;
317 | }
318 |
319 | Logger.log(json);
320 |
321 | console.log('%cWS END RECV FRAME ============================', 'color: orange; font-size: large; margin-bottom: 20px;');
322 |
323 | this._onmessage(m);
324 | }
325 |
326 | resetVars() {
327 | this.endStream();
328 | this._ws.onmessage = this._onmessage;
329 | this._ws = null;
330 | this._onmessage = null;
331 | this.currentSoundProfile = null;
332 | this.interceptNextStreamServerUpdate = false;
333 | this.base_ssrc = null;
334 | this.voice_ssrc = null;
335 | this.audio_ssrc = null;
336 | }
337 |
338 | patchGoLive(m) {
339 | Patcher.after(m, 'default', (_, __, ret) => {
340 | Logger.log(ret)
341 |
342 | if (ret.props.children.props.children[2].props.children[1].props.activeSlide == 2) {
343 | if (ret.props.children.props.children[2].props.children[1].props.children[2].props.children.props.children.props.selectedSource.sound) {
344 | this.showTuxOk = true;
345 | ret.props.children.props.children[2].props.children[2].props.children[0] =
346 | {React.createElement(ButtonData, {
347 | onClick: () => {
348 | const streamInfo = ret.props.children.props.children[2].props.children[1].props.children[2].props.children.props.children.props;
349 | this.currentSoundProfile = streamInfo.selectedSource.sound;
350 | this.selectedFPS = streamInfo.selectedFPS;
351 | this.selectedResolution = streamInfo.selectedResolution;
352 | Logger.log("Creating Sound Stream");
353 | this.createStream(streamInfo.guildId, SelectedChannelStore.getVoiceChannelId());
354 | },
355 | size: ButtonData.Sizes.SMALL
356 | }, "Go Live with Sound")}
357 |
358 | } else {
359 | this.showTuxOk = false;
360 | }
361 | }
362 | });
363 | }
364 |
365 | createStream(guild_id, channel_id) {
366 | this.interceptNextStreamServerUpdate = true;
367 | WebSocketControl.streamCreate(
368 | guild_id === null ? 'call' : 'guild', // type
369 | guild_id, // guild_id
370 | channel_id, // channel or DM id
371 | null, // preferred_region
372 | );
373 | }
374 |
375 | parseData(msg) {
376 | let obj = JSON.parse(msg.data);
377 | Logger.log(obj)
378 | switch (obj.type) {
379 | case 'ApplicationList':
380 | Dispatcher.dispatch({
381 | type: 'TUX_APPS',
382 | apps: obj.apps
383 | });
384 | break;
385 | case 'StreamPreview':
386 | // Alternatively, DiscordNative.http.makeChunkedRequest
387 | Logger.log(this.streamKey)
388 | ChunkedRequests.makeChunkedRequest(`/streams/${this.streamKey}/preview`, {
389 | thumbnail: `data:image/jpeg;base64,${obj.jpg}` // May have to include charset?
390 | }, {
391 | method: 'POST',
392 | token: AuthenticationStore.getToken()
393 | });
394 | break;
395 | default:
396 | Logger.err(`Received unknown command type: ${obj.type}`);
397 | }
398 | }
399 |
400 | // server_id PRIORITY: RTC Server ID -> Guild ID -> Channel ID
401 | // Guild ID will always exist, so get RTC Server ID
402 | startStream(pid, xid, selectedResolution, framerate, ip, port, secret_key, voice_ssrc, base_ssrc, audio_ssrc) {
403 | let resolution = null;
404 | switch (selectedResolution) {
405 | case 720: resolution = {
406 | width: 1280,
407 | height: 720,
408 | is_fixed: true
409 | };
410 | break;
411 | case 1080: resolution = {
412 | width: 1920,
413 | height: 1080,
414 | is_fixed: true
415 | };
416 | break;
417 | default: resolution = {
418 | width: 0,
419 | height: 0,
420 | is_fixed: false
421 | };
422 | break;
423 | }
424 |
425 | this.webSocket.send(JSON.stringify({
426 | type: 'StartStream',
427 | pid: pid,
428 | xid: xid,
429 | resolution: resolution,
430 | framerate: framerate,
431 | // server_id: server_id,
432 | // user_id: AuthenticationStore.getId(),
433 | // token: token,
434 | // session_id: AuthenticationStore.getSessionId(), // getSessionId [no], getMediaSessionId [no], getRemoteSessionId [no], getActiveMediaSessionId [no]
435 | rtc_connection_id: RTCConnectionStore.getRTCConnectionId(),
436 | secret_key: secret_key,
437 | voice_ssrc: voice_ssrc,
438 | base_ssrc: base_ssrc,
439 | ip: ip,
440 | port: port,
441 | audio_ssrc: audio_ssrc,
442 | }));
443 | }
444 |
445 | endStream() {
446 | this.webSocket.send(JSON.stringify({
447 | type: 'StopStream'
448 | }));
449 | }
450 |
451 | getInfo(xids) {
452 | this.webSocket.send(JSON.stringify({
453 | type: 'GetInfo',
454 | xids: xids
455 | }));
456 | }
457 |
458 | onStop() {
459 | this.webSocket.close();
460 | if (this._ws) {
461 | this.resetVars();
462 | }
463 | Patcher.unpatchAll();
464 | if (this.observer)
465 | this.observer.disconnect();
466 | }
467 | }
468 | }
--------------------------------------------------------------------------------
/daemon/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "adler"
7 | version = "1.0.2"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
10 |
11 | [[package]]
12 | name = "aead"
13 | version = "0.5.2"
14 | source = "registry+https://github.com/rust-lang/crates.io-index"
15 | checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
16 | dependencies = [
17 | "crypto-common",
18 | "generic-array",
19 | ]
20 |
21 | [[package]]
22 | name = "aho-corasick"
23 | version = "1.0.1"
24 | source = "registry+https://github.com/rust-lang/crates.io-index"
25 | checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
26 | dependencies = [
27 | "memchr",
28 | ]
29 |
30 | [[package]]
31 | name = "android_system_properties"
32 | version = "0.1.5"
33 | source = "registry+https://github.com/rust-lang/crates.io-index"
34 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
35 | dependencies = [
36 | "libc",
37 | ]
38 |
39 | [[package]]
40 | name = "anyhow"
41 | version = "1.0.71"
42 | source = "registry+https://github.com/rust-lang/crates.io-index"
43 | checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
44 |
45 | [[package]]
46 | name = "async-tungstenite"
47 | version = "0.22.2"
48 | source = "registry+https://github.com/rust-lang/crates.io-index"
49 | checksum = "ce01ac37fdc85f10a43c43bc582cbd566720357011578a935761075f898baf58"
50 | dependencies = [
51 | "futures-io",
52 | "futures-util",
53 | "log",
54 | "pin-project-lite",
55 | "tokio",
56 | "tungstenite",
57 | ]
58 |
59 | [[package]]
60 | name = "atomic_refcell"
61 | version = "0.1.10"
62 | source = "registry+https://github.com/rust-lang/crates.io-index"
63 | checksum = "79d6dc922a2792b006573f60b2648076355daeae5ce9cb59507e5908c9625d31"
64 |
65 | [[package]]
66 | name = "autocfg"
67 | version = "1.1.0"
68 | source = "registry+https://github.com/rust-lang/crates.io-index"
69 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
70 |
71 | [[package]]
72 | name = "base64"
73 | version = "0.13.1"
74 | source = "registry+https://github.com/rust-lang/crates.io-index"
75 | checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
76 |
77 | [[package]]
78 | name = "bit_field"
79 | version = "0.10.2"
80 | source = "registry+https://github.com/rust-lang/crates.io-index"
81 | checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
82 |
83 | [[package]]
84 | name = "bitflags"
85 | version = "1.3.2"
86 | source = "registry+https://github.com/rust-lang/crates.io-index"
87 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
88 |
89 | [[package]]
90 | name = "block-buffer"
91 | version = "0.10.4"
92 | source = "registry+https://github.com/rust-lang/crates.io-index"
93 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
94 | dependencies = [
95 | "generic-array",
96 | ]
97 |
98 | [[package]]
99 | name = "bumpalo"
100 | version = "3.13.0"
101 | source = "registry+https://github.com/rust-lang/crates.io-index"
102 | checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
103 |
104 | [[package]]
105 | name = "bytemuck"
106 | version = "1.13.1"
107 | source = "registry+https://github.com/rust-lang/crates.io-index"
108 | checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
109 |
110 | [[package]]
111 | name = "byteorder"
112 | version = "1.4.3"
113 | source = "registry+https://github.com/rust-lang/crates.io-index"
114 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
115 |
116 | [[package]]
117 | name = "bytes"
118 | version = "1.4.0"
119 | source = "registry+https://github.com/rust-lang/crates.io-index"
120 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
121 |
122 | [[package]]
123 | name = "cc"
124 | version = "1.0.79"
125 | source = "registry+https://github.com/rust-lang/crates.io-index"
126 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
127 |
128 | [[package]]
129 | name = "cfg-expr"
130 | version = "0.15.1"
131 | source = "registry+https://github.com/rust-lang/crates.io-index"
132 | checksum = "c8790cf1286da485c72cf5fc7aeba308438800036ec67d89425924c4807268c9"
133 | dependencies = [
134 | "smallvec",
135 | "target-lexicon",
136 | ]
137 |
138 | [[package]]
139 | name = "cfg-if"
140 | version = "1.0.0"
141 | source = "registry+https://github.com/rust-lang/crates.io-index"
142 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
143 |
144 | [[package]]
145 | name = "chrono"
146 | version = "0.4.24"
147 | source = "registry+https://github.com/rust-lang/crates.io-index"
148 | checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"
149 | dependencies = [
150 | "iana-time-zone",
151 | "js-sys",
152 | "num-integer",
153 | "num-traits",
154 | "time 0.1.45",
155 | "wasm-bindgen",
156 | "winapi",
157 | ]
158 |
159 | [[package]]
160 | name = "cipher"
161 | version = "0.4.4"
162 | source = "registry+https://github.com/rust-lang/crates.io-index"
163 | checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
164 | dependencies = [
165 | "crypto-common",
166 | "inout",
167 | "zeroize",
168 | ]
169 |
170 | [[package]]
171 | name = "color_quant"
172 | version = "1.1.0"
173 | source = "registry+https://github.com/rust-lang/crates.io-index"
174 | checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
175 |
176 | [[package]]
177 | name = "core-foundation"
178 | version = "0.9.3"
179 | source = "registry+https://github.com/rust-lang/crates.io-index"
180 | checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
181 | dependencies = [
182 | "core-foundation-sys",
183 | "libc",
184 | ]
185 |
186 | [[package]]
187 | name = "core-foundation-sys"
188 | version = "0.8.4"
189 | source = "registry+https://github.com/rust-lang/crates.io-index"
190 | checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
191 |
192 | [[package]]
193 | name = "cpufeatures"
194 | version = "0.2.7"
195 | source = "registry+https://github.com/rust-lang/crates.io-index"
196 | checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58"
197 | dependencies = [
198 | "libc",
199 | ]
200 |
201 | [[package]]
202 | name = "crc32fast"
203 | version = "1.3.2"
204 | source = "registry+https://github.com/rust-lang/crates.io-index"
205 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
206 | dependencies = [
207 | "cfg-if",
208 | ]
209 |
210 | [[package]]
211 | name = "crossbeam-channel"
212 | version = "0.5.8"
213 | source = "registry+https://github.com/rust-lang/crates.io-index"
214 | checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
215 | dependencies = [
216 | "cfg-if",
217 | "crossbeam-utils",
218 | ]
219 |
220 | [[package]]
221 | name = "crossbeam-deque"
222 | version = "0.8.3"
223 | source = "registry+https://github.com/rust-lang/crates.io-index"
224 | checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
225 | dependencies = [
226 | "cfg-if",
227 | "crossbeam-epoch",
228 | "crossbeam-utils",
229 | ]
230 |
231 | [[package]]
232 | name = "crossbeam-epoch"
233 | version = "0.9.14"
234 | source = "registry+https://github.com/rust-lang/crates.io-index"
235 | checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
236 | dependencies = [
237 | "autocfg",
238 | "cfg-if",
239 | "crossbeam-utils",
240 | "memoffset",
241 | "scopeguard",
242 | ]
243 |
244 | [[package]]
245 | name = "crossbeam-utils"
246 | version = "0.8.15"
247 | source = "registry+https://github.com/rust-lang/crates.io-index"
248 | checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
249 | dependencies = [
250 | "cfg-if",
251 | ]
252 |
253 | [[package]]
254 | name = "crunchy"
255 | version = "0.2.2"
256 | source = "registry+https://github.com/rust-lang/crates.io-index"
257 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
258 |
259 | [[package]]
260 | name = "crypto-common"
261 | version = "0.1.6"
262 | source = "registry+https://github.com/rust-lang/crates.io-index"
263 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
264 | dependencies = [
265 | "generic-array",
266 | "rand_core",
267 | "typenum",
268 | ]
269 |
270 | [[package]]
271 | name = "ctrlc"
272 | version = "3.3.0"
273 | source = "registry+https://github.com/rust-lang/crates.io-index"
274 | checksum = "04d778600249295e82b6ab12e291ed9029407efee0cfb7baf67157edc65964df"
275 | dependencies = [
276 | "nix",
277 | "windows-sys 0.48.0",
278 | ]
279 |
280 | [[package]]
281 | name = "data-encoding"
282 | version = "2.4.0"
283 | source = "registry+https://github.com/rust-lang/crates.io-index"
284 | checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
285 |
286 | [[package]]
287 | name = "derivative"
288 | version = "2.2.0"
289 | source = "registry+https://github.com/rust-lang/crates.io-index"
290 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
291 | dependencies = [
292 | "proc-macro2",
293 | "quote",
294 | "syn 1.0.109",
295 | ]
296 |
297 | [[package]]
298 | name = "digest"
299 | version = "0.10.7"
300 | source = "registry+https://github.com/rust-lang/crates.io-index"
301 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
302 | dependencies = [
303 | "block-buffer",
304 | "crypto-common",
305 | ]
306 |
307 | [[package]]
308 | name = "discortp"
309 | version = "0.5.0"
310 | source = "registry+https://github.com/rust-lang/crates.io-index"
311 | checksum = "524b9439c09174aede2c88d58cfc6b83575b06569d1af4d07562f76595b2896b"
312 | dependencies = [
313 | "pnet_macros",
314 | "pnet_macros_support",
315 | ]
316 |
317 | [[package]]
318 | name = "either"
319 | version = "1.8.1"
320 | source = "registry+https://github.com/rust-lang/crates.io-index"
321 | checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
322 |
323 | [[package]]
324 | name = "errno"
325 | version = "0.3.1"
326 | source = "registry+https://github.com/rust-lang/crates.io-index"
327 | checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
328 | dependencies = [
329 | "errno-dragonfly",
330 | "libc",
331 | "windows-sys 0.48.0",
332 | ]
333 |
334 | [[package]]
335 | name = "errno-dragonfly"
336 | version = "0.1.2"
337 | source = "registry+https://github.com/rust-lang/crates.io-index"
338 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
339 | dependencies = [
340 | "cc",
341 | "libc",
342 | ]
343 |
344 | [[package]]
345 | name = "exr"
346 | version = "1.6.3"
347 | source = "registry+https://github.com/rust-lang/crates.io-index"
348 | checksum = "bdd2162b720141a91a054640662d3edce3d50a944a50ffca5313cd951abb35b4"
349 | dependencies = [
350 | "bit_field",
351 | "flume",
352 | "half",
353 | "lebe",
354 | "miniz_oxide 0.6.2",
355 | "rayon-core",
356 | "smallvec",
357 | "zune-inflate",
358 | ]
359 |
360 | [[package]]
361 | name = "fastrand"
362 | version = "1.9.0"
363 | source = "registry+https://github.com/rust-lang/crates.io-index"
364 | checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
365 | dependencies = [
366 | "instant",
367 | ]
368 |
369 | [[package]]
370 | name = "fdeflate"
371 | version = "0.3.0"
372 | source = "registry+https://github.com/rust-lang/crates.io-index"
373 | checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10"
374 | dependencies = [
375 | "simd-adler32",
376 | ]
377 |
378 | [[package]]
379 | name = "flate2"
380 | version = "1.0.26"
381 | source = "registry+https://github.com/rust-lang/crates.io-index"
382 | checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
383 | dependencies = [
384 | "crc32fast",
385 | "miniz_oxide 0.7.1",
386 | ]
387 |
388 | [[package]]
389 | name = "flume"
390 | version = "0.10.14"
391 | source = "registry+https://github.com/rust-lang/crates.io-index"
392 | checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
393 | dependencies = [
394 | "futures-core",
395 | "futures-sink",
396 | "nanorand",
397 | "pin-project",
398 | "spin",
399 | ]
400 |
401 | [[package]]
402 | name = "fnv"
403 | version = "1.0.7"
404 | source = "registry+https://github.com/rust-lang/crates.io-index"
405 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
406 |
407 | [[package]]
408 | name = "foreign-types"
409 | version = "0.3.2"
410 | source = "registry+https://github.com/rust-lang/crates.io-index"
411 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
412 | dependencies = [
413 | "foreign-types-shared",
414 | ]
415 |
416 | [[package]]
417 | name = "foreign-types-shared"
418 | version = "0.1.1"
419 | source = "registry+https://github.com/rust-lang/crates.io-index"
420 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
421 |
422 | [[package]]
423 | name = "form_urlencoded"
424 | version = "1.1.0"
425 | source = "registry+https://github.com/rust-lang/crates.io-index"
426 | checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
427 | dependencies = [
428 | "percent-encoding",
429 | ]
430 |
431 | [[package]]
432 | name = "futures-channel"
433 | version = "0.3.28"
434 | source = "registry+https://github.com/rust-lang/crates.io-index"
435 | checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
436 | dependencies = [
437 | "futures-core",
438 | ]
439 |
440 | [[package]]
441 | name = "futures-core"
442 | version = "0.3.28"
443 | source = "registry+https://github.com/rust-lang/crates.io-index"
444 | checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
445 |
446 | [[package]]
447 | name = "futures-executor"
448 | version = "0.3.28"
449 | source = "registry+https://github.com/rust-lang/crates.io-index"
450 | checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
451 | dependencies = [
452 | "futures-core",
453 | "futures-task",
454 | "futures-util",
455 | ]
456 |
457 | [[package]]
458 | name = "futures-io"
459 | version = "0.3.28"
460 | source = "registry+https://github.com/rust-lang/crates.io-index"
461 | checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
462 |
463 | [[package]]
464 | name = "futures-macro"
465 | version = "0.3.28"
466 | source = "registry+https://github.com/rust-lang/crates.io-index"
467 | checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
468 | dependencies = [
469 | "proc-macro2",
470 | "quote",
471 | "syn 2.0.16",
472 | ]
473 |
474 | [[package]]
475 | name = "futures-sink"
476 | version = "0.3.28"
477 | source = "registry+https://github.com/rust-lang/crates.io-index"
478 | checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
479 |
480 | [[package]]
481 | name = "futures-task"
482 | version = "0.3.28"
483 | source = "registry+https://github.com/rust-lang/crates.io-index"
484 | checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
485 |
486 | [[package]]
487 | name = "futures-util"
488 | version = "0.3.28"
489 | source = "registry+https://github.com/rust-lang/crates.io-index"
490 | checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
491 | dependencies = [
492 | "futures-core",
493 | "futures-macro",
494 | "futures-sink",
495 | "futures-task",
496 | "pin-project-lite",
497 | "pin-utils",
498 | "slab",
499 | ]
500 |
501 | [[package]]
502 | name = "generic-array"
503 | version = "0.14.7"
504 | source = "registry+https://github.com/rust-lang/crates.io-index"
505 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
506 | dependencies = [
507 | "typenum",
508 | "version_check",
509 | ]
510 |
511 | [[package]]
512 | name = "getrandom"
513 | version = "0.2.9"
514 | source = "registry+https://github.com/rust-lang/crates.io-index"
515 | checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
516 | dependencies = [
517 | "cfg-if",
518 | "js-sys",
519 | "libc",
520 | "wasi 0.11.0+wasi-snapshot-preview1",
521 | "wasm-bindgen",
522 | ]
523 |
524 | [[package]]
525 | name = "gif"
526 | version = "0.12.0"
527 | source = "registry+https://github.com/rust-lang/crates.io-index"
528 | checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045"
529 | dependencies = [
530 | "color_quant",
531 | "weezl",
532 | ]
533 |
534 | [[package]]
535 | name = "gio-sys"
536 | version = "0.17.4"
537 | source = "registry+https://github.com/rust-lang/crates.io-index"
538 | checksum = "6b1d43b0d7968b48455244ecafe41192871257f5740aa6b095eb19db78e362a5"
539 | dependencies = [
540 | "glib-sys",
541 | "gobject-sys",
542 | "libc",
543 | "system-deps",
544 | "winapi",
545 | ]
546 |
547 | [[package]]
548 | name = "glib"
549 | version = "0.17.9"
550 | source = "registry+https://github.com/rust-lang/crates.io-index"
551 | checksum = "a7f1de7cbde31ea4f0a919453a2dcece5d54d5b70e08f8ad254dc4840f5f09b6"
552 | dependencies = [
553 | "bitflags",
554 | "futures-channel",
555 | "futures-core",
556 | "futures-executor",
557 | "futures-task",
558 | "futures-util",
559 | "gio-sys",
560 | "glib-macros",
561 | "glib-sys",
562 | "gobject-sys",
563 | "libc",
564 | "memchr",
565 | "once_cell",
566 | "smallvec",
567 | "thiserror",
568 | ]
569 |
570 | [[package]]
571 | name = "glib-macros"
572 | version = "0.17.9"
573 | source = "registry+https://github.com/rust-lang/crates.io-index"
574 | checksum = "0a7206c5c03851ef126ea1444990e81fdd6765fb799d5bc694e4897ca01bb97f"
575 | dependencies = [
576 | "anyhow",
577 | "heck",
578 | "proc-macro-crate",
579 | "proc-macro-error",
580 | "proc-macro2",
581 | "quote",
582 | "syn 1.0.109",
583 | ]
584 |
585 | [[package]]
586 | name = "glib-sys"
587 | version = "0.17.4"
588 | source = "registry+https://github.com/rust-lang/crates.io-index"
589 | checksum = "49f00ad0a1bf548e61adfff15d83430941d9e1bb620e334f779edd1c745680a5"
590 | dependencies = [
591 | "libc",
592 | "system-deps",
593 | ]
594 |
595 | [[package]]
596 | name = "gobject-sys"
597 | version = "0.17.4"
598 | source = "registry+https://github.com/rust-lang/crates.io-index"
599 | checksum = "15e75b0000a64632b2d8ca3cf856af9308e3a970844f6e9659bd197f026793d0"
600 | dependencies = [
601 | "glib-sys",
602 | "libc",
603 | "system-deps",
604 | ]
605 |
606 | [[package]]
607 | name = "gst-plugin-discordstreamer"
608 | version = "0.0.1"
609 | source = "git+https://github.com/ImTheSquid/gst-discordsender#abf8429b9867bfa8397949b9c373fd16f0f4740b"
610 | dependencies = [
611 | "byteorder",
612 | "discortp",
613 | "gst-plugin-version-helper",
614 | "gstreamer",
615 | "gstreamer-app",
616 | "gstreamer-audio",
617 | "gstreamer-base",
618 | "gstreamer-video",
619 | "once_cell",
620 | "parking_lot",
621 | "rand",
622 | "serde",
623 | "serde_plain",
624 | "xsalsa20poly1305",
625 | ]
626 |
627 | [[package]]
628 | name = "gst-plugin-version-helper"
629 | version = "0.7.5"
630 | source = "registry+https://github.com/rust-lang/crates.io-index"
631 | checksum = "87921209945e5dc809848a100115fad65bd127671896f0206f45e272080cc4c9"
632 | dependencies = [
633 | "chrono",
634 | ]
635 |
636 | [[package]]
637 | name = "gst-plugin-ximageredux"
638 | version = "0.1.7"
639 | source = "registry+https://github.com/rust-lang/crates.io-index"
640 | checksum = "facf6e022c58db3d1bf79d8e7edb9a81805f68f5975e15aeeec4fa120153e95e"
641 | dependencies = [
642 | "anyhow",
643 | "derivative",
644 | "gst-plugin-version-helper",
645 | "gstreamer",
646 | "gstreamer-app",
647 | "gstreamer-base",
648 | "gstreamer-video",
649 | "once_cell",
650 | "xcb",
651 | ]
652 |
653 | [[package]]
654 | name = "gstreamer"
655 | version = "0.20.5"
656 | source = "registry+https://github.com/rust-lang/crates.io-index"
657 | checksum = "4530401c89be6dc10d77ae1587b811cf455c97dce7abf594cb9164527c7da7fc"
658 | dependencies = [
659 | "bitflags",
660 | "cfg-if",
661 | "futures-channel",
662 | "futures-core",
663 | "futures-util",
664 | "glib",
665 | "gstreamer-sys",
666 | "libc",
667 | "muldiv",
668 | "num-integer",
669 | "num-rational",
670 | "once_cell",
671 | "option-operations",
672 | "paste",
673 | "pretty-hex",
674 | "smallvec",
675 | "thiserror",
676 | ]
677 |
678 | [[package]]
679 | name = "gstreamer-app"
680 | version = "0.20.0"
681 | source = "registry+https://github.com/rust-lang/crates.io-index"
682 | checksum = "aa1550d18fe8d97900148cc97d63a3212c3d53169c8469b9bf617de8953c05a8"
683 | dependencies = [
684 | "bitflags",
685 | "futures-core",
686 | "futures-sink",
687 | "glib",
688 | "gstreamer",
689 | "gstreamer-app-sys",
690 | "gstreamer-base",
691 | "libc",
692 | "once_cell",
693 | ]
694 |
695 | [[package]]
696 | name = "gstreamer-app-sys"
697 | version = "0.20.0"
698 | source = "registry+https://github.com/rust-lang/crates.io-index"
699 | checksum = "d7cb5375aa8c23012ec5fadde1a37b972e87680776772669d2628772867056e2"
700 | dependencies = [
701 | "glib-sys",
702 | "gstreamer-base-sys",
703 | "gstreamer-sys",
704 | "libc",
705 | "system-deps",
706 | ]
707 |
708 | [[package]]
709 | name = "gstreamer-audio"
710 | version = "0.20.4"
711 | source = "registry+https://github.com/rust-lang/crates.io-index"
712 | checksum = "06b5a8658e575f6469053026ac663a348d5a562c9fce20ab2ca0c349e05d079e"
713 | dependencies = [
714 | "bitflags",
715 | "cfg-if",
716 | "glib",
717 | "gstreamer",
718 | "gstreamer-audio-sys",
719 | "gstreamer-base",
720 | "libc",
721 | "once_cell",
722 | ]
723 |
724 | [[package]]
725 | name = "gstreamer-audio-sys"
726 | version = "0.20.0"
727 | source = "registry+https://github.com/rust-lang/crates.io-index"
728 | checksum = "9d4001b779e4707b32acd6ec0960e327b926369c1a34f7c41d477ac42b2670e8"
729 | dependencies = [
730 | "glib-sys",
731 | "gobject-sys",
732 | "gstreamer-base-sys",
733 | "gstreamer-sys",
734 | "libc",
735 | "system-deps",
736 | ]
737 |
738 | [[package]]
739 | name = "gstreamer-base"
740 | version = "0.20.5"
741 | source = "registry+https://github.com/rust-lang/crates.io-index"
742 | checksum = "0b8ff5dfbf7bcaf1466a385b836bad0d8da25759f121458727fdda1f771c69b3"
743 | dependencies = [
744 | "atomic_refcell",
745 | "bitflags",
746 | "cfg-if",
747 | "glib",
748 | "gstreamer",
749 | "gstreamer-base-sys",
750 | "libc",
751 | ]
752 |
753 | [[package]]
754 | name = "gstreamer-base-sys"
755 | version = "0.20.0"
756 | source = "registry+https://github.com/rust-lang/crates.io-index"
757 | checksum = "26114ed96f6668380f5a1554128159e98e06c3a7a8460f216d7cd6dce28f928c"
758 | dependencies = [
759 | "glib-sys",
760 | "gobject-sys",
761 | "gstreamer-sys",
762 | "libc",
763 | "system-deps",
764 | ]
765 |
766 | [[package]]
767 | name = "gstreamer-sys"
768 | version = "0.20.0"
769 | source = "registry+https://github.com/rust-lang/crates.io-index"
770 | checksum = "e56fe047adef7d47dbafa8bc1340fddb53c325e16574763063702fc94b5786d2"
771 | dependencies = [
772 | "glib-sys",
773 | "gobject-sys",
774 | "libc",
775 | "system-deps",
776 | ]
777 |
778 | [[package]]
779 | name = "gstreamer-video"
780 | version = "0.20.4"
781 | source = "registry+https://github.com/rust-lang/crates.io-index"
782 | checksum = "dce97769effde2d779dc4f7037b37106457b74e53f2a711bddc90b30ffeb7e06"
783 | dependencies = [
784 | "bitflags",
785 | "cfg-if",
786 | "futures-channel",
787 | "glib",
788 | "gstreamer",
789 | "gstreamer-base",
790 | "gstreamer-video-sys",
791 | "libc",
792 | "once_cell",
793 | ]
794 |
795 | [[package]]
796 | name = "gstreamer-video-sys"
797 | version = "0.20.0"
798 | source = "registry+https://github.com/rust-lang/crates.io-index"
799 | checksum = "66ddb6112d438aac0004d2db6053a572f92b1c5e0e9d6ff6c71d9245f7f73e46"
800 | dependencies = [
801 | "glib-sys",
802 | "gobject-sys",
803 | "gstreamer-base-sys",
804 | "gstreamer-sys",
805 | "libc",
806 | "system-deps",
807 | ]
808 |
809 | [[package]]
810 | name = "half"
811 | version = "2.2.1"
812 | source = "registry+https://github.com/rust-lang/crates.io-index"
813 | checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0"
814 | dependencies = [
815 | "crunchy",
816 | ]
817 |
818 | [[package]]
819 | name = "hashbrown"
820 | version = "0.12.3"
821 | source = "registry+https://github.com/rust-lang/crates.io-index"
822 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
823 |
824 | [[package]]
825 | name = "heck"
826 | version = "0.4.1"
827 | source = "registry+https://github.com/rust-lang/crates.io-index"
828 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
829 |
830 | [[package]]
831 | name = "hermit-abi"
832 | version = "0.2.6"
833 | source = "registry+https://github.com/rust-lang/crates.io-index"
834 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
835 | dependencies = [
836 | "libc",
837 | ]
838 |
839 | [[package]]
840 | name = "hermit-abi"
841 | version = "0.3.1"
842 | source = "registry+https://github.com/rust-lang/crates.io-index"
843 | checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
844 |
845 | [[package]]
846 | name = "http"
847 | version = "0.2.9"
848 | source = "registry+https://github.com/rust-lang/crates.io-index"
849 | checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
850 | dependencies = [
851 | "bytes",
852 | "fnv",
853 | "itoa",
854 | ]
855 |
856 | [[package]]
857 | name = "httparse"
858 | version = "1.8.0"
859 | source = "registry+https://github.com/rust-lang/crates.io-index"
860 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
861 |
862 | [[package]]
863 | name = "iana-time-zone"
864 | version = "0.1.56"
865 | source = "registry+https://github.com/rust-lang/crates.io-index"
866 | checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c"
867 | dependencies = [
868 | "android_system_properties",
869 | "core-foundation-sys",
870 | "iana-time-zone-haiku",
871 | "js-sys",
872 | "wasm-bindgen",
873 | "windows",
874 | ]
875 |
876 | [[package]]
877 | name = "iana-time-zone-haiku"
878 | version = "0.1.2"
879 | source = "registry+https://github.com/rust-lang/crates.io-index"
880 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
881 | dependencies = [
882 | "cc",
883 | ]
884 |
885 | [[package]]
886 | name = "idna"
887 | version = "0.3.0"
888 | source = "registry+https://github.com/rust-lang/crates.io-index"
889 | checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
890 | dependencies = [
891 | "unicode-bidi",
892 | "unicode-normalization",
893 | ]
894 |
895 | [[package]]
896 | name = "image"
897 | version = "0.24.6"
898 | source = "registry+https://github.com/rust-lang/crates.io-index"
899 | checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a"
900 | dependencies = [
901 | "bytemuck",
902 | "byteorder",
903 | "color_quant",
904 | "exr",
905 | "gif",
906 | "jpeg-decoder",
907 | "num-rational",
908 | "num-traits",
909 | "png",
910 | "qoi",
911 | "tiff",
912 | ]
913 |
914 | [[package]]
915 | name = "indexmap"
916 | version = "1.9.3"
917 | source = "registry+https://github.com/rust-lang/crates.io-index"
918 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
919 | dependencies = [
920 | "autocfg",
921 | "hashbrown",
922 | ]
923 |
924 | [[package]]
925 | name = "inout"
926 | version = "0.1.3"
927 | source = "registry+https://github.com/rust-lang/crates.io-index"
928 | checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
929 | dependencies = [
930 | "generic-array",
931 | ]
932 |
933 | [[package]]
934 | name = "instant"
935 | version = "0.1.12"
936 | source = "registry+https://github.com/rust-lang/crates.io-index"
937 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
938 | dependencies = [
939 | "cfg-if",
940 | ]
941 |
942 | [[package]]
943 | name = "io-lifetimes"
944 | version = "1.0.10"
945 | source = "registry+https://github.com/rust-lang/crates.io-index"
946 | checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
947 | dependencies = [
948 | "hermit-abi 0.3.1",
949 | "libc",
950 | "windows-sys 0.48.0",
951 | ]
952 |
953 | [[package]]
954 | name = "itoa"
955 | version = "1.0.6"
956 | source = "registry+https://github.com/rust-lang/crates.io-index"
957 | checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
958 |
959 | [[package]]
960 | name = "jpeg-decoder"
961 | version = "0.3.0"
962 | source = "registry+https://github.com/rust-lang/crates.io-index"
963 | checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
964 | dependencies = [
965 | "rayon",
966 | ]
967 |
968 | [[package]]
969 | name = "js-sys"
970 | version = "0.3.63"
971 | source = "registry+https://github.com/rust-lang/crates.io-index"
972 | checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790"
973 | dependencies = [
974 | "wasm-bindgen",
975 | ]
976 |
977 | [[package]]
978 | name = "lazy_static"
979 | version = "1.4.0"
980 | source = "registry+https://github.com/rust-lang/crates.io-index"
981 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
982 |
983 | [[package]]
984 | name = "lebe"
985 | version = "0.5.2"
986 | source = "registry+https://github.com/rust-lang/crates.io-index"
987 | checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
988 |
989 | [[package]]
990 | name = "libc"
991 | version = "0.2.144"
992 | source = "registry+https://github.com/rust-lang/crates.io-index"
993 | checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
994 |
995 | [[package]]
996 | name = "libpulse-binding"
997 | version = "2.27.1"
998 | source = "registry+https://github.com/rust-lang/crates.io-index"
999 | checksum = "1745b20bfc194ac12ef828f144f0ec2d4a7fe993281fa3567a0bd4969aee6890"
1000 | dependencies = [
1001 | "bitflags",
1002 | "libc",
1003 | "libpulse-sys",
1004 | "num-derive",
1005 | "num-traits",
1006 | "winapi",
1007 | ]
1008 |
1009 | [[package]]
1010 | name = "libpulse-sys"
1011 | version = "1.20.1"
1012 | source = "registry+https://github.com/rust-lang/crates.io-index"
1013 | checksum = "2191e6880818d1df4cf72eac8e91dce7a5a52ba0da4b2a5cdafabc22b937eadb"
1014 | dependencies = [
1015 | "libc",
1016 | "num-derive",
1017 | "num-traits",
1018 | "pkg-config",
1019 | "winapi",
1020 | ]
1021 |
1022 | [[package]]
1023 | name = "linux-raw-sys"
1024 | version = "0.3.8"
1025 | source = "registry+https://github.com/rust-lang/crates.io-index"
1026 | checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
1027 |
1028 | [[package]]
1029 | name = "lock_api"
1030 | version = "0.4.9"
1031 | source = "registry+https://github.com/rust-lang/crates.io-index"
1032 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
1033 | dependencies = [
1034 | "autocfg",
1035 | "scopeguard",
1036 | ]
1037 |
1038 | [[package]]
1039 | name = "log"
1040 | version = "0.4.17"
1041 | source = "registry+https://github.com/rust-lang/crates.io-index"
1042 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
1043 | dependencies = [
1044 | "cfg-if",
1045 | ]
1046 |
1047 | [[package]]
1048 | name = "memchr"
1049 | version = "2.5.0"
1050 | source = "registry+https://github.com/rust-lang/crates.io-index"
1051 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
1052 |
1053 | [[package]]
1054 | name = "memoffset"
1055 | version = "0.8.0"
1056 | source = "registry+https://github.com/rust-lang/crates.io-index"
1057 | checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
1058 | dependencies = [
1059 | "autocfg",
1060 | ]
1061 |
1062 | [[package]]
1063 | name = "miniz_oxide"
1064 | version = "0.6.2"
1065 | source = "registry+https://github.com/rust-lang/crates.io-index"
1066 | checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
1067 | dependencies = [
1068 | "adler",
1069 | ]
1070 |
1071 | [[package]]
1072 | name = "miniz_oxide"
1073 | version = "0.7.1"
1074 | source = "registry+https://github.com/rust-lang/crates.io-index"
1075 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
1076 | dependencies = [
1077 | "adler",
1078 | "simd-adler32",
1079 | ]
1080 |
1081 | [[package]]
1082 | name = "mio"
1083 | version = "0.8.6"
1084 | source = "registry+https://github.com/rust-lang/crates.io-index"
1085 | checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
1086 | dependencies = [
1087 | "libc",
1088 | "log",
1089 | "wasi 0.11.0+wasi-snapshot-preview1",
1090 | "windows-sys 0.45.0",
1091 | ]
1092 |
1093 | [[package]]
1094 | name = "muldiv"
1095 | version = "1.0.1"
1096 | source = "registry+https://github.com/rust-lang/crates.io-index"
1097 | checksum = "956787520e75e9bd233246045d19f42fb73242759cc57fba9611d940ae96d4b0"
1098 |
1099 | [[package]]
1100 | name = "nanorand"
1101 | version = "0.7.0"
1102 | source = "registry+https://github.com/rust-lang/crates.io-index"
1103 | checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
1104 | dependencies = [
1105 | "getrandom",
1106 | ]
1107 |
1108 | [[package]]
1109 | name = "native-tls"
1110 | version = "0.2.11"
1111 | source = "registry+https://github.com/rust-lang/crates.io-index"
1112 | checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
1113 | dependencies = [
1114 | "lazy_static",
1115 | "libc",
1116 | "log",
1117 | "openssl",
1118 | "openssl-probe",
1119 | "openssl-sys",
1120 | "schannel",
1121 | "security-framework",
1122 | "security-framework-sys",
1123 | "tempfile",
1124 | ]
1125 |
1126 | [[package]]
1127 | name = "nix"
1128 | version = "0.26.2"
1129 | source = "registry+https://github.com/rust-lang/crates.io-index"
1130 | checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
1131 | dependencies = [
1132 | "bitflags",
1133 | "cfg-if",
1134 | "libc",
1135 | "static_assertions",
1136 | ]
1137 |
1138 | [[package]]
1139 | name = "no-std-net"
1140 | version = "0.6.0"
1141 | source = "registry+https://github.com/rust-lang/crates.io-index"
1142 | checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65"
1143 |
1144 | [[package]]
1145 | name = "ntapi"
1146 | version = "0.4.1"
1147 | source = "registry+https://github.com/rust-lang/crates.io-index"
1148 | checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
1149 | dependencies = [
1150 | "winapi",
1151 | ]
1152 |
1153 | [[package]]
1154 | name = "nu-ansi-term"
1155 | version = "0.46.0"
1156 | source = "registry+https://github.com/rust-lang/crates.io-index"
1157 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
1158 | dependencies = [
1159 | "overload",
1160 | "winapi",
1161 | ]
1162 |
1163 | [[package]]
1164 | name = "num-derive"
1165 | version = "0.3.3"
1166 | source = "registry+https://github.com/rust-lang/crates.io-index"
1167 | checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
1168 | dependencies = [
1169 | "proc-macro2",
1170 | "quote",
1171 | "syn 1.0.109",
1172 | ]
1173 |
1174 | [[package]]
1175 | name = "num-integer"
1176 | version = "0.1.45"
1177 | source = "registry+https://github.com/rust-lang/crates.io-index"
1178 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
1179 | dependencies = [
1180 | "autocfg",
1181 | "num-traits",
1182 | ]
1183 |
1184 | [[package]]
1185 | name = "num-rational"
1186 | version = "0.4.1"
1187 | source = "registry+https://github.com/rust-lang/crates.io-index"
1188 | checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
1189 | dependencies = [
1190 | "autocfg",
1191 | "num-integer",
1192 | "num-traits",
1193 | ]
1194 |
1195 | [[package]]
1196 | name = "num-traits"
1197 | version = "0.2.15"
1198 | source = "registry+https://github.com/rust-lang/crates.io-index"
1199 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
1200 | dependencies = [
1201 | "autocfg",
1202 | ]
1203 |
1204 | [[package]]
1205 | name = "num_cpus"
1206 | version = "1.15.0"
1207 | source = "registry+https://github.com/rust-lang/crates.io-index"
1208 | checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
1209 | dependencies = [
1210 | "hermit-abi 0.2.6",
1211 | "libc",
1212 | ]
1213 |
1214 | [[package]]
1215 | name = "once_cell"
1216 | version = "1.17.1"
1217 | source = "registry+https://github.com/rust-lang/crates.io-index"
1218 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
1219 |
1220 | [[package]]
1221 | name = "opaque-debug"
1222 | version = "0.3.0"
1223 | source = "registry+https://github.com/rust-lang/crates.io-index"
1224 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
1225 |
1226 | [[package]]
1227 | name = "openssl"
1228 | version = "0.10.52"
1229 | source = "registry+https://github.com/rust-lang/crates.io-index"
1230 | checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56"
1231 | dependencies = [
1232 | "bitflags",
1233 | "cfg-if",
1234 | "foreign-types",
1235 | "libc",
1236 | "once_cell",
1237 | "openssl-macros",
1238 | "openssl-sys",
1239 | ]
1240 |
1241 | [[package]]
1242 | name = "openssl-macros"
1243 | version = "0.1.1"
1244 | source = "registry+https://github.com/rust-lang/crates.io-index"
1245 | checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
1246 | dependencies = [
1247 | "proc-macro2",
1248 | "quote",
1249 | "syn 2.0.16",
1250 | ]
1251 |
1252 | [[package]]
1253 | name = "openssl-probe"
1254 | version = "0.1.5"
1255 | source = "registry+https://github.com/rust-lang/crates.io-index"
1256 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
1257 |
1258 | [[package]]
1259 | name = "openssl-sys"
1260 | version = "0.9.87"
1261 | source = "registry+https://github.com/rust-lang/crates.io-index"
1262 | checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e"
1263 | dependencies = [
1264 | "cc",
1265 | "libc",
1266 | "pkg-config",
1267 | "vcpkg",
1268 | ]
1269 |
1270 | [[package]]
1271 | name = "option-operations"
1272 | version = "0.5.0"
1273 | source = "registry+https://github.com/rust-lang/crates.io-index"
1274 | checksum = "7c26d27bb1aeab65138e4bf7666045169d1717febcc9ff870166be8348b223d0"
1275 | dependencies = [
1276 | "paste",
1277 | ]
1278 |
1279 | [[package]]
1280 | name = "overload"
1281 | version = "0.1.1"
1282 | source = "registry+https://github.com/rust-lang/crates.io-index"
1283 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
1284 |
1285 | [[package]]
1286 | name = "parking_lot"
1287 | version = "0.12.1"
1288 | source = "registry+https://github.com/rust-lang/crates.io-index"
1289 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
1290 | dependencies = [
1291 | "lock_api",
1292 | "parking_lot_core",
1293 | ]
1294 |
1295 | [[package]]
1296 | name = "parking_lot_core"
1297 | version = "0.9.7"
1298 | source = "registry+https://github.com/rust-lang/crates.io-index"
1299 | checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
1300 | dependencies = [
1301 | "cfg-if",
1302 | "libc",
1303 | "redox_syscall 0.2.16",
1304 | "smallvec",
1305 | "windows-sys 0.45.0",
1306 | ]
1307 |
1308 | [[package]]
1309 | name = "paste"
1310 | version = "1.0.12"
1311 | source = "registry+https://github.com/rust-lang/crates.io-index"
1312 | checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
1313 |
1314 | [[package]]
1315 | name = "percent-encoding"
1316 | version = "2.2.0"
1317 | source = "registry+https://github.com/rust-lang/crates.io-index"
1318 | checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
1319 |
1320 | [[package]]
1321 | name = "pin-project"
1322 | version = "1.1.0"
1323 | source = "registry+https://github.com/rust-lang/crates.io-index"
1324 | checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead"
1325 | dependencies = [
1326 | "pin-project-internal",
1327 | ]
1328 |
1329 | [[package]]
1330 | name = "pin-project-internal"
1331 | version = "1.1.0"
1332 | source = "registry+https://github.com/rust-lang/crates.io-index"
1333 | checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07"
1334 | dependencies = [
1335 | "proc-macro2",
1336 | "quote",
1337 | "syn 2.0.16",
1338 | ]
1339 |
1340 | [[package]]
1341 | name = "pin-project-lite"
1342 | version = "0.2.9"
1343 | source = "registry+https://github.com/rust-lang/crates.io-index"
1344 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
1345 |
1346 | [[package]]
1347 | name = "pin-utils"
1348 | version = "0.1.0"
1349 | source = "registry+https://github.com/rust-lang/crates.io-index"
1350 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
1351 |
1352 | [[package]]
1353 | name = "pkg-config"
1354 | version = "0.3.27"
1355 | source = "registry+https://github.com/rust-lang/crates.io-index"
1356 | checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
1357 |
1358 | [[package]]
1359 | name = "pnet_base"
1360 | version = "0.31.0"
1361 | source = "registry+https://github.com/rust-lang/crates.io-index"
1362 | checksum = "f9d3a993d49e5fd5d4d854d6999d4addca1f72d86c65adf224a36757161c02b6"
1363 | dependencies = [
1364 | "no-std-net",
1365 | ]
1366 |
1367 | [[package]]
1368 | name = "pnet_macros"
1369 | version = "0.31.0"
1370 | source = "registry+https://github.com/rust-lang/crates.io-index"
1371 | checksum = "48dd52a5211fac27e7acb14cfc9f30ae16ae0e956b7b779c8214c74559cef4c3"
1372 | dependencies = [
1373 | "proc-macro2",
1374 | "quote",
1375 | "regex",
1376 | "syn 1.0.109",
1377 | ]
1378 |
1379 | [[package]]
1380 | name = "pnet_macros_support"
1381 | version = "0.31.0"
1382 | source = "registry+https://github.com/rust-lang/crates.io-index"
1383 | checksum = "89de095dc7739349559913aed1ef6a11e73ceade4897dadc77c5e09de6740750"
1384 | dependencies = [
1385 | "pnet_base",
1386 | ]
1387 |
1388 | [[package]]
1389 | name = "png"
1390 | version = "0.17.8"
1391 | source = "registry+https://github.com/rust-lang/crates.io-index"
1392 | checksum = "aaeebc51f9e7d2c150d3f3bfeb667f2aa985db5ef1e3d212847bdedb488beeaa"
1393 | dependencies = [
1394 | "bitflags",
1395 | "crc32fast",
1396 | "fdeflate",
1397 | "flate2",
1398 | "miniz_oxide 0.7.1",
1399 | ]
1400 |
1401 | [[package]]
1402 | name = "poly1305"
1403 | version = "0.8.0"
1404 | source = "registry+https://github.com/rust-lang/crates.io-index"
1405 | checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
1406 | dependencies = [
1407 | "cpufeatures",
1408 | "opaque-debug",
1409 | "universal-hash",
1410 | ]
1411 |
1412 | [[package]]
1413 | name = "ppv-lite86"
1414 | version = "0.2.17"
1415 | source = "registry+https://github.com/rust-lang/crates.io-index"
1416 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
1417 |
1418 | [[package]]
1419 | name = "pretty-hex"
1420 | version = "0.3.0"
1421 | source = "registry+https://github.com/rust-lang/crates.io-index"
1422 | checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5"
1423 |
1424 | [[package]]
1425 | name = "proc-macro-crate"
1426 | version = "1.3.1"
1427 | source = "registry+https://github.com/rust-lang/crates.io-index"
1428 | checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
1429 | dependencies = [
1430 | "once_cell",
1431 | "toml_edit",
1432 | ]
1433 |
1434 | [[package]]
1435 | name = "proc-macro-error"
1436 | version = "1.0.4"
1437 | source = "registry+https://github.com/rust-lang/crates.io-index"
1438 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
1439 | dependencies = [
1440 | "proc-macro-error-attr",
1441 | "proc-macro2",
1442 | "quote",
1443 | "syn 1.0.109",
1444 | "version_check",
1445 | ]
1446 |
1447 | [[package]]
1448 | name = "proc-macro-error-attr"
1449 | version = "1.0.4"
1450 | source = "registry+https://github.com/rust-lang/crates.io-index"
1451 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
1452 | dependencies = [
1453 | "proc-macro2",
1454 | "quote",
1455 | "version_check",
1456 | ]
1457 |
1458 | [[package]]
1459 | name = "proc-macro2"
1460 | version = "1.0.58"
1461 | source = "registry+https://github.com/rust-lang/crates.io-index"
1462 | checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8"
1463 | dependencies = [
1464 | "unicode-ident",
1465 | ]
1466 |
1467 | [[package]]
1468 | name = "qoi"
1469 | version = "0.4.1"
1470 | source = "registry+https://github.com/rust-lang/crates.io-index"
1471 | checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
1472 | dependencies = [
1473 | "bytemuck",
1474 | ]
1475 |
1476 | [[package]]
1477 | name = "quick-xml"
1478 | version = "0.28.2"
1479 | source = "registry+https://github.com/rust-lang/crates.io-index"
1480 | checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1"
1481 | dependencies = [
1482 | "memchr",
1483 | ]
1484 |
1485 | [[package]]
1486 | name = "quote"
1487 | version = "1.0.27"
1488 | source = "registry+https://github.com/rust-lang/crates.io-index"
1489 | checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
1490 | dependencies = [
1491 | "proc-macro2",
1492 | ]
1493 |
1494 | [[package]]
1495 | name = "rand"
1496 | version = "0.8.5"
1497 | source = "registry+https://github.com/rust-lang/crates.io-index"
1498 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
1499 | dependencies = [
1500 | "libc",
1501 | "rand_chacha",
1502 | "rand_core",
1503 | ]
1504 |
1505 | [[package]]
1506 | name = "rand_chacha"
1507 | version = "0.3.1"
1508 | source = "registry+https://github.com/rust-lang/crates.io-index"
1509 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
1510 | dependencies = [
1511 | "ppv-lite86",
1512 | "rand_core",
1513 | ]
1514 |
1515 | [[package]]
1516 | name = "rand_core"
1517 | version = "0.6.4"
1518 | source = "registry+https://github.com/rust-lang/crates.io-index"
1519 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
1520 | dependencies = [
1521 | "getrandom",
1522 | ]
1523 |
1524 | [[package]]
1525 | name = "rayon"
1526 | version = "1.7.0"
1527 | source = "registry+https://github.com/rust-lang/crates.io-index"
1528 | checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
1529 | dependencies = [
1530 | "either",
1531 | "rayon-core",
1532 | ]
1533 |
1534 | [[package]]
1535 | name = "rayon-core"
1536 | version = "1.11.0"
1537 | source = "registry+https://github.com/rust-lang/crates.io-index"
1538 | checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
1539 | dependencies = [
1540 | "crossbeam-channel",
1541 | "crossbeam-deque",
1542 | "crossbeam-utils",
1543 | "num_cpus",
1544 | ]
1545 |
1546 | [[package]]
1547 | name = "redox_syscall"
1548 | version = "0.2.16"
1549 | source = "registry+https://github.com/rust-lang/crates.io-index"
1550 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
1551 | dependencies = [
1552 | "bitflags",
1553 | ]
1554 |
1555 | [[package]]
1556 | name = "redox_syscall"
1557 | version = "0.3.5"
1558 | source = "registry+https://github.com/rust-lang/crates.io-index"
1559 | checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
1560 | dependencies = [
1561 | "bitflags",
1562 | ]
1563 |
1564 | [[package]]
1565 | name = "regex"
1566 | version = "1.8.2"
1567 | source = "registry+https://github.com/rust-lang/crates.io-index"
1568 | checksum = "d1a59b5d8e97dee33696bf13c5ba8ab85341c002922fba050069326b9c498974"
1569 | dependencies = [
1570 | "aho-corasick",
1571 | "memchr",
1572 | "regex-syntax",
1573 | ]
1574 |
1575 | [[package]]
1576 | name = "regex-syntax"
1577 | version = "0.7.2"
1578 | source = "registry+https://github.com/rust-lang/crates.io-index"
1579 | checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
1580 |
1581 | [[package]]
1582 | name = "rustix"
1583 | version = "0.37.19"
1584 | source = "registry+https://github.com/rust-lang/crates.io-index"
1585 | checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
1586 | dependencies = [
1587 | "bitflags",
1588 | "errno",
1589 | "io-lifetimes",
1590 | "libc",
1591 | "linux-raw-sys",
1592 | "windows-sys 0.48.0",
1593 | ]
1594 |
1595 | [[package]]
1596 | name = "ryu"
1597 | version = "1.0.13"
1598 | source = "registry+https://github.com/rust-lang/crates.io-index"
1599 | checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
1600 |
1601 | [[package]]
1602 | name = "salsa20"
1603 | version = "0.10.2"
1604 | source = "registry+https://github.com/rust-lang/crates.io-index"
1605 | checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213"
1606 | dependencies = [
1607 | "cipher",
1608 | ]
1609 |
1610 | [[package]]
1611 | name = "schannel"
1612 | version = "0.1.21"
1613 | source = "registry+https://github.com/rust-lang/crates.io-index"
1614 | checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3"
1615 | dependencies = [
1616 | "windows-sys 0.42.0",
1617 | ]
1618 |
1619 | [[package]]
1620 | name = "scopeguard"
1621 | version = "1.1.0"
1622 | source = "registry+https://github.com/rust-lang/crates.io-index"
1623 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
1624 |
1625 | [[package]]
1626 | name = "security-framework"
1627 | version = "2.9.1"
1628 | source = "registry+https://github.com/rust-lang/crates.io-index"
1629 | checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8"
1630 | dependencies = [
1631 | "bitflags",
1632 | "core-foundation",
1633 | "core-foundation-sys",
1634 | "libc",
1635 | "security-framework-sys",
1636 | ]
1637 |
1638 | [[package]]
1639 | name = "security-framework-sys"
1640 | version = "2.9.0"
1641 | source = "registry+https://github.com/rust-lang/crates.io-index"
1642 | checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7"
1643 | dependencies = [
1644 | "core-foundation-sys",
1645 | "libc",
1646 | ]
1647 |
1648 | [[package]]
1649 | name = "serde"
1650 | version = "1.0.163"
1651 | source = "registry+https://github.com/rust-lang/crates.io-index"
1652 | checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
1653 | dependencies = [
1654 | "serde_derive",
1655 | ]
1656 |
1657 | [[package]]
1658 | name = "serde_derive"
1659 | version = "1.0.163"
1660 | source = "registry+https://github.com/rust-lang/crates.io-index"
1661 | checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
1662 | dependencies = [
1663 | "proc-macro2",
1664 | "quote",
1665 | "syn 2.0.16",
1666 | ]
1667 |
1668 | [[package]]
1669 | name = "serde_json"
1670 | version = "1.0.96"
1671 | source = "registry+https://github.com/rust-lang/crates.io-index"
1672 | checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
1673 | dependencies = [
1674 | "itoa",
1675 | "ryu",
1676 | "serde",
1677 | ]
1678 |
1679 | [[package]]
1680 | name = "serde_plain"
1681 | version = "1.0.1"
1682 | source = "registry+https://github.com/rust-lang/crates.io-index"
1683 | checksum = "d6018081315db179d0ce57b1fe4b62a12a0028c9cf9bbef868c9cf477b3c34ae"
1684 | dependencies = [
1685 | "serde",
1686 | ]
1687 |
1688 | [[package]]
1689 | name = "serde_spanned"
1690 | version = "0.6.2"
1691 | source = "registry+https://github.com/rust-lang/crates.io-index"
1692 | checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d"
1693 | dependencies = [
1694 | "serde",
1695 | ]
1696 |
1697 | [[package]]
1698 | name = "sha1"
1699 | version = "0.10.5"
1700 | source = "registry+https://github.com/rust-lang/crates.io-index"
1701 | checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
1702 | dependencies = [
1703 | "cfg-if",
1704 | "cpufeatures",
1705 | "digest",
1706 | ]
1707 |
1708 | [[package]]
1709 | name = "sharded-slab"
1710 | version = "0.1.4"
1711 | source = "registry+https://github.com/rust-lang/crates.io-index"
1712 | checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
1713 | dependencies = [
1714 | "lazy_static",
1715 | ]
1716 |
1717 | [[package]]
1718 | name = "signal-hook-registry"
1719 | version = "1.4.1"
1720 | source = "registry+https://github.com/rust-lang/crates.io-index"
1721 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
1722 | dependencies = [
1723 | "libc",
1724 | ]
1725 |
1726 | [[package]]
1727 | name = "simd-adler32"
1728 | version = "0.3.5"
1729 | source = "registry+https://github.com/rust-lang/crates.io-index"
1730 | checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f"
1731 |
1732 | [[package]]
1733 | name = "slab"
1734 | version = "0.4.8"
1735 | source = "registry+https://github.com/rust-lang/crates.io-index"
1736 | checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
1737 | dependencies = [
1738 | "autocfg",
1739 | ]
1740 |
1741 | [[package]]
1742 | name = "smallvec"
1743 | version = "1.10.0"
1744 | source = "registry+https://github.com/rust-lang/crates.io-index"
1745 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
1746 |
1747 | [[package]]
1748 | name = "socket2"
1749 | version = "0.4.9"
1750 | source = "registry+https://github.com/rust-lang/crates.io-index"
1751 | checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
1752 | dependencies = [
1753 | "libc",
1754 | "winapi",
1755 | ]
1756 |
1757 | [[package]]
1758 | name = "spin"
1759 | version = "0.9.8"
1760 | source = "registry+https://github.com/rust-lang/crates.io-index"
1761 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
1762 | dependencies = [
1763 | "lock_api",
1764 | ]
1765 |
1766 | [[package]]
1767 | name = "static_assertions"
1768 | version = "1.1.0"
1769 | source = "registry+https://github.com/rust-lang/crates.io-index"
1770 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
1771 |
1772 | [[package]]
1773 | name = "subtle"
1774 | version = "2.5.0"
1775 | source = "registry+https://github.com/rust-lang/crates.io-index"
1776 | checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
1777 |
1778 | [[package]]
1779 | name = "syn"
1780 | version = "1.0.109"
1781 | source = "registry+https://github.com/rust-lang/crates.io-index"
1782 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
1783 | dependencies = [
1784 | "proc-macro2",
1785 | "quote",
1786 | "unicode-ident",
1787 | ]
1788 |
1789 | [[package]]
1790 | name = "syn"
1791 | version = "2.0.16"
1792 | source = "registry+https://github.com/rust-lang/crates.io-index"
1793 | checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01"
1794 | dependencies = [
1795 | "proc-macro2",
1796 | "quote",
1797 | "unicode-ident",
1798 | ]
1799 |
1800 | [[package]]
1801 | name = "sysinfo"
1802 | version = "0.26.9"
1803 | source = "registry+https://github.com/rust-lang/crates.io-index"
1804 | checksum = "5c18a6156d1f27a9592ee18c1a846ca8dd5c258b7179fc193ae87c74ebb666f5"
1805 | dependencies = [
1806 | "cfg-if",
1807 | "core-foundation-sys",
1808 | "libc",
1809 | "ntapi",
1810 | "once_cell",
1811 | "rayon",
1812 | "winapi",
1813 | ]
1814 |
1815 | [[package]]
1816 | name = "system-deps"
1817 | version = "6.1.0"
1818 | source = "registry+https://github.com/rust-lang/crates.io-index"
1819 | checksum = "e5fa6fb9ee296c0dc2df41a656ca7948546d061958115ddb0bcaae43ad0d17d2"
1820 | dependencies = [
1821 | "cfg-expr",
1822 | "heck",
1823 | "pkg-config",
1824 | "toml",
1825 | "version-compare",
1826 | ]
1827 |
1828 | [[package]]
1829 | name = "target-lexicon"
1830 | version = "0.12.7"
1831 | source = "registry+https://github.com/rust-lang/crates.io-index"
1832 | checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5"
1833 |
1834 | [[package]]
1835 | name = "tempfile"
1836 | version = "3.5.0"
1837 | source = "registry+https://github.com/rust-lang/crates.io-index"
1838 | checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"
1839 | dependencies = [
1840 | "cfg-if",
1841 | "fastrand",
1842 | "redox_syscall 0.3.5",
1843 | "rustix",
1844 | "windows-sys 0.45.0",
1845 | ]
1846 |
1847 | [[package]]
1848 | name = "thiserror"
1849 | version = "1.0.40"
1850 | source = "registry+https://github.com/rust-lang/crates.io-index"
1851 | checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
1852 | dependencies = [
1853 | "thiserror-impl",
1854 | ]
1855 |
1856 | [[package]]
1857 | name = "thiserror-impl"
1858 | version = "1.0.40"
1859 | source = "registry+https://github.com/rust-lang/crates.io-index"
1860 | checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
1861 | dependencies = [
1862 | "proc-macro2",
1863 | "quote",
1864 | "syn 2.0.16",
1865 | ]
1866 |
1867 | [[package]]
1868 | name = "thread_local"
1869 | version = "1.1.7"
1870 | source = "registry+https://github.com/rust-lang/crates.io-index"
1871 | checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
1872 | dependencies = [
1873 | "cfg-if",
1874 | "once_cell",
1875 | ]
1876 |
1877 | [[package]]
1878 | name = "tiff"
1879 | version = "0.8.1"
1880 | source = "registry+https://github.com/rust-lang/crates.io-index"
1881 | checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471"
1882 | dependencies = [
1883 | "flate2",
1884 | "jpeg-decoder",
1885 | "weezl",
1886 | ]
1887 |
1888 | [[package]]
1889 | name = "time"
1890 | version = "0.1.45"
1891 | source = "registry+https://github.com/rust-lang/crates.io-index"
1892 | checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
1893 | dependencies = [
1894 | "libc",
1895 | "wasi 0.10.0+wasi-snapshot-preview1",
1896 | "winapi",
1897 | ]
1898 |
1899 | [[package]]
1900 | name = "time"
1901 | version = "0.3.21"
1902 | source = "registry+https://github.com/rust-lang/crates.io-index"
1903 | checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc"
1904 | dependencies = [
1905 | "itoa",
1906 | "serde",
1907 | "time-core",
1908 | "time-macros",
1909 | ]
1910 |
1911 | [[package]]
1912 | name = "time-core"
1913 | version = "0.1.1"
1914 | source = "registry+https://github.com/rust-lang/crates.io-index"
1915 | checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
1916 |
1917 | [[package]]
1918 | name = "time-macros"
1919 | version = "0.2.9"
1920 | source = "registry+https://github.com/rust-lang/crates.io-index"
1921 | checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b"
1922 | dependencies = [
1923 | "time-core",
1924 | ]
1925 |
1926 | [[package]]
1927 | name = "tinyvec"
1928 | version = "1.6.0"
1929 | source = "registry+https://github.com/rust-lang/crates.io-index"
1930 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
1931 | dependencies = [
1932 | "tinyvec_macros",
1933 | ]
1934 |
1935 | [[package]]
1936 | name = "tinyvec_macros"
1937 | version = "0.1.1"
1938 | source = "registry+https://github.com/rust-lang/crates.io-index"
1939 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
1940 |
1941 | [[package]]
1942 | name = "tokio"
1943 | version = "1.28.1"
1944 | source = "registry+https://github.com/rust-lang/crates.io-index"
1945 | checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105"
1946 | dependencies = [
1947 | "autocfg",
1948 | "bytes",
1949 | "libc",
1950 | "mio",
1951 | "num_cpus",
1952 | "parking_lot",
1953 | "pin-project-lite",
1954 | "signal-hook-registry",
1955 | "socket2",
1956 | "tokio-macros",
1957 | "windows-sys 0.48.0",
1958 | ]
1959 |
1960 | [[package]]
1961 | name = "tokio-macros"
1962 | version = "2.1.0"
1963 | source = "registry+https://github.com/rust-lang/crates.io-index"
1964 | checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
1965 | dependencies = [
1966 | "proc-macro2",
1967 | "quote",
1968 | "syn 2.0.16",
1969 | ]
1970 |
1971 | [[package]]
1972 | name = "tokio-native-tls"
1973 | version = "0.3.1"
1974 | source = "registry+https://github.com/rust-lang/crates.io-index"
1975 | checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
1976 | dependencies = [
1977 | "native-tls",
1978 | "tokio",
1979 | ]
1980 |
1981 | [[package]]
1982 | name = "toml"
1983 | version = "0.7.4"
1984 | source = "registry+https://github.com/rust-lang/crates.io-index"
1985 | checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec"
1986 | dependencies = [
1987 | "serde",
1988 | "serde_spanned",
1989 | "toml_datetime",
1990 | "toml_edit",
1991 | ]
1992 |
1993 | [[package]]
1994 | name = "toml_datetime"
1995 | version = "0.6.2"
1996 | source = "registry+https://github.com/rust-lang/crates.io-index"
1997 | checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f"
1998 | dependencies = [
1999 | "serde",
2000 | ]
2001 |
2002 | [[package]]
2003 | name = "toml_edit"
2004 | version = "0.19.9"
2005 | source = "registry+https://github.com/rust-lang/crates.io-index"
2006 | checksum = "92d964908cec0d030b812013af25a0e57fddfadb1e066ecc6681d86253129d4f"
2007 | dependencies = [
2008 | "indexmap",
2009 | "serde",
2010 | "serde_spanned",
2011 | "toml_datetime",
2012 | "winnow",
2013 | ]
2014 |
2015 | [[package]]
2016 | name = "tracing"
2017 | version = "0.1.37"
2018 | source = "registry+https://github.com/rust-lang/crates.io-index"
2019 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
2020 | dependencies = [
2021 | "cfg-if",
2022 | "pin-project-lite",
2023 | "tracing-attributes",
2024 | "tracing-core",
2025 | ]
2026 |
2027 | [[package]]
2028 | name = "tracing-appender"
2029 | version = "0.2.2"
2030 | source = "registry+https://github.com/rust-lang/crates.io-index"
2031 | checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e"
2032 | dependencies = [
2033 | "crossbeam-channel",
2034 | "time 0.3.21",
2035 | "tracing-subscriber",
2036 | ]
2037 |
2038 | [[package]]
2039 | name = "tracing-attributes"
2040 | version = "0.1.24"
2041 | source = "registry+https://github.com/rust-lang/crates.io-index"
2042 | checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74"
2043 | dependencies = [
2044 | "proc-macro2",
2045 | "quote",
2046 | "syn 2.0.16",
2047 | ]
2048 |
2049 | [[package]]
2050 | name = "tracing-core"
2051 | version = "0.1.31"
2052 | source = "registry+https://github.com/rust-lang/crates.io-index"
2053 | checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
2054 | dependencies = [
2055 | "once_cell",
2056 | "valuable",
2057 | ]
2058 |
2059 | [[package]]
2060 | name = "tracing-log"
2061 | version = "0.1.3"
2062 | source = "registry+https://github.com/rust-lang/crates.io-index"
2063 | checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
2064 | dependencies = [
2065 | "lazy_static",
2066 | "log",
2067 | "tracing-core",
2068 | ]
2069 |
2070 | [[package]]
2071 | name = "tracing-subscriber"
2072 | version = "0.3.17"
2073 | source = "registry+https://github.com/rust-lang/crates.io-index"
2074 | checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77"
2075 | dependencies = [
2076 | "nu-ansi-term",
2077 | "sharded-slab",
2078 | "smallvec",
2079 | "thread_local",
2080 | "tracing-core",
2081 | "tracing-log",
2082 | ]
2083 |
2084 | [[package]]
2085 | name = "tungstenite"
2086 | version = "0.19.0"
2087 | source = "registry+https://github.com/rust-lang/crates.io-index"
2088 | checksum = "15fba1a6d6bb030745759a9a2a588bfe8490fc8b4751a277db3a0be1c9ebbf67"
2089 | dependencies = [
2090 | "byteorder",
2091 | "bytes",
2092 | "data-encoding",
2093 | "http",
2094 | "httparse",
2095 | "log",
2096 | "rand",
2097 | "sha1",
2098 | "thiserror",
2099 | "url",
2100 | "utf-8",
2101 | ]
2102 |
2103 | [[package]]
2104 | name = "tuxphones"
2105 | version = "0.1.0"
2106 | dependencies = [
2107 | "async-tungstenite",
2108 | "base64",
2109 | "chrono",
2110 | "ctrlc",
2111 | "futures-util",
2112 | "gst-plugin-discordstreamer",
2113 | "gst-plugin-ximageredux",
2114 | "gstreamer",
2115 | "image",
2116 | "lazy_static",
2117 | "libpulse-binding",
2118 | "once_cell",
2119 | "rand",
2120 | "serde",
2121 | "serde_json",
2122 | "sysinfo",
2123 | "tokio",
2124 | "tokio-native-tls",
2125 | "tracing",
2126 | "tracing-appender",
2127 | "tracing-log",
2128 | "tracing-subscriber",
2129 | "xcb",
2130 | ]
2131 |
2132 | [[package]]
2133 | name = "typenum"
2134 | version = "1.16.0"
2135 | source = "registry+https://github.com/rust-lang/crates.io-index"
2136 | checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
2137 |
2138 | [[package]]
2139 | name = "unicode-bidi"
2140 | version = "0.3.13"
2141 | source = "registry+https://github.com/rust-lang/crates.io-index"
2142 | checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
2143 |
2144 | [[package]]
2145 | name = "unicode-ident"
2146 | version = "1.0.8"
2147 | source = "registry+https://github.com/rust-lang/crates.io-index"
2148 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
2149 |
2150 | [[package]]
2151 | name = "unicode-normalization"
2152 | version = "0.1.22"
2153 | source = "registry+https://github.com/rust-lang/crates.io-index"
2154 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
2155 | dependencies = [
2156 | "tinyvec",
2157 | ]
2158 |
2159 | [[package]]
2160 | name = "universal-hash"
2161 | version = "0.5.1"
2162 | source = "registry+https://github.com/rust-lang/crates.io-index"
2163 | checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
2164 | dependencies = [
2165 | "crypto-common",
2166 | "subtle",
2167 | ]
2168 |
2169 | [[package]]
2170 | name = "url"
2171 | version = "2.3.1"
2172 | source = "registry+https://github.com/rust-lang/crates.io-index"
2173 | checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
2174 | dependencies = [
2175 | "form_urlencoded",
2176 | "idna",
2177 | "percent-encoding",
2178 | ]
2179 |
2180 | [[package]]
2181 | name = "utf-8"
2182 | version = "0.7.6"
2183 | source = "registry+https://github.com/rust-lang/crates.io-index"
2184 | checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
2185 |
2186 | [[package]]
2187 | name = "valuable"
2188 | version = "0.1.0"
2189 | source = "registry+https://github.com/rust-lang/crates.io-index"
2190 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
2191 |
2192 | [[package]]
2193 | name = "vcpkg"
2194 | version = "0.2.15"
2195 | source = "registry+https://github.com/rust-lang/crates.io-index"
2196 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
2197 |
2198 | [[package]]
2199 | name = "version-compare"
2200 | version = "0.1.1"
2201 | source = "registry+https://github.com/rust-lang/crates.io-index"
2202 | checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
2203 |
2204 | [[package]]
2205 | name = "version_check"
2206 | version = "0.9.4"
2207 | source = "registry+https://github.com/rust-lang/crates.io-index"
2208 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
2209 |
2210 | [[package]]
2211 | name = "wasi"
2212 | version = "0.10.0+wasi-snapshot-preview1"
2213 | source = "registry+https://github.com/rust-lang/crates.io-index"
2214 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
2215 |
2216 | [[package]]
2217 | name = "wasi"
2218 | version = "0.11.0+wasi-snapshot-preview1"
2219 | source = "registry+https://github.com/rust-lang/crates.io-index"
2220 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
2221 |
2222 | [[package]]
2223 | name = "wasm-bindgen"
2224 | version = "0.2.86"
2225 | source = "registry+https://github.com/rust-lang/crates.io-index"
2226 | checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73"
2227 | dependencies = [
2228 | "cfg-if",
2229 | "wasm-bindgen-macro",
2230 | ]
2231 |
2232 | [[package]]
2233 | name = "wasm-bindgen-backend"
2234 | version = "0.2.86"
2235 | source = "registry+https://github.com/rust-lang/crates.io-index"
2236 | checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb"
2237 | dependencies = [
2238 | "bumpalo",
2239 | "log",
2240 | "once_cell",
2241 | "proc-macro2",
2242 | "quote",
2243 | "syn 2.0.16",
2244 | "wasm-bindgen-shared",
2245 | ]
2246 |
2247 | [[package]]
2248 | name = "wasm-bindgen-macro"
2249 | version = "0.2.86"
2250 | source = "registry+https://github.com/rust-lang/crates.io-index"
2251 | checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258"
2252 | dependencies = [
2253 | "quote",
2254 | "wasm-bindgen-macro-support",
2255 | ]
2256 |
2257 | [[package]]
2258 | name = "wasm-bindgen-macro-support"
2259 | version = "0.2.86"
2260 | source = "registry+https://github.com/rust-lang/crates.io-index"
2261 | checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
2262 | dependencies = [
2263 | "proc-macro2",
2264 | "quote",
2265 | "syn 2.0.16",
2266 | "wasm-bindgen-backend",
2267 | "wasm-bindgen-shared",
2268 | ]
2269 |
2270 | [[package]]
2271 | name = "wasm-bindgen-shared"
2272 | version = "0.2.86"
2273 | source = "registry+https://github.com/rust-lang/crates.io-index"
2274 | checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
2275 |
2276 | [[package]]
2277 | name = "weezl"
2278 | version = "0.1.7"
2279 | source = "registry+https://github.com/rust-lang/crates.io-index"
2280 | checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
2281 |
2282 | [[package]]
2283 | name = "winapi"
2284 | version = "0.3.9"
2285 | source = "registry+https://github.com/rust-lang/crates.io-index"
2286 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
2287 | dependencies = [
2288 | "winapi-i686-pc-windows-gnu",
2289 | "winapi-x86_64-pc-windows-gnu",
2290 | ]
2291 |
2292 | [[package]]
2293 | name = "winapi-i686-pc-windows-gnu"
2294 | version = "0.4.0"
2295 | source = "registry+https://github.com/rust-lang/crates.io-index"
2296 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
2297 |
2298 | [[package]]
2299 | name = "winapi-x86_64-pc-windows-gnu"
2300 | version = "0.4.0"
2301 | source = "registry+https://github.com/rust-lang/crates.io-index"
2302 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
2303 |
2304 | [[package]]
2305 | name = "windows"
2306 | version = "0.48.0"
2307 | source = "registry+https://github.com/rust-lang/crates.io-index"
2308 | checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
2309 | dependencies = [
2310 | "windows-targets 0.48.0",
2311 | ]
2312 |
2313 | [[package]]
2314 | name = "windows-sys"
2315 | version = "0.42.0"
2316 | source = "registry+https://github.com/rust-lang/crates.io-index"
2317 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
2318 | dependencies = [
2319 | "windows_aarch64_gnullvm 0.42.2",
2320 | "windows_aarch64_msvc 0.42.2",
2321 | "windows_i686_gnu 0.42.2",
2322 | "windows_i686_msvc 0.42.2",
2323 | "windows_x86_64_gnu 0.42.2",
2324 | "windows_x86_64_gnullvm 0.42.2",
2325 | "windows_x86_64_msvc 0.42.2",
2326 | ]
2327 |
2328 | [[package]]
2329 | name = "windows-sys"
2330 | version = "0.45.0"
2331 | source = "registry+https://github.com/rust-lang/crates.io-index"
2332 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
2333 | dependencies = [
2334 | "windows-targets 0.42.2",
2335 | ]
2336 |
2337 | [[package]]
2338 | name = "windows-sys"
2339 | version = "0.48.0"
2340 | source = "registry+https://github.com/rust-lang/crates.io-index"
2341 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
2342 | dependencies = [
2343 | "windows-targets 0.48.0",
2344 | ]
2345 |
2346 | [[package]]
2347 | name = "windows-targets"
2348 | version = "0.42.2"
2349 | source = "registry+https://github.com/rust-lang/crates.io-index"
2350 | checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
2351 | dependencies = [
2352 | "windows_aarch64_gnullvm 0.42.2",
2353 | "windows_aarch64_msvc 0.42.2",
2354 | "windows_i686_gnu 0.42.2",
2355 | "windows_i686_msvc 0.42.2",
2356 | "windows_x86_64_gnu 0.42.2",
2357 | "windows_x86_64_gnullvm 0.42.2",
2358 | "windows_x86_64_msvc 0.42.2",
2359 | ]
2360 |
2361 | [[package]]
2362 | name = "windows-targets"
2363 | version = "0.48.0"
2364 | source = "registry+https://github.com/rust-lang/crates.io-index"
2365 | checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
2366 | dependencies = [
2367 | "windows_aarch64_gnullvm 0.48.0",
2368 | "windows_aarch64_msvc 0.48.0",
2369 | "windows_i686_gnu 0.48.0",
2370 | "windows_i686_msvc 0.48.0",
2371 | "windows_x86_64_gnu 0.48.0",
2372 | "windows_x86_64_gnullvm 0.48.0",
2373 | "windows_x86_64_msvc 0.48.0",
2374 | ]
2375 |
2376 | [[package]]
2377 | name = "windows_aarch64_gnullvm"
2378 | version = "0.42.2"
2379 | source = "registry+https://github.com/rust-lang/crates.io-index"
2380 | checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
2381 |
2382 | [[package]]
2383 | name = "windows_aarch64_gnullvm"
2384 | version = "0.48.0"
2385 | source = "registry+https://github.com/rust-lang/crates.io-index"
2386 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
2387 |
2388 | [[package]]
2389 | name = "windows_aarch64_msvc"
2390 | version = "0.42.2"
2391 | source = "registry+https://github.com/rust-lang/crates.io-index"
2392 | checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
2393 |
2394 | [[package]]
2395 | name = "windows_aarch64_msvc"
2396 | version = "0.48.0"
2397 | source = "registry+https://github.com/rust-lang/crates.io-index"
2398 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
2399 |
2400 | [[package]]
2401 | name = "windows_i686_gnu"
2402 | version = "0.42.2"
2403 | source = "registry+https://github.com/rust-lang/crates.io-index"
2404 | checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
2405 |
2406 | [[package]]
2407 | name = "windows_i686_gnu"
2408 | version = "0.48.0"
2409 | source = "registry+https://github.com/rust-lang/crates.io-index"
2410 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
2411 |
2412 | [[package]]
2413 | name = "windows_i686_msvc"
2414 | version = "0.42.2"
2415 | source = "registry+https://github.com/rust-lang/crates.io-index"
2416 | checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
2417 |
2418 | [[package]]
2419 | name = "windows_i686_msvc"
2420 | version = "0.48.0"
2421 | source = "registry+https://github.com/rust-lang/crates.io-index"
2422 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
2423 |
2424 | [[package]]
2425 | name = "windows_x86_64_gnu"
2426 | version = "0.42.2"
2427 | source = "registry+https://github.com/rust-lang/crates.io-index"
2428 | checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
2429 |
2430 | [[package]]
2431 | name = "windows_x86_64_gnu"
2432 | version = "0.48.0"
2433 | source = "registry+https://github.com/rust-lang/crates.io-index"
2434 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
2435 |
2436 | [[package]]
2437 | name = "windows_x86_64_gnullvm"
2438 | version = "0.42.2"
2439 | source = "registry+https://github.com/rust-lang/crates.io-index"
2440 | checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
2441 |
2442 | [[package]]
2443 | name = "windows_x86_64_gnullvm"
2444 | version = "0.48.0"
2445 | source = "registry+https://github.com/rust-lang/crates.io-index"
2446 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
2447 |
2448 | [[package]]
2449 | name = "windows_x86_64_msvc"
2450 | version = "0.42.2"
2451 | source = "registry+https://github.com/rust-lang/crates.io-index"
2452 | checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
2453 |
2454 | [[package]]
2455 | name = "windows_x86_64_msvc"
2456 | version = "0.48.0"
2457 | source = "registry+https://github.com/rust-lang/crates.io-index"
2458 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
2459 |
2460 | [[package]]
2461 | name = "winnow"
2462 | version = "0.4.6"
2463 | source = "registry+https://github.com/rust-lang/crates.io-index"
2464 | checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699"
2465 | dependencies = [
2466 | "memchr",
2467 | ]
2468 |
2469 | [[package]]
2470 | name = "xcb"
2471 | version = "1.2.1"
2472 | source = "registry+https://github.com/rust-lang/crates.io-index"
2473 | checksum = "4b90c622d513012e7419594a2138953603c63848cb189041e7b5dc04d3895da5"
2474 | dependencies = [
2475 | "bitflags",
2476 | "libc",
2477 | "quick-xml",
2478 | ]
2479 |
2480 | [[package]]
2481 | name = "xsalsa20poly1305"
2482 | version = "0.9.1"
2483 | source = "registry+https://github.com/rust-lang/crates.io-index"
2484 | checksum = "02a6dad357567f81cd78ee75f7c61f1b30bb2fe4390be8fb7c69e2ac8dffb6c7"
2485 | dependencies = [
2486 | "aead",
2487 | "poly1305",
2488 | "salsa20",
2489 | "subtle",
2490 | "zeroize",
2491 | ]
2492 |
2493 | [[package]]
2494 | name = "zeroize"
2495 | version = "1.6.0"
2496 | source = "registry+https://github.com/rust-lang/crates.io-index"
2497 | checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
2498 |
2499 | [[package]]
2500 | name = "zune-inflate"
2501 | version = "0.2.54"
2502 | source = "registry+https://github.com/rust-lang/crates.io-index"
2503 | checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
2504 | dependencies = [
2505 | "simd-adler32",
2506 | ]
2507 |
--------------------------------------------------------------------------------
/daemon/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "tuxphones"
3 | version = "0.1.0"
4 | edition = "2021"
5 | license = "MIT"
6 | description = "Daemon for Tuxphones BetterDiscord plugin"
7 | homepage = "https://github.com/ImTheSquid/Tuxphones"
8 | repository = "https://github.com/ImTheSquid/Tuxphones"
9 |
10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
11 |
12 | [dependencies]
13 | libpulse-binding = "2.27.1"
14 | gst = { package = "gstreamer", version = "0.20.5", features = ["v1_18"] }
15 | once_cell = "1.17.1"
16 | serde = { version = "1.0", features = ["derive"] }
17 | serde_json = "1.0"
18 | ctrlc={ version = "3.2.5", features = ["termination"] }
19 | xcb = { version = "1.2.1", features = ["res"] }
20 | sysinfo = "0.26.9"
21 | tracing = "0.1.37"
22 | tracing-subscriber = "0.3.17"
23 | futures-util = "0.3.28"
24 | lazy_static = "1.4.0"
25 | rand = "0.8.5"
26 | image = "0.24.6"
27 | base64 = "0.13.1"
28 | tokio = { version = "1.28.0", features = ["full"] }
29 | tokio-native-tls = "0.3.1"
30 | tracing-log = "0.1.3"
31 | tracing-appender = "0.2.2"
32 | chrono = "0.4.24"
33 | gst-plugin-ximageredux = "0.1.7"
34 | async-tungstenite = { version = "0.22.1", features = ["tokio-runtime"] }
35 | gst-plugin-discordstreamer = {git = "https://github.com/ImTheSquid/gst-discordsender"}
--------------------------------------------------------------------------------
/daemon/src/gstreamer.rs:
--------------------------------------------------------------------------------
1 | use discordstreamer::discordstreamer::DiscordStreamer;
2 | use gst::prelude::*;
3 | use gst::{
4 | debug_bin_to_dot_data, glib, DebugGraphDetails, Element, PadLinkError, StateChangeError,
5 | StateChangeSuccess,
6 | };
7 | use gst::subclass::prelude::ObjectSubclassIsExt;
8 | use image::EncodableLayout;
9 | use tracing::{debug, error, info, trace};
10 | use tracing_log::log::Level;
11 |
12 | use crate::{
13 | socket::{StreamResolutionInformation},
14 | xid,
15 | };
16 |
17 | #[derive(Debug)]
18 | pub enum GstInitializationError {
19 | Init(glib::Error),
20 | Element(glib::BoolError),
21 | Pad(PadLinkError),
22 | }
23 |
24 | #[derive(Debug)]
25 | pub struct StreamSSRCs {
26 | pub audio: u32,
27 | pub video: u32,
28 | pub rtx: u32,
29 | }
30 |
31 | impl std::fmt::Display for GstInitializationError {
32 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 | let str = match self {
34 | GstInitializationError::Init(e) => format!("Initialization error: {:?}", e),
35 | GstInitializationError::Element(e) => format!("Element error: {:?}", e),
36 | GstInitializationError::Pad(e) => format!("Pad error: {:?}", e),
37 | };
38 | f.write_str(&str)
39 | }
40 | }
41 |
42 | #[derive(Clone, Copy)]
43 | pub struct H264Settings {
44 | pub nvidia_encoder: bool,
45 | }
46 |
47 | #[derive(Clone, Copy)]
48 | #[allow(dead_code)]
49 | pub enum VideoEncoderType {
50 | H264(H264Settings),
51 | VP8,
52 | VP9,
53 | }
54 |
55 | impl From for GstInitializationError {
56 | fn from(error: glib::Error) -> Self {
57 | GstInitializationError::Init(error)
58 | }
59 | }
60 |
61 | impl From for GstInitializationError {
62 | fn from(error: glib::BoolError) -> Self {
63 | GstInitializationError::Element(error)
64 | }
65 | }
66 |
67 | impl From for GstInitializationError {
68 | fn from(error: PadLinkError) -> Self {
69 | GstInitializationError::Pad(error)
70 | }
71 | }
72 |
73 | pub struct GstHandle {
74 | pipeline: gst::Pipeline,
75 | encoder: Element,
76 | encoder_type: VideoEncoderType,
77 | }
78 |
79 | //Custom drop logic to deinit gstreamer when all handles are dropped
80 | impl Drop for GstHandle {
81 | fn drop(&mut self) {
82 | info!("dropping GstHandle");
83 | // Debug diagram
84 | let out = debug_bin_to_dot_data(&self.pipeline, DebugGraphDetails::ALL);
85 | //TODO: Move to logs folder
86 | std::fs::write("/tmp/tuxphones_gstdrop.dot", out.as_str()).unwrap();
87 |
88 | if let Err(e) = self.pipeline.set_state(gst::State::Null) {
89 | error!("Failed to stop pipeline: {:?}", e);
90 | };
91 | }
92 | }
93 |
94 | impl GstHandle {
95 | pub async fn new(
96 | encoder_to_use: VideoEncoderType,
97 | xid: xid,
98 | resolution: StreamResolutionInformation,
99 | fps: i32,
100 | secret_key: Vec,
101 | base_ssrc: u32,
102 | address: String,
103 | ) -> Result {
104 | info!("Creating new GstHandle");
105 | //Create a new GStreamer pipeline
106 | let pipeline = gst::Pipeline::new(None);
107 |
108 | //--VIDEO--
109 |
110 | //Create a new ximagesrc to get video from the X server
111 | let ximagesrc = ximageredux::XImageRedux::default();
112 |
113 | let videoscale = gst::ElementFactory::make("videoscale").build()?;
114 |
115 | //Creating a capsfilter to set the resolution and the fps
116 | let capsfilter = gst::ElementFactory::make("capsfilter").build()?;
117 |
118 | let mut cap = gst::Caps::builder("video/x-raw")
119 | .field("frame_rate", gst::Fraction::new(fps, 1));
120 |
121 | //If the resolution is specified, add it to the caps
122 | if resolution.is_fixed {
123 | cap = cap
124 | .field("width", resolution.width as i32)
125 | .field("height", resolution.height as i32);
126 | };
127 |
128 | capsfilter.set_property(
129 | "caps",
130 | &cap.build(),
131 | );
132 |
133 | // ximagesrc.set_property_from_str("show-pointer", "1");
134 | //Set xid based on constructor parameter to get video only from the specified X window
135 | ximagesrc.set_property("xid", xid as u32);
136 |
137 | //Create a new videoconvert to allow encoding of the raw video
138 | let videoconvert = gst::ElementFactory::make("videoconvert").build()?;
139 |
140 | //Chose encoder based on constructor params
141 | let encoder = match encoder_to_use {
142 | VideoEncoderType::H264(settings) => {
143 | //Use nvidia encoder based on settings
144 | if settings.nvidia_encoder {
145 | let nvh264enc = gst::ElementFactory::make("nvh264enc").build()?;
146 | nvh264enc.set_property("gop-size", 2560i32);
147 | nvh264enc.set_property_from_str("rc-mode", "cbr-ld-hq");
148 | nvh264enc.set_property("zerolatency", true);
149 | nvh264enc
150 | } else {
151 | let x264enc = gst::ElementFactory::make("x264enc").build()?;
152 | x264enc.set_property("threads", 12u32);
153 | x264enc.set_property_from_str("tune", "zerolatency");
154 | x264enc.set_property_from_str("speed-preset", "ultrafast");
155 | x264enc.set_property("key-int-max", 2560u32);
156 | x264enc.set_property("b-adapt", false);
157 | x264enc.set_property("vbv-buf-capacity", 120u32);
158 | x264enc
159 | }
160 | }
161 | VideoEncoderType::VP8 => {
162 | let vp8enc = gst::ElementFactory::make("vp8enc").build()?;
163 | vp8enc.set_property("threads", 12i32);
164 | vp8enc.set_property("cpu-used", -16i32);
165 | vp8enc.set_property_from_str("end-usage", "cbr");
166 | vp8enc.set_property("buffer-initial-size", 100i32);
167 | vp8enc.set_property("buffer-optimal-size", 120i32);
168 | vp8enc.set_property("buffer-size", 150i32);
169 | vp8enc.set_property("max-intra-bitrate", 250i32);
170 | vp8enc.set_property_from_str("error-resilient", "default");
171 | vp8enc.set_property("lag-in-frames", 0i32);
172 | vp8enc
173 | }
174 | VideoEncoderType::VP9 => {
175 | let vp9enc = gst::ElementFactory::make("vp9enc").build()?;
176 | vp9enc.set_property("threads", 12i32);
177 | vp9enc.set_property("cpu-used", -16i32);
178 | vp9enc.set_property_from_str("end-usage", "cbr");
179 | vp9enc.set_property("buffer-initial-size", 100i32);
180 | vp9enc.set_property("buffer-optimal-size", 120i32);
181 | vp9enc.set_property("buffer-size", 150i32);
182 | vp9enc.set_property("max-intra-bitrate", 250i32);
183 | vp9enc.set_property_from_str("error-resilient", "default");
184 | vp9enc.set_property("lag-in-frames", 0i32);
185 | vp9enc
186 | }
187 | };
188 |
189 | //--AUDIO--
190 |
191 | // Caps filter for audio from conversion to encoding
192 | let audio_capsfilter = gst::ElementFactory::make("capsfilter").build()?;
193 |
194 | let cap = gst::Caps::builder("audio/x-raw")
195 | .field("channels", 2)
196 | .field("rate", 48000);
197 |
198 | audio_capsfilter.set_property(
199 | "caps",
200 | &cap.build(),
201 | );
202 |
203 | //Create a new pulsesrc to get audio from the PulseAudio server
204 | let pulsesrc = gst::ElementFactory::make("pulsesrc").build()?;
205 | //Set the audio device based on constructor parameter (should be the sink of the audio application)
206 | pulsesrc.set_property_from_str("device", "tuxphones.monitor");
207 |
208 | //Create a new audioconvert to allow encoding of the raw audio
209 | let audioconvert = gst::ElementFactory::make("audioconvert").build()?;
210 | //Encoder for the raw audio to opus
211 | let opusenc = gst::ElementFactory::make("opusenc").build()?;
212 | opusenc.set_property("bitrate", 32000i32);
213 | opusenc.set_property_from_str("bitrate-type", "cbr");
214 | opusenc.set_property("inband-fec", true);
215 | opusenc.set_property("packet-loss-percentage", 50);
216 |
217 | //DESTINATION
218 | let discord_streamer = DiscordStreamer::default();
219 | let video_ssrc = base_ssrc;
220 | let audio_ssrc = base_ssrc + 1;
221 | discord_streamer.set_property("crypto-key", glib::Bytes::from(secret_key.as_bytes()));
222 | discord_streamer.set_property("address", address.to_value());
223 | discord_streamer.set_property("video-ssrc", video_ssrc.to_value());
224 | discord_streamer.set_property("audio-ssrc", audio_ssrc.to_value());
225 | debug!("DiscordStreamer created");
226 | trace!("DiscordStreamer address: {:?}", address);
227 | trace!("DiscordStreamer video-ssrc: {:?}", base_ssrc);
228 | trace!("DiscordStreamer audio-ssrc: {:?}", base_ssrc+1);
229 | trace!("DiscordStreamer crypto-key: {:?}", secret_key);
230 |
231 |
232 | //queues
233 | let video_encoder_queue = gst::ElementFactory::make("queue").build()?;
234 | let audio_encoder_queue = gst::ElementFactory::make("queue").build()?;
235 | let video_webrtc_queue = gst::ElementFactory::make("queue").build()?;
236 | let audio_webrtc_queue = gst::ElementFactory::make("queue").build()?;
237 |
238 | //Add elements to the pipeline
239 | pipeline.add_many(&[
240 | ximagesrc.upcast_ref::(),
241 | &videoscale,
242 | &capsfilter,
243 | &videoconvert,
244 | &encoder,
245 | &video_encoder_queue,
246 | &video_webrtc_queue,
247 | /*&pulsesrc,
248 | &audioconvert,
249 | &audio_capsfilter,
250 | &opusenc,
251 | &audio_encoder_queue,
252 | &audio_webrtc_queue,
253 | */
254 | discord_streamer.upcast_ref::(),
255 | ])?;
256 |
257 | //Link video elements
258 | Element::link_many(&[
259 | ximagesrc.upcast_ref::(),
260 | &videoscale,
261 | &capsfilter,
262 | &videoconvert,
263 | &video_encoder_queue,
264 | &encoder,
265 | &video_webrtc_queue,
266 | discord_streamer.upcast_ref::(),
267 | ])?;
268 |
269 | /* //Link audio elements
270 | Element::link_many(&[
271 | &pulsesrc,
272 | &audio_capsfilter,
273 | &audioconvert,
274 | &audio_encoder_queue,
275 | &opusenc,
276 | &audio_webrtc_queue,
277 | discord_streamer.upcast_ref::(),
278 | ])?;
279 | */
280 | // Debug diagram
281 | let out = debug_bin_to_dot_data(&pipeline, DebugGraphDetails::ALL);
282 | //TODO: Move to logs folder
283 | std::fs::write("/tmp/tuxphones_gst.dot", out.as_str()).unwrap();
284 |
285 | Ok(GstHandle {
286 | pipeline,
287 | encoder,
288 | encoder_type: encoder_to_use,
289 | })
290 | }
291 |
292 | pub async fn start(
293 | &self,
294 | ) -> Result {
295 | self.pipeline.set_state(gst::State::Playing)?;
296 |
297 | Ok(StateChangeSuccess::Success)
298 | }
299 | }
300 |
--------------------------------------------------------------------------------
/daemon/src/lib.rs:
--------------------------------------------------------------------------------
1 | use std::{
2 | sync::{
3 | atomic::{AtomicBool, Ordering},
4 | Arc,
5 | },
6 | time::{self, Duration},
7 | };
8 |
9 | use sysinfo::{Pid, PidExt, Process, ProcessExt, SystemExt};
10 | use tracing::{error, info};
11 |
12 | use pulse::PulseHandle;
13 | use socket::{Application, SocketListenerCommand, WebSocket};
14 | use x::XServerHandle;
15 | // Makes sure typing is preserved
16 | use u32 as pid;
17 | use u32 as xid;
18 |
19 | use crate::gstreamer::{GstHandle, H264Settings, VideoEncoderType};
20 |
21 | use tokio::{
22 | sync::{
23 | mpsc::{self, Receiver},
24 | Mutex,
25 | },
26 | time::sleep,
27 | };
28 |
29 | mod gstreamer;
30 | mod pulse;
31 | pub mod socket;
32 | mod x;
33 |
34 | pub struct CommandProcessor {
35 | thread: Option>,
36 | }
37 |
38 | impl CommandProcessor {
39 | pub fn new(
40 | mut receiver: Receiver,
41 | run: Arc,
42 | sleep_time: Duration,
43 | websocket: Arc>,
44 | ) -> Self {
45 | let thread = tokio::spawn(async move {
46 | let mut pulse = match PulseHandle::new() {
47 | Ok(handle) => handle,
48 | Err(e) => {
49 | error!("Pulse error: {}", e);
50 | run.store(false, Ordering::SeqCst);
51 | return;
52 | }
53 | };
54 |
55 | let x = match XServerHandle::new() {
56 | Ok(handle) => handle,
57 | Err(e) => {
58 | error!("X Server error: {}", e);
59 | run.store(false, Ordering::SeqCst);
60 | return;
61 | }
62 | };
63 |
64 | let mut last_stream_preview: Option = None;
65 | let mut current_xid = None;
66 |
67 | let mut gst_is_loaded = false;
68 |
69 | let mut stream = None;
70 |
71 | loop {
72 | if !run.load(Ordering::SeqCst) {
73 | // Kill websocket if still running
74 | stream.take();
75 | current_xid.take();
76 | if gst_is_loaded {
77 | unsafe {
78 | gst::deinit();
79 | }
80 | }
81 | info!("Command processor shut down");
82 | break;
83 | }
84 |
85 | match receiver.try_recv() {
86 | Ok(cmd) => {
87 | match cmd {
88 | SocketListenerCommand::StartStream {
89 | pid,
90 | xid,
91 | resolution,
92 | framerate,
93 | rtc_connection_id,
94 | secret_key,
95 | voice_ssrc,
96 | base_ssrc,
97 | ip,
98 | port,
99 | } => {
100 | info!("[StartStream] Command received");
101 | match pulse.setup_audio_capture(None) {
102 | Ok(_) => {}
103 | Err(e) => {
104 | error!("Failed to setup pulse capture: {}", e);
105 | continue;
106 | }
107 | }
108 |
109 | match pulse.start_capture(pid) {
110 | Ok(_) => {}
111 | Err(e) => {
112 | error!("Failed to start pulse capture: {}", e);
113 | continue;
114 | }
115 | }
116 |
117 | let _ = current_xid.insert(xid);
118 |
119 | // Quick and drity check to try to detect Nvidia drivers
120 | // TODO: Find a better way to do this
121 | //let nvidia_encoder = if let Ok(out) = Command::new("lspci").arg("-nnk").output() {
122 | // String::from_utf8_lossy(&out.stdout).contains("nvidia")
123 | //} else { false };
124 |
125 | if !gst_is_loaded {
126 | gst_is_loaded = true;
127 | gst::init().expect("Failed to intialize gstreamer");
128 | }
129 |
130 | let gst = GstHandle::new(
131 | VideoEncoderType::H264(H264Settings {
132 | nvidia_encoder: false,
133 | }),
134 | xid,
135 | resolution.clone(),
136 | framerate.into(),
137 | secret_key,
138 | base_ssrc,
139 | format!("{}:{}", ip, port),
140 | )
141 | .await
142 | .expect("Failed to initialize gstreamer pipeline");
143 | gst.start()
144 | .await
145 | .expect("Failed to start stream");
146 |
147 | let _ = stream.insert(gst);
148 |
149 | info!("[StartStream] Command processed (stream started)");
150 | }
151 | SocketListenerCommand::StopStream
152 | | SocketListenerCommand::StopStreamInternal => {
153 | info!("[StopStream] Command received");
154 |
155 | // Kill gstreamer
156 | stream.take();
157 |
158 | pulse.stop_capture();
159 | pulse.teardown_audio_capture();
160 |
161 | info!("[StopStream] Command processed (stream stopped)");
162 |
163 | // If stream was stopped internally, send a notification to the client
164 | if cmd == SocketListenerCommand::StopStreamInternal {
165 | if let Err(e) =
166 | websocket.lock().await.stream_stop_internal().await
167 | {
168 | error!(
169 | "Failed to notify client of internal stream stop: {:?}",
170 | e
171 | );
172 | }
173 | }
174 | }
175 | SocketListenerCommand::GetInfo { xids } => {
176 | info!("[GetInfo] Command received");
177 |
178 | // Find all PIDs of given XIDs
179 | let xid_pid: Vec<(xid, pid)> = xids
180 | .into_iter()
181 | .filter_map(|xid| {
182 | if let Ok(Some(pid)) = x.pid_from_xid(xid) {
183 | return Some((xid, pid));
184 | }
185 |
186 | None
187 | })
188 | .collect();
189 |
190 | // Do initial matching against returned Pulse PIDs
191 | let mut apps = pulse.get_audio_applications();
192 | let mut found_applications = vec![];
193 | for (xid, pid) in &xid_pid {
194 | if let Some(idx) = apps.iter().position(|app| app.pid == *pid) {
195 | let app = apps.remove(idx);
196 | found_applications.push(Application {
197 | name: app.name,
198 | pid: *pid,
199 | xid: *xid,
200 | });
201 | }
202 | }
203 |
204 | // If there are more Pulse applications to resolve, lookup process name and try to find pair with given PID for XID
205 | // Find all processes with given name
206 | let mut system = sysinfo::System::new();
207 | system.refresh_processes();
208 | let processes_with_cmd: Vec<(&Pid, &Process)> = system
209 | .processes()
210 | .iter()
211 | .filter(|(_, p)| !p.cmd().is_empty())
212 | .collect();
213 |
214 | for app in &apps {
215 | for (proc_pid, process) in &processes_with_cmd {
216 | let cmd_strings: Vec<&str> =
217 | process.cmd()[0].split(' ').collect();
218 | // If the command matches the Pulse application name
219 | if cmd_strings[0].ends_with(&format!("/{}", &app.name)) {
220 | // And the PID of an XID window matches the PID of the found process
221 | if let Some((xid, _)) = xid_pid
222 | .iter()
223 | .find(|(_, pid)| *pid == proc_pid.as_u32())
224 | {
225 | // Push the application and go to the next one
226 | found_applications.push(Application {
227 | name: app.name.clone(),
228 | pid: app.pid,
229 | xid: *xid,
230 | });
231 | break;
232 | }
233 | }
234 | }
235 | }
236 |
237 | match websocket
238 | .lock()
239 | .await
240 | .application_info(&found_applications)
241 | .await
242 | {
243 | Ok(_) => info!(
244 | "[GetInfo] Command processed (applications found: {})",
245 | found_applications.len()
246 | ),
247 | Err(e) => error!("Failed to send application data: {}", e),
248 | }
249 | }
250 | }
251 | }
252 | Err(e) => match e {
253 | mpsc::error::TryRecvError::Disconnected => {
254 | error!("Failed to watch for receiver: {}", e);
255 | run.store(false, Ordering::SeqCst);
256 | break;
257 | }
258 | mpsc::error::TryRecvError::Empty => {
259 | // Check if time to send a stream preview
260 | let send_preview = if stream.is_some() {
261 | if let Some(last) = last_stream_preview {
262 | time::Instant::now().duration_since(last) > Duration::from_secs(10 * 60)
263 | } else {
264 | true
265 | }
266 | } else {
267 | false
268 | };
269 |
270 | if send_preview {
271 | let _ = last_stream_preview.insert(time::Instant::now());
272 | info!("Sending stream preview");
273 | if let Err(e) = websocket
274 | .lock()
275 | .await
276 | .stream_preview(
277 | &x.take_screenshot(current_xid.unwrap()).unwrap(),
278 | )
279 | .await
280 | {
281 | error!("Failed to send stream preview: {}", e);
282 | }
283 | }
284 |
285 | sleep(sleep_time).await;
286 | }
287 | },
288 | }
289 | }
290 | });
291 |
292 | CommandProcessor {
293 | thread: Some(thread),
294 | }
295 | }
296 |
297 | /// Waits for the `CommandProcessor`'s internal thread to join.
298 | pub async fn join(&mut self) {
299 | if let Some(thread) = self.thread.take() {
300 | thread.await.expect("Unable to join thread");
301 | }
302 | }
303 | }
304 |
--------------------------------------------------------------------------------
/daemon/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::{fs, panic, process, sync::{
2 | Arc,
3 | atomic::{AtomicBool, Ordering},
4 | }, time::Duration};
5 | use std::collections::HashMap;
6 | use std::str::FromStr;
7 |
8 | use tokio::{
9 | signal::{ctrl_c, unix::SignalKind},
10 | sync::mpsc,
11 | };
12 | use tokio::sync::Mutex;
13 | use tracing::{error, info, Level};
14 | use tracing_log::LogTracer;
15 | use tracing_subscriber::{filter, Layer};
16 | use tracing_subscriber::layer::SubscriberExt;
17 |
18 | use tuxphones::{CommandProcessor, socket::WebSocket};
19 |
20 | #[tokio::main]
21 | async fn main() {
22 | initialize_logging();
23 |
24 | let run = Arc::new(AtomicBool::new(true));
25 | let r = Arc::clone(&run);
26 |
27 | // Ctrl+C handling
28 | // match ctrlc::set_handler(move || {
29 | // info!("Interrupt!");
30 | // r.store(false, Ordering::SeqCst);
31 | // }) {
32 | // Ok(_) => {},
33 | // Err(e) => {
34 | // error!("Failed to set interrupt handler! {}", e);
35 | // process::exit(1);
36 | // }
37 | // }
38 |
39 | let (sender, receiver) = mpsc::channel(1000);
40 |
41 | let socket_watcher: Arc> = match WebSocket::new(9000, sender.clone()).await {
42 | Ok(s) => Arc::new(Mutex::new(s)),
43 | Err(_) => {
44 | error!("Error creating socket watcher!");
45 | process::exit(2);
46 | }
47 | };
48 |
49 | let mut command_processor = CommandProcessor::new(
50 | receiver,
51 | Arc::clone(&run),
52 | Duration::from_millis(500),
53 | socket_watcher.clone(),
54 | );
55 |
56 | info!("Daemon started");
57 |
58 | let mut sig = tokio::signal::unix::signal(SignalKind::terminate()).unwrap();
59 |
60 | tokio::select! {
61 | _ = sig.recv() => {},
62 | _ = ctrl_c() => {}
63 | }
64 |
65 | r.store(false, Ordering::SeqCst);
66 |
67 | socket_watcher.lock().await.abort().await;
68 | command_processor.join().await;
69 | }
70 |
71 | fn initialize_logging() {
72 | // "TUX_LOG=category=level,category=level..."
73 | // "TUX_FILE_LOG=category=level,category=level..."
74 | // "TUX_FILE_PATH=/path/to/folder"
75 | // TUX_FILE_PATH can include {date} and {time} which will be replaced with the current date and time and {pid} which will be replaced with the current process id
76 | //Where category is one of:
77 | // - "tuxphones" for the daemon itself
78 | // - "tuxphones::websocket" for the websocket
79 | // - "tuxphones::command" for the command processor
80 | //And level is one of:
81 | // - 0 = disabled
82 | // - 1 = "error"
83 | // - 2 = "debug"
84 | // - 3 = "info"
85 | // - 4 = "debug"
86 | // - 5 = "trace"
87 | let console_categories: HashMap = std::env::var("TUX_LOG")
88 | .unwrap_or_else(|_| "tuxphones=3".to_string())
89 | .split(',')
90 | .map(|s| {
91 | let mut split = s.split('=');
92 | (split.next().unwrap().to_string(), split.next().unwrap().parse().unwrap())
93 | })
94 | .collect();
95 |
96 | let file_categories: HashMap = std::env::var("TUX_FILE_LOG")
97 | .unwrap_or_else(|_| "tuxphones=3".to_string())
98 | .split(',')
99 | .map(|s| {
100 | let mut split = s.split('=');
101 | (split.next().unwrap().to_string(), split.next().unwrap().parse().unwrap())
102 | })
103 | .collect();
104 |
105 | let mut file_subscribers = Vec::new();
106 |
107 | if !file_categories.is_empty() {
108 | let file_path = std::env::var("TUX_FILE_PATH").unwrap_or_else(|_| "/tmp/tuxphones-{date}-{time}-{pid}".to_string());
109 |
110 | //replace {date}, {time}, {pid} in the file path with the current date, time, process
111 | let file_path = file_path
112 | .replace("{date}", &chrono::Local::now().format("%Y:%m:%d").to_string())
113 | .replace("{time}", &chrono::Local::now().format("%H:%M:%S").to_string())
114 | .replace("{pid}", &process::id().to_string());
115 |
116 | //Create the folder file_path and if already exist add a -1, -2, -3, etc to the end of the folder name
117 | let mut file_path = std::path::PathBuf::from(file_path);
118 | let mut i = 0;
119 | while file_path.exists() {
120 | i += 1;
121 | file_path = file_path.with_file_name(format!("{}-{}", file_path.file_name().unwrap().to_str().unwrap(), i));
122 | }
123 |
124 | match fs::create_dir_all(&file_path) {
125 | Ok(_) => {
126 | if std::env::var("TUX_OPEN_LOG_ON_START").unwrap_or_else(|_| "false".to_string()).parse::().unwrap() {
127 | match process::Command::new("xdg-open").arg(&file_path).spawn() {
128 | Ok(_) => {},
129 | Err(e) => {
130 | eprintln!("Failed to open folder! {}", e);
131 | }
132 | }
133 | }
134 | //For each file_category create a file and a tracing_subscriber for it
135 | for (category, level) in file_categories {
136 | let file = fs::File::create(format!("{}/{}.log", file_path.to_str().unwrap(), category)).unwrap();
137 | //TODO: Figure out why the non_blocking wrapper doesn't work
138 | //let (non_blocking, _guard) = tracing_appender::non_blocking(file);
139 | let file_log = tracing_subscriber::fmt::layer()
140 | .with_ansi(false)
141 | .with_writer(file)
142 | .with_target(false)
143 | .with_filter(filter::filter_fn(move |meta| {
144 | meta.target() == category && meta.level() <= &Level::from_str(&level.to_string()).unwrap()
145 | }))
146 | .boxed();
147 |
148 | file_subscribers.push(file_log);
149 | }
150 | }
151 | Err(_) => {
152 | eprintln!("Failed to create folder {}, file logging disabled", file_path.to_str().unwrap());
153 | }
154 | };
155 | }
156 |
157 | // Stdout logging
158 | let stdout_log = tracing_subscriber::fmt::layer().pretty();
159 |
160 | let subscriber = tracing_subscriber::registry()
161 | .with(stdout_log
162 | .with_filter(filter::filter_fn(move |meta| {
163 | console_categories.get(meta.target()).map(|level| {
164 | meta.level() <= &Level::from_str(&level.to_string()).unwrap()
165 | }).unwrap_or(false)
166 | }))
167 | )
168 | .with(file_subscribers);
169 | error!("Logging initialized");
170 |
171 | match tracing::subscriber::set_global_default(subscriber) {
172 | Ok(_) => {}
173 | Err(e) => {
174 | eprintln!("Failed to set global logging default subscriber: {}", e);
175 | }
176 | }
177 |
178 | LogTracer::init().unwrap();
179 | }
--------------------------------------------------------------------------------
/daemon/src/pulse.rs:
--------------------------------------------------------------------------------
1 | use std::{cell::RefCell, ops::Deref, sync::Arc};
2 |
3 | use crate::pid;
4 | use libpulse_binding::{
5 | callbacks::ListResult,
6 | context::{Context, FlagSet as ContextFlagSet, State},
7 | mainloop::threaded::Mainloop,
8 | operation::Operation,
9 | };
10 |
11 | pub struct PulseHandle {
12 | mainloop: Arc>,
13 | context: Arc>,
14 | audio_is_setup: bool,
15 | tuxphones_sink_module_index: Option,
16 | combined_sink_index: Option,
17 | combined_sink_module_index: Option,
18 | current_app_info: Option,
19 | }
20 |
21 | unsafe impl Send for PulseHandle {}
22 |
23 | struct CurrentAppInfo {
24 | sink_input_restore_index: u32,
25 | index: u32,
26 | }
27 |
28 | pub struct BasicSinkInfo {
29 | pub name: String,
30 | pub index: u32,
31 | module: Option,
32 | }
33 |
34 | #[derive(Debug)]
35 | pub struct AudioApplication {
36 | pub name: String,
37 | pub pid: pid,
38 | pub index: u32,
39 | pub sink_index: u32,
40 | }
41 |
42 | #[derive(Debug)]
43 | pub enum PulseInitializationError {
44 | NoAlloc,
45 | LoopStartErr(i32),
46 | ContextConnectErr(i32),
47 | ContextStateErr,
48 | }
49 |
50 | impl std::fmt::Display for PulseInitializationError {
51 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 | let str = match self {
53 | PulseInitializationError::NoAlloc => "Unable to allocate".to_string(),
54 | PulseInitializationError::LoopStartErr(code) => format!("Loop start error: {}", code),
55 | PulseInitializationError::ContextConnectErr(code) => {
56 | format!("Context connection error: {}", code)
57 | }
58 | PulseInitializationError::ContextStateErr => "Context state error".to_string(),
59 | };
60 | f.write_str(&str)
61 | }
62 | }
63 |
64 | #[derive(Debug)]
65 | pub enum PulseCaptureSetupError {
66 | NoPassthrough,
67 | NoDefaultSink,
68 | }
69 |
70 | impl std::fmt::Display for PulseCaptureSetupError {
71 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 | f.write_str(match self {
73 | PulseCaptureSetupError::NoPassthrough => "No passthrough sink found",
74 | PulseCaptureSetupError::NoDefaultSink => "No default sink found",
75 | })
76 | }
77 | }
78 |
79 | #[derive(Debug)]
80 | pub enum PulseCaptureError {
81 | NotSetup,
82 | NoAppWithPid,
83 | }
84 |
85 | impl std::fmt::Display for PulseCaptureError {
86 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 | f.write_str(match self {
88 | PulseCaptureError::NotSetup => "Capture not setup",
89 | PulseCaptureError::NoAppWithPid => "No app with given PID found",
90 | })
91 | }
92 | }
93 |
94 | impl Drop for PulseHandle {
95 | fn drop(&mut self) {
96 | self.stop_capture();
97 | if self.audio_is_setup {
98 | self.teardown_audio_capture();
99 | }
100 |
101 | self.mainloop.borrow_mut().lock();
102 | self.context.borrow_mut().disconnect();
103 | self.mainloop.borrow_mut().unlock();
104 | self.mainloop.borrow_mut().stop();
105 | }
106 | }
107 |
108 | impl PulseHandle {
109 | /// Creates a new Pulse handle
110 | pub fn new() -> Result {
111 | let mainloop = Arc::new(RefCell::new(match Mainloop::new() {
112 | Some(l) => l,
113 | None => return Err(PulseInitializationError::NoAlloc),
114 | }));
115 |
116 | match mainloop.borrow_mut().start() {
117 | Ok(_) => {}
118 | Err(e) => return Err(PulseInitializationError::LoopStartErr(e.0)),
119 | }
120 |
121 | // Lock mainloop to create context
122 | mainloop.borrow_mut().lock();
123 |
124 | let context = Arc::new(RefCell::new(
125 | match Context::new(mainloop.borrow_mut().deref(), "tuxphones") {
126 | Some(c) => c,
127 | None => {
128 | mainloop.borrow_mut().unlock();
129 | mainloop.borrow_mut().stop();
130 | return Err(PulseInitializationError::NoAlloc);
131 | }
132 | },
133 | ));
134 |
135 | // State callback to wait for connection
136 | {
137 | let ml_ref = Arc::clone(&mainloop);
138 | let context_ref = Arc::clone(&context);
139 | context
140 | .borrow_mut()
141 | .set_state_callback(Some(Box::new(move || {
142 | // Needs to be unsafe to be able to borrow mutably multiple times
143 | match unsafe { (*context_ref.as_ptr()).get_state() } {
144 | State::Ready | State::Failed | State::Terminated => unsafe {
145 | (*ml_ref.as_ptr()).signal(false);
146 | },
147 | _ => {}
148 | }
149 | })));
150 | }
151 |
152 | match context
153 | .borrow_mut()
154 | .connect(None, ContextFlagSet::NOFLAGS, None)
155 | {
156 | Ok(_) => {}
157 | Err(e) => {
158 | mainloop.borrow_mut().unlock();
159 | mainloop.borrow_mut().stop();
160 | return Err(PulseInitializationError::ContextConnectErr(e.0));
161 | }
162 | }
163 |
164 | loop {
165 | match context.borrow_mut().get_state() {
166 | State::Ready => break,
167 | State::Failed | State::Terminated => {
168 | mainloop.borrow_mut().unlock();
169 | mainloop.borrow_mut().stop();
170 | return Err(PulseInitializationError::ContextStateErr);
171 | }
172 | _ => mainloop.borrow_mut().wait(),
173 | }
174 | }
175 | context.borrow_mut().set_state_callback(None);
176 |
177 | mainloop.borrow_mut().unlock();
178 |
179 | Ok(PulseHandle {
180 | context: Arc::clone(&context),
181 | mainloop: Arc::clone(&mainloop),
182 | audio_is_setup: false,
183 | tuxphones_sink_module_index: None,
184 | combined_sink_index: None,
185 | combined_sink_module_index: None,
186 | current_app_info: None,
187 | })
188 | }
189 |
190 | /// Gets the sinks connected to the Pulse server
191 | pub fn get_sinks(&mut self) -> Vec {
192 | self.mainloop.borrow_mut().lock();
193 | let results = Arc::new(RefCell::new(Some(vec![])));
194 |
195 | let ml_ref = Arc::clone(&self.mainloop);
196 | let results_ref = Arc::clone(&results);
197 | let op = self
198 | .context
199 | .borrow_mut()
200 | .introspect()
201 | .get_sink_info_list(move |res| match res {
202 | ListResult::Item(info) => {
203 | results_ref
204 | .borrow_mut()
205 | .as_mut()
206 | .unwrap()
207 | .push(BasicSinkInfo {
208 | name: info
209 | .name
210 | .as_ref()
211 | .map_or(String::from("unknown name"), |n| n.to_string()),
212 | index: info.index,
213 | module: info.owner_module,
214 | })
215 | }
216 | ListResult::End | ListResult::Error => unsafe {
217 | (*ml_ref.as_ptr()).signal(false);
218 | },
219 | });
220 |
221 | op_wait(&mut self.mainloop.borrow_mut(), &op);
222 |
223 | self.mainloop.borrow_mut().unlock();
224 |
225 | let res = results.borrow_mut().take().unwrap();
226 | res
227 | }
228 |
229 | /// Gets all applications that are producing audio
230 | pub fn get_audio_applications(&mut self) -> Vec {
231 | self.mainloop.borrow_mut().lock();
232 |
233 | let results = Arc::new(RefCell::new(Some(vec![])));
234 |
235 | let ml_ref = Arc::clone(&self.mainloop);
236 | let results_ref = Arc::clone(&results);
237 | let op = self
238 | .context
239 | .borrow_mut()
240 | .introspect()
241 | .get_sink_input_info_list(move |res| match res {
242 | ListResult::Item(info) => {
243 | if let Some(pid) = info.proplist.get_str("application.process.id") {
244 | results_ref
245 | .borrow_mut()
246 | .as_mut()
247 | .unwrap()
248 | .push(AudioApplication {
249 | name: info
250 | .proplist
251 | .get_str("application.name")
252 | .unwrap_or_else(|| "NONAME".to_string()),
253 | pid: pid.parse().unwrap(),
254 | index: info.index,
255 | sink_index: info.sink,
256 | });
257 | }
258 | }
259 | ListResult::End | ListResult::Error => unsafe {
260 | (*ml_ref.as_ptr()).signal(false);
261 | },
262 | });
263 |
264 | op_wait(&mut self.mainloop.borrow_mut(), &op);
265 |
266 | self.mainloop.borrow_mut().unlock();
267 |
268 | let res = results.borrow_mut().take().unwrap();
269 | res
270 | }
271 |
272 | /// Adds sinks for audio capture
273 | pub fn setup_audio_capture(
274 | &mut self,
275 | passthrough_override: Option<&str>,
276 | ) -> Result<(), PulseCaptureSetupError> {
277 | // Don't do the same thing twice
278 | if self.audio_is_setup {
279 | return Ok(());
280 | }
281 |
282 | let passthrough_sink = match passthrough_override {
283 | Some(s) => s.to_string(),
284 | None => {
285 | self.mainloop.borrow_mut().lock();
286 | let result: Arc>> = Arc::new(RefCell::new(None));
287 | let ml_ref = Arc::clone(&self.mainloop);
288 | let res_ref = Arc::clone(&result);
289 | let op = self
290 | .context
291 | .borrow_mut()
292 | .introspect()
293 | .get_sink_info_by_name("@DEFAULT_SINK@", move |info| unsafe {
294 | match info {
295 | ListResult::Item(sink) => res_ref.borrow_mut().replace(
296 | sink.name
297 | .as_ref()
298 | .map_or(String::from("unknown name"), |n| n.to_string()),
299 | ),
300 | ListResult::End | ListResult::Error => None,
301 | };
302 |
303 | (*ml_ref.as_ptr()).signal(false);
304 | });
305 |
306 | op_wait(&mut self.mainloop.borrow_mut(), &op);
307 | self.mainloop.borrow_mut().unlock();
308 |
309 | let res = match result.borrow_mut().take() {
310 | Some(r) => r,
311 | None => return Err(PulseCaptureSetupError::NoDefaultSink),
312 | };
313 | res
314 | }
315 | };
316 |
317 | let mut tux_sink_found = false;
318 | let mut tux_combined_sink_found = false;
319 | let mut passthrough_sink_found = false;
320 |
321 | for sink in self.get_sinks() {
322 | match &sink.name[..] {
323 | "tuxphones" => tux_sink_found = true,
324 | "tuxphones-combined" => tux_combined_sink_found = true,
325 | n if n == passthrough_sink => passthrough_sink_found = true,
326 | _ => {}
327 | }
328 | }
329 |
330 | if !passthrough_sink_found {
331 | return Err(PulseCaptureSetupError::NoPassthrough);
332 | }
333 |
334 | self.mainloop.borrow_mut().lock();
335 | if !tux_sink_found {
336 | let ml_ref = Arc::clone(&self.mainloop);
337 | let op = self.context.borrow_mut().introspect().load_module(
338 | "module-null-sink",
339 | "sink_name=tuxphones sink_properties=device.description=tuxphones",
340 | move |_| unsafe {
341 | (*ml_ref.as_ptr()).signal(false);
342 | },
343 | );
344 |
345 | op_wait(&mut self.mainloop.borrow_mut(), &op);
346 | }
347 |
348 | if !tux_combined_sink_found {
349 | let ml_ref = Arc::clone(&self.mainloop);
350 | // adjust_time=0 prevents a crash for some reason
351 | let op = self.context.borrow_mut().introspect().load_module(
352 | "module-combine-sink",
353 | &format!("sink_name=tuxphones-combined sink_properties=device.description=tuxphones-combined adjust_time=0 slaves=tuxphones,{}", passthrough_sink),
354 | move |_| unsafe {
355 | (*ml_ref.as_ptr()).signal(false);
356 | }
357 | );
358 |
359 | op_wait(&mut self.mainloop.borrow_mut(), &op);
360 | }
361 |
362 | self.mainloop.borrow_mut().unlock();
363 |
364 | for sink in self.get_sinks() {
365 | match &sink.name[..] {
366 | "tuxphones" => self.tuxphones_sink_module_index = Some(sink.module.unwrap()),
367 | "tuxphones-combined" => {
368 | self.combined_sink_module_index = Some(sink.module.unwrap());
369 | self.combined_sink_index = Some(sink.index);
370 | }
371 | _ => {}
372 | }
373 | }
374 |
375 | self.audio_is_setup = true;
376 | Ok(())
377 | }
378 |
379 | /// Removes audio capture sinks
380 | pub fn teardown_audio_capture(&mut self) {
381 | if !self.audio_is_setup {
382 | return;
383 | }
384 |
385 | self.audio_is_setup = false;
386 |
387 | self.mainloop.borrow_mut().lock();
388 |
389 | if let Some(idx) = self.tuxphones_sink_module_index {
390 | self.unload_module(idx);
391 | }
392 |
393 | if let Some(idx) = self.combined_sink_module_index {
394 | self.unload_module(idx);
395 | }
396 |
397 | self.tuxphones_sink_module_index = None;
398 | self.combined_sink_index = None;
399 | self.combined_sink_module_index = None;
400 |
401 | self.mainloop.borrow_mut().unlock();
402 | }
403 |
404 | /// Unloads modules
405 | fn unload_module(&mut self, idx: u32) {
406 | let ml_ref = Arc::clone(&self.mainloop);
407 | let op = self
408 | .context
409 | .borrow_mut()
410 | .introspect()
411 | .unload_module(idx, move |_| unsafe {
412 | (*ml_ref.as_ptr()).signal(false);
413 | });
414 |
415 | op_wait(&mut self.mainloop.borrow_mut(), &op);
416 | }
417 |
418 | /// Starts capturing audio from the application with the given Pulse PID
419 | pub fn start_capture(&mut self, pid: pid) -> Result<(), PulseCaptureError> {
420 | if !self.audio_is_setup || self.combined_sink_module_index.is_none() {
421 | return Err(PulseCaptureError::NotSetup);
422 | }
423 |
424 | for app in self.get_audio_applications() {
425 | if app.pid == pid {
426 | self.mainloop.borrow_mut().lock();
427 |
428 | let ml_ref = Arc::clone(&self.mainloop);
429 | self.current_app_info = Some(CurrentAppInfo {
430 | sink_input_restore_index: app.sink_index,
431 | index: app.index,
432 | });
433 | let op = self
434 | .context
435 | .borrow_mut()
436 | .introspect()
437 | .move_sink_input_by_index(
438 | app.index,
439 | self.combined_sink_index.unwrap(),
440 | Some(Box::new(move |_| unsafe {
441 | (*ml_ref.as_ptr()).signal(false);
442 | })),
443 | );
444 |
445 | op_wait(&mut self.mainloop.borrow_mut(), &op);
446 |
447 | self.mainloop.borrow_mut().unlock();
448 |
449 | return Ok(());
450 | }
451 | }
452 |
453 | Err(PulseCaptureError::NoAppWithPid)
454 | }
455 |
456 | /// Stop capturing audio from application
457 | pub fn stop_capture(&mut self) {
458 | self.mainloop.borrow_mut().lock();
459 |
460 | if let Some(info) = &self.current_app_info {
461 | let ml_ref = Arc::clone(&self.mainloop);
462 | let op = self
463 | .context
464 | .borrow_mut()
465 | .introspect()
466 | .move_sink_input_by_index(
467 | info.index,
468 | info.sink_input_restore_index,
469 | Some(Box::new(move |_| unsafe {
470 | (*ml_ref.as_ptr()).signal(false);
471 | })),
472 | );
473 |
474 | op_wait(&mut self.mainloop.borrow_mut(), &op);
475 | }
476 |
477 | self.current_app_info = None;
478 | self.mainloop.borrow_mut().unlock();
479 | }
480 | }
481 |
482 | /// Wait for operation to complete
483 | fn op_wait(ml: &mut Mainloop, op: &Operation) {
484 | while op.get_state() == libpulse_binding::operation::State::Running {
485 | ml.wait()
486 | }
487 | }
488 |
--------------------------------------------------------------------------------
/daemon/src/socket.rs:
--------------------------------------------------------------------------------
1 | use crate::{pid, xid};
2 | use async_tungstenite::{
3 | tokio::{accept_async, TokioAdapter},
4 | tungstenite::{Error, Message},
5 | WebSocketStream,
6 | };
7 | use futures_util::{stream::SplitSink, SinkExt, StreamExt};
8 | use serde::{Deserialize, Serialize};
9 | use std::{collections::HashMap, fmt::Display, net::SocketAddr, sync::Arc};
10 | use tokio::{
11 | net::{TcpListener, TcpStream},
12 | sync::{mpsc, Mutex},
13 | task::{self, JoinHandle},
14 | };
15 | use tracing::{error, trace};
16 |
17 | type ConnectionsArc =
18 | Arc>, Message>>>>;
19 | type CommandSender = mpsc::Sender;
20 |
21 | /// Listens on a socket for commands
22 | pub struct WebSocket {
23 | thread: Option>,
24 | connections: ConnectionsArc,
25 | }
26 |
27 | /// Possible errors when creating a `SocketListener`
28 | #[derive(Debug, Clone)]
29 | pub enum SocketListenerCreationError {
30 | // Unable to bind WebSocket to the specified port
31 | UnableToBindPort(u16),
32 | }
33 |
34 | /// Holds information relating to stream resolution
35 | #[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
36 | #[serde(tag = "type")]
37 | pub struct StreamResolutionInformation {
38 | pub width: u16,
39 | pub height: u16,
40 | /// Whether or not the stream resolution can change
41 | pub is_fixed: bool,
42 | }
43 |
44 | /// Holds RTC ICE information
45 | #[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
46 | #[serde(tag = "type")]
47 | pub struct IceData {
48 | pub urls: Vec,
49 | pub username: String,
50 | pub credential: String,
51 | }
52 |
53 | /// Commands that can be received from the client plugin
54 | #[derive(Deserialize, Debug, PartialEq, Eq)]
55 | #[serde(tag = "type")]
56 | pub enum SocketListenerCommand {
57 | /// Starts a new soundshare stream
58 | StartStream {
59 | /// Pulse PID
60 | pid: pid,
61 | /// XID
62 | xid: xid,
63 | /// Target resolution
64 | resolution: StreamResolutionInformation,
65 | /// Target framerate
66 | framerate: u8,
67 | /// RTC Connection ID
68 | rtc_connection_id: String,
69 | /// Secret key for Sodium encryption
70 | secret_key: Vec,
71 | /// The associated voice chat's SSRC
72 | voice_ssrc: u32,
73 | /// The base SSRC for creating a stream, needs to be offset to get video (+1) and RTX (+2) SSRCs
74 | base_ssrc: u32,
75 | ip: String,
76 | port: u16,
77 | },
78 | /// Stops the currently-running stream
79 | StopStream,
80 | /// Internal stop stream command, notifies client plugin
81 | StopStreamInternal,
82 | /// Gets info on which windows can have sound captured
83 | GetInfo {
84 | /// XIDs available to Discord
85 | xids: Vec,
86 | },
87 | }
88 |
89 | #[derive(Serialize)]
90 | #[serde(tag = "type")]
91 | struct ApplicationList<'a> {
92 | apps: &'a Vec,
93 | }
94 |
95 | #[derive(Serialize, Debug)]
96 | #[serde(tag = "type")]
97 | pub struct Application {
98 | pub name: String,
99 | pub pid: pid,
100 | pub xid: xid,
101 | }
102 |
103 | #[derive(Serialize, Debug)]
104 | #[serde(tag = "type")]
105 | pub struct StreamStop {}
106 |
107 | #[derive(Serialize, Debug)]
108 | #[serde(tag = "type")]
109 | pub struct StreamPreview {
110 | jpg: String,
111 | }
112 |
113 | impl Display for SocketListenerCreationError {
114 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 | match self {
116 | Self::UnableToBindPort(port) => {
117 | f.write_str(&format!("Unable to bind to localhost on port {}", port))
118 | }
119 | }
120 | }
121 | }
122 |
123 | impl WebSocket {
124 | pub async fn new(
125 | port: u16,
126 | sender: CommandSender,
127 | ) -> Result {
128 | let connections: ConnectionsArc = Arc::new(Mutex::new(HashMap::new()));
129 |
130 | let listener = match TcpListener::bind(format!("127.0.0.1:{}", port)).await {
131 | Ok(l) => l,
132 | Err(_) => return Err(SocketListenerCreationError::UnableToBindPort(port)),
133 | };
134 |
135 | // Spawn listener thread to check for commands sent to the socket
136 | let conn_arc = connections.clone();
137 | let thread = task::spawn(async move {
138 | while let Ok((stream, _)) = listener.accept().await {
139 | tokio::spawn(Self::accept_connection(
140 | stream,
141 | conn_arc.clone(),
142 | sender.clone(),
143 | ));
144 | }
145 | });
146 |
147 | Ok(WebSocket {
148 | thread: Some(thread),
149 | connections,
150 | })
151 | }
152 |
153 | async fn send(&self, data: &T) -> Result<(), Error>
154 | where
155 | T: ?Sized + Serialize,
156 | {
157 | let mut conn = self.connections.lock().await;
158 | let mut to_remove = Vec::new();
159 | for (addr, stream) in conn.iter_mut() {
160 | match stream
161 | .send(Message::Text(serde_json::to_string(data).unwrap()))
162 | .await
163 | {
164 | Ok(()) => {}
165 | Err(e) => match e {
166 | Error::ConnectionClosed => {
167 | to_remove.push(*addr);
168 | }
169 | _ => return Err(e),
170 | },
171 | }
172 | }
173 |
174 | for rm in to_remove {
175 | conn.remove(&rm);
176 | }
177 |
178 | Ok(())
179 | }
180 |
181 | pub async fn application_info(&self, apps: &Vec) -> Result<(), Error> {
182 | self.send(&ApplicationList { apps }).await
183 | }
184 |
185 | pub async fn stream_stop_internal(&self) -> Result<(), Error> {
186 | self.send(&StreamStop {}).await
187 | }
188 |
189 | pub async fn stream_preview(&self, data: &Vec) -> Result<(), Error> {
190 | self.send(&StreamPreview {
191 | jpg: base64::encode(data),
192 | })
193 | .await
194 | }
195 |
196 | /// Kills the WebSocket thread and closes everything up
197 | pub async fn abort(&mut self) {
198 | if let Some(thread) = self.thread.take() {
199 | thread.abort();
200 | }
201 | }
202 |
203 | async fn handle_connection(
204 | stream: TcpStream,
205 | connections: ConnectionsArc,
206 | sender: CommandSender,
207 | ) -> Result<(), Error> {
208 | let addr = stream.peer_addr().unwrap();
209 |
210 | let ws_stream = match accept_async(stream).await {
211 | Ok(s) => s,
212 | Err(e) => {
213 | error!("{}", e.to_string());
214 | return Ok(());
215 | }
216 | };
217 |
218 | let (write, mut read) = ws_stream.split();
219 |
220 | connections.lock().await.insert(addr, write);
221 |
222 | while let Some(msg) = read.next().await {
223 | let msg = msg?;
224 | if msg.is_text() {
225 | trace!("Received command: {}", msg.to_text().unwrap());
226 |
227 | match serde_json::from_str::(msg.to_text().unwrap()) {
228 | Ok(cmd) => match sender.send(cmd).await {
229 | Ok(_) => {}
230 | Err(e) => error!("Failed to send command: {}", e),
231 | },
232 | Err(e) => error!("Failed to deserialize command: {}", e),
233 | }
234 | }
235 | }
236 |
237 | Ok(())
238 | }
239 |
240 | async fn accept_connection(
241 | stream: TcpStream,
242 | connections: ConnectionsArc,
243 | sender: CommandSender,
244 | ) {
245 | let addr = stream.peer_addr().unwrap();
246 | if let Err(e) = Self::handle_connection(stream, connections.clone(), sender).await {
247 | match e {
248 | Error::ConnectionClosed | Error::Protocol(_) | Error::Utf8 => {
249 | connections.lock().await.remove(&addr);
250 | }
251 | err => error!("Error processing connection: {}", err),
252 | }
253 | }
254 | }
255 | }
256 |
--------------------------------------------------------------------------------
/daemon/src/x.rs:
--------------------------------------------------------------------------------
1 | use std::{
2 | io::Cursor,
3 | };
4 |
5 | // use async_std::{channel::Sender, task};
6 | use crate::{pid, xid};
7 | use image::ImageBuffer;
8 | use sysinfo::{PidExt, ProcessExt, SystemExt};
9 | use xcb::{
10 | res::{ClientIdMask, ClientIdSpec, QueryClientIds},
11 | x::{
12 | self, GetGeometry, GetImage,
13 | },
14 | };
15 |
16 | pub struct XServerHandle {
17 | connection: xcb::Connection,
18 | /// List of PIDs that are related to Xorg
19 | xorg_procs: Vec,
20 | }
21 |
22 | #[derive(PartialEq, Eq, Clone, Copy)]
23 | pub struct Size {
24 | pub width: u16,
25 | pub height: u16,
26 | }
27 |
28 | impl XServerHandle {
29 | pub fn new() -> Result {
30 | // Connect to the server
31 | let (conn, _) = xcb::Connection::connect(None)?;
32 |
33 | // Get the current Xorg process to make sure XServer isn't falsely recognizing windows (cached)
34 | let mut system = sysinfo::System::new();
35 | system.refresh_processes();
36 | let xorg_procs = system
37 | .processes_by_name("Xorg")
38 | .map(|p| p.pid().as_u32())
39 | .collect();
40 |
41 | Ok(XServerHandle {
42 | connection: conn,
43 | /*cache: HashMap::new(), last_cache_wipe: None,*/ xorg_procs,
44 | })
45 | }
46 |
47 | /// Attempts to derive a PID from an XID
48 | pub fn pid_from_xid(&self, xid: xid) -> Result