Initial commit
This commit is contained in:
commit
fd68d9f463
10 changed files with 662 additions and 0 deletions
53
recorder/README.md
Normal file
53
recorder/README.md
Normal file
|
@ -0,0 +1,53 @@
|
|||
# Niri Recorder
|
||||
|
||||
Niri recorder is a shell script and waybar module to for creating screen
|
||||
recordings meant to be easily shared online.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- `socat`
|
||||
- `bash`
|
||||
- `wf-recorder`
|
||||
- `niri`
|
||||
- `jq`
|
||||
- `ffmpeg`
|
||||
- `slurp`
|
||||
- `python3` (optional)
|
||||
- `waybar` (optional)
|
||||
- `wl-clipboard`
|
||||
|
||||
## Recorder
|
||||
|
||||
A utility script meant to be triggered via keybinding. It will record your
|
||||
screen, then automatically compress the recording and copy its URI path to your
|
||||
clipboard, so you can easily paste it inside applications like Discord and
|
||||
Matrix. You do not need the Waybar module to use the script.
|
||||
|
||||
**Usage**:
|
||||
|
||||
`recorder.sh`
|
||||
|
||||
1. Run the script once to begin recording. Arguments:
|
||||
|
||||
- `screen` \[default]: record entire screen. Select screen if multi-monitor.
|
||||
- `region`: Select a region to record.
|
||||
|
||||
2. Run the script again to stop recording.
|
||||
|
||||
- Arguments don't matter this time.
|
||||
- Compression and copying to clipboard is done automatically.
|
||||
|
||||
## Waybar Module
|
||||
|
||||
There is an included waybar module. This module shows the current recording
|
||||
state. You can also use it to start/stop the recording with your mouse. Please
|
||||
see `recorder_config.jsonc` for an example of how to setup the custom module.
|
||||
|
||||
You can also use `test_waybar.sh` to test the module without creating any
|
||||
recordings. After you've loaded the Waybar module, simply run the script, and
|
||||
you should see the module responding to socket messages.
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
- Thanks to [Axlefublr](https://axlefublr.github.io/screen-recording/) for the
|
||||
method to optimize the compression of the video.
|
9
recorder/module_config.jsonc
Normal file
9
recorder/module_config.jsonc
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"custom/recorder": {
|
||||
"exec": "/path/to/recorder.py",
|
||||
"return-type": "json",
|
||||
"restart-interval": "never",
|
||||
"on-click": "/path/to/recorder.sh screen",
|
||||
"on-click-right": "/path/to/recorder.sh region"
|
||||
}
|
||||
}
|
107
recorder/recorder.py
Executable file
107
recorder/recorder.py
Executable file
|
@ -0,0 +1,107 @@
|
|||
#!/bin/env python
|
||||
|
||||
import os
|
||||
import socket
|
||||
import json
|
||||
import asyncio
|
||||
|
||||
# helper prints dict as json string
|
||||
def p(obj):
|
||||
print(json.dumps(obj), flush=True)
|
||||
|
||||
SOCKET = "/tmp/recorder-sock.sock"
|
||||
# default tooltip
|
||||
DEF_TT = "Click to record screen. Right-click to record region."
|
||||
# default text
|
||||
DEF_TEXT = "rec"
|
||||
|
||||
# Remove socket already exists
|
||||
try:
|
||||
os.unlink(SOCKET)
|
||||
except OSError:
|
||||
# doesn't exist, we're good
|
||||
pass
|
||||
|
||||
# track singleton delayed task
|
||||
delayed_task = None
|
||||
|
||||
# async function to send a message with delay
|
||||
async def delayed_msg(delay, message):
|
||||
try:
|
||||
await asyncio.sleep(delay)
|
||||
p(message)
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
# function handles message from socket
|
||||
def handle_message(data: str, loop):
|
||||
global delayed_task
|
||||
# always cancel delayed task
|
||||
# if there's new message
|
||||
if delayed_task:
|
||||
delayed_task.cancel()
|
||||
out = {}
|
||||
out_s = ""
|
||||
out_t = ""
|
||||
# process the message type
|
||||
if data:
|
||||
if data == "REC":
|
||||
out_s = "on"
|
||||
out_t = "Recording in progress. Click to stop."
|
||||
elif data == "CMP":
|
||||
out_s = "compressing"
|
||||
out_t = "Recording is being compressed."
|
||||
elif data == "CPD":
|
||||
out_s = "copied"
|
||||
out_t = "Recording has been copied to clipboard."
|
||||
elif data == "STP":
|
||||
out_s = "done"
|
||||
out_t = "Recording has been stopped."
|
||||
elif data == "ERR":
|
||||
out_s = "error"
|
||||
out_t = "Recording has encountered an error."
|
||||
# format the output
|
||||
out["text"] = f"rec: {out_s}" if out_s != "" else DEF_TEXT
|
||||
out["tooltip"] = out_t if out_t != "" else DEF_TT
|
||||
# print to waybar
|
||||
p(out)
|
||||
|
||||
# check if delayed message should be sent afterwards
|
||||
if data in ["ERR", "CPD", "STP"]:
|
||||
# probably redundant but... for good measure lol
|
||||
if delayed_task:
|
||||
delayed_task.cancel()
|
||||
# delayed print of default output
|
||||
delayed_out = {"text": DEF_TEXT, "tooltip": DEF_TT}
|
||||
delayed_task = loop.create_task(delayed_msg(5, delayed_out))
|
||||
|
||||
# start the server
|
||||
async def server():
|
||||
# pointer to this event loop
|
||||
loop = asyncio.get_running_loop()
|
||||
# open our socket
|
||||
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as server:
|
||||
server.bind(SOCKET)
|
||||
# needed for async
|
||||
server.setblocking(False)
|
||||
# only one connection at a time
|
||||
server.listen(1)
|
||||
# main loop; runs until waybar exits
|
||||
while True:
|
||||
# receive data from socket
|
||||
conn, _ = await loop.sock_accept(server)
|
||||
with conn:
|
||||
# parse string
|
||||
data = (await loop.sock_recv(conn, 1024)).decode().strip()
|
||||
# handle the data
|
||||
handle_message(data, loop)
|
||||
|
||||
# main function
|
||||
async def main():
|
||||
# start by outputing default contents
|
||||
p({"text": DEF_TEXT, "tooltip": DEF_TT})
|
||||
# start the server
|
||||
await server()
|
||||
|
||||
# entry point
|
||||
asyncio.run(main())
|
69
recorder/recorder.sh
Executable file
69
recorder/recorder.sh
Executable file
|
@ -0,0 +1,69 @@
|
|||
#!/bin/env bash
|
||||
|
||||
# depends: wf-recorder, libnotify
|
||||
|
||||
TEMPDIR="/tmp/niri-recorder"
|
||||
mkdir -p "$TEMPDIR"
|
||||
LOCK="$TEMPDIR/lock"
|
||||
RFMT=".mkv"
|
||||
OFMT=".mp4"
|
||||
RAW="$TEMPDIR/raw$RFMT"
|
||||
OUTDIR="$HOME/Videos/niri-recorder"
|
||||
mkdir -p "$OUTDIR"
|
||||
|
||||
# sends current recording state to socket
|
||||
function sig {
|
||||
echo "$1" | socat - UNIX-CONNECT:/tmp/recorder-sock.sock
|
||||
}
|
||||
|
||||
# function generates unique name for recording
|
||||
function compname {
|
||||
now=$(date +"%y-%m-%d-%H:%M:%S")
|
||||
echo "$OUTDIR/$now$OFMT"
|
||||
}
|
||||
|
||||
# First we need to check if the recording
|
||||
# lockfile exists.
|
||||
if [[ -f "$LOCK" ]]; then
|
||||
# Stop the recording
|
||||
sig "STP"
|
||||
kill "$(cat "$LOCK")"
|
||||
# remove lockfile
|
||||
rm "$LOCK"
|
||||
outpath="$(compname)"
|
||||
sig "CMP"
|
||||
# compress the recording
|
||||
ffmpeg -y -i "$RAW" -c:v libx264 -preset slow -crf 21 -r 30 -b:v 2M -maxrate 3M -bufsize 4M -c:a aac -b:a 96k -movflags +faststart "$outpath" || (sig "ERR"; exit 1)
|
||||
# copy URI path to clipboard
|
||||
wl-copy -t 'text/uri-list' <<<"file://$outpath" || (sig "ERR"; exit 1)
|
||||
sig "CPD"
|
||||
# delete the raw recording
|
||||
rm "$RAW"
|
||||
|
||||
else
|
||||
# count how many monitors are attached
|
||||
num_mon=$(niri msg --json outputs | jq 'keys | length')
|
||||
wf_flags="-Dyf"
|
||||
if [ "$1" = "region" ];then
|
||||
# select a screen region
|
||||
sel=$(slurp) || exit 1
|
||||
wf-recorder -g "$sel" "$wf_flags" "$RAW" &
|
||||
elif [ "$1" = "screen" ] || [ "$1" = "" ] && (( num_mon > 1 )); then
|
||||
# select entire screen
|
||||
sel=$(slurp -o) || exit 1
|
||||
wf-recorder -g "$sel" "$wf_flags" "$RAW" &
|
||||
else
|
||||
# this runs when screen is specified and there's only one monitor
|
||||
# it also runs with no args bc screen is default
|
||||
wf-recorder "$wf_flags" "$RAW" &
|
||||
fi
|
||||
|
||||
sig "REC"
|
||||
# create lockfile
|
||||
touch "$LOCK"
|
||||
|
||||
# save recorder's process id to lockfile
|
||||
PID=$!
|
||||
echo "$PID" > "$LOCK"
|
||||
fi
|
||||
|
14
recorder/test-waybar.sh
Executable file
14
recorder/test-waybar.sh
Executable file
|
@ -0,0 +1,14 @@
|
|||
#!/bin/env bash
|
||||
|
||||
echo "REC" | socat - UNIX-CONNECT:/tmp/recorder-sock.sock; \
|
||||
sleep 2; \
|
||||
echo "ERR" | socat - UNIX-CONNECT:/tmp/recorder-sock.sock; \
|
||||
sleep 2; \
|
||||
echo "REC" | socat - UNIX-CONNECT:/tmp/recorder-sock.sock; \
|
||||
sleep 2; \
|
||||
echo "STP" | socat - UNIX-CONNECT:/tmp/recorder-sock.sock; \
|
||||
sleep 2; \
|
||||
echo "CMP" | socat - UNIX-CONNECT:/tmp/recorder-sock.sock; \
|
||||
sleep 2; \
|
||||
echo "CPD" | socat - UNIX-CONNECT:/tmp/recorder-sock.sock; \
|
||||
sleep 2;
|
Loading…
Add table
Add a link
Reference in a new issue