mirror of
https://github.com/end-4/dots-hyprland.git
synced 2026-06-06 07:19:27 -05:00
use quickshell region selector for recording
This commit is contained in:
@@ -73,9 +73,10 @@ bindd = Super+Shift, C, Color picker, exec, hyprpicker -a # Pick color (Hex) >>
|
||||
bindld = ,Print, Screenshot >> clipboard ,exec,grim - | wl-copy # Screenshot >> clipboard
|
||||
bindld = Ctrl,Print, Screenshot >> clipboard & save, exec, mkdir -p $(xdg-user-dir PICTURES)/Screenshots && grim $(xdg-user-dir PICTURES)/Screenshots/Screenshot_"$(date '+%Y-%m-%d_%H.%M.%S')".png # Screenshot >> clipboard & file
|
||||
# Recording stuff
|
||||
bindl = Super+Alt, R, exec, ~/.config/hypr/hyprland/scripts/record.sh # Record region (no sound)
|
||||
bindl = Ctrl+Alt, R, exec, ~/.config/hypr/hyprland/scripts/record.sh --fullscreen # [hidden] Record screen (no sound)
|
||||
bindl = Super+Shift+Alt, R, exec, ~/.config/hypr/hyprland/scripts/record.sh --fullscreen-sound # Record screen (with sound)
|
||||
bindl = Super+Alt, R, global, quickshell:regionRecord # Record region (no sound)
|
||||
bindl = Super+Alt, R, exec, qs -c $qsConfig ipc call TEST_ALIVE || ~/.config/quickshell/$qsConfig/scripts/videos/record.sh # [hidden] Record region (no sound) (fallback)
|
||||
bindl = Ctrl+Alt, R, exec, ~/.config/quickshell/$qsConfig/scripts/videos/record.sh --fullscreen # [hidden] Record screen (no sound)
|
||||
bindl = Super+Shift+Alt, R, exec, ~/.config/quickshell/$qsConfig/scripts/videos/record.sh --fullscreen --sound # Record screen (with sound)
|
||||
# AI
|
||||
bindd = Super+Shift+Alt, mouse:273, Generate AI summary for selected text, exec, ~/.config/hypr/hyprland/scripts/ai/primary-buffer-query.sh # AI summary for selected text
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
getdate() {
|
||||
date '+%Y-%m-%d_%H.%M.%S'
|
||||
}
|
||||
getaudiooutput() {
|
||||
pactl list sources | grep 'Name' | grep 'monitor' | cut -d ' ' -f2
|
||||
}
|
||||
getactivemonitor() {
|
||||
hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name'
|
||||
}
|
||||
|
||||
xdgvideo="$(xdg-user-dir VIDEOS)"
|
||||
if [[ $xdgvideo = "$HOME" ]]; then
|
||||
unset xdgvideo
|
||||
fi
|
||||
mkdir -p "${xdgvideo:-$HOME/Videos}"
|
||||
cd "${xdgvideo:-$HOME/Videos}" || exit
|
||||
|
||||
if pgrep wf-recorder > /dev/null; then
|
||||
notify-send "Recording Stopped" "Stopped" -a 'Recorder' &
|
||||
pkill wf-recorder &
|
||||
else
|
||||
if [[ "$1" == "--fullscreen-sound" ]]; then
|
||||
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
||||
wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --audio="$(getaudiooutput)"
|
||||
elif [[ "$1" == "--fullscreen" ]]; then
|
||||
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
||||
wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t
|
||||
else
|
||||
if ! region="$(slurp 2>&1)"; then
|
||||
notify-send "Recording cancelled" "Selection was cancelled" -a 'Recorder' & disown
|
||||
exit 1
|
||||
fi
|
||||
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
||||
if [[ "$1" == "--sound" ]]; then
|
||||
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region" --audio="$(getaudiooutput)"
|
||||
else
|
||||
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -41,7 +41,7 @@ Item {
|
||||
visible: Config.options.bar.utilButtons.showScreenRecord
|
||||
sourceComponent: CircleUtilButton {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
onClicked: Quickshell.execDetached(["bash", "-c", "~/.config/hypr/hyprland/scripts/record.sh"])
|
||||
onClicked: Quickshell.execDetached([Directories.recordScriptPath])
|
||||
MaterialSymbol {
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
fill: 1
|
||||
|
||||
@@ -42,6 +42,7 @@ Singleton {
|
||||
property string userAiPrompts: FileUtils.trimFileProtocol(`${Directories.shellConfig}/ai/prompts`)
|
||||
property string aiChats: FileUtils.trimFileProtocol(`${Directories.state}/user/ai/chats`)
|
||||
property string aiTranslationScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/ai/gemini-translate.sh`)
|
||||
property string recordScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/videos/record.sh`)
|
||||
// Cleanup on init
|
||||
Component.onCompleted: {
|
||||
Quickshell.execDetached(["mkdir", "-p", `${shellConfig}`])
|
||||
|
||||
@@ -44,6 +44,9 @@ Toolbar {
|
||||
return "image_search";
|
||||
case RegionSelection.SnipAction.CharRecognition:
|
||||
return "document_scanner";
|
||||
case RegionSelection.SnipAction.Record:
|
||||
case RegionSelection.SnipAction.RecordWithSound:
|
||||
return "videocam";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import Qt.labs.synchronizer
|
||||
PanelWindow {
|
||||
id: root
|
||||
visible: false
|
||||
color: "transparent"
|
||||
WlrLayershell.namespace: "quickshell:regionSelector"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||
@@ -26,7 +27,7 @@ PanelWindow {
|
||||
}
|
||||
|
||||
// TODO: Ask: sidebar AI; Ocr: tesseract
|
||||
enum SnipAction { Copy, Edit, Search, CharRecognition }
|
||||
enum SnipAction { Copy, Edit, Search, CharRecognition, Record, RecordWithSound }
|
||||
enum SelectionMode { RectCorners, Circle }
|
||||
property var action: RegionSelection.SnipAction.Copy
|
||||
property var selectionMode: RegionSelection.SelectionMode.RectCorners
|
||||
@@ -175,14 +176,35 @@ PanelWindow {
|
||||
property real regionY: Math.min(dragStartY, draggingY)
|
||||
|
||||
Process {
|
||||
id: screenshotProcess
|
||||
id: screenshotProc
|
||||
running: true
|
||||
command: ["bash", "-c", `mkdir -p '${StringUtils.shellSingleQuoteEscape(root.screenshotDir)}' && grim -o '${StringUtils.shellSingleQuoteEscape(root.screen.name)}' '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}'`]
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.visible = true;
|
||||
if (root.enableContentRegions) imageDetectionProcess.running = true;
|
||||
root.preparationDone = !checkRecordingProc.running;
|
||||
}
|
||||
}
|
||||
property bool isRecording: root.action === RegionSelection.SnipAction.Record || root.action === RegionSelection.SnipAction.RecordWithSound
|
||||
property bool recordingShouldStop: false
|
||||
Process {
|
||||
id: checkRecordingProc
|
||||
running: isRecording
|
||||
command: ["pidof", "wf-recorder"]
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.preparationDone = !screenshotProc.running
|
||||
root.recordingShouldStop = (exitCode === 0);
|
||||
}
|
||||
}
|
||||
property bool preparationDone: false
|
||||
onPreparationDoneChanged: {
|
||||
if (!preparationDone) return;
|
||||
if (root.isRecording && root.recordingShouldStop) {
|
||||
Quickshell.execDetached([Directories.recordScriptPath]);
|
||||
root.dismiss();
|
||||
return;
|
||||
}
|
||||
root.visible = true;
|
||||
}
|
||||
|
||||
Process {
|
||||
id: imageDetectionProcess
|
||||
@@ -221,11 +243,16 @@ PanelWindow {
|
||||
}
|
||||
|
||||
// Set command for action
|
||||
const rx = Math.round(root.regionX * root.monitorScale);
|
||||
const ry = Math.round(root.regionY * root.monitorScale);
|
||||
const rw = Math.round(root.regionWidth * root.monitorScale);
|
||||
const rh = Math.round(root.regionHeight * root.monitorScale);
|
||||
const cropBase = `magick ${StringUtils.shellSingleQuoteEscape(root.screenshotPath)} `
|
||||
+ `-crop ${root.regionWidth * root.monitorScale}x${root.regionHeight * root.monitorScale}+${root.regionX * root.monitorScale}+${root.regionY * root.monitorScale}`
|
||||
+ `-crop ${rw}x${rh}+${rx}+${ry}`
|
||||
const cropToStdout = `${cropBase} -`
|
||||
const cropInPlace = `${cropBase} '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}'`
|
||||
const cleanup = `rm '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}'`
|
||||
const slurpRegion = `${rx},${ry} ${rw}x${rh}`
|
||||
const uploadAndGetUrl = (filePath) => {
|
||||
return `curl -sF files[]=@'${StringUtils.shellSingleQuoteEscape(filePath)}' ${root.fileUploadApiEndpoint} | jq -r '.files[0].url'`
|
||||
}
|
||||
@@ -242,6 +269,12 @@ PanelWindow {
|
||||
case RegionSelection.SnipAction.CharRecognition:
|
||||
snipProc.command = ["bash", "-c", `${cropInPlace} && tesseract '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}' stdout -l $(tesseract --list-langs | awk 'NR>1{print $1}' | tr '\\n' '+' | sed 's/\\+$/\\n/') | wl-copy && ${cleanup}`]
|
||||
break;
|
||||
case RegionSelection.SnipAction.Record:
|
||||
snipProc.command = ["bash", "-c", `${Directories.recordScriptPath} --region '${slurpRegion}'`]
|
||||
break;
|
||||
case RegionSelection.SnipAction.RecordWithSound:
|
||||
snipProc.command = ["bash", "-c", `${Directories.recordScriptPath} --region '${slurpRegion}' --sound`]
|
||||
break;
|
||||
default:
|
||||
console.warn("[Region Selector] Unknown snip action, skipping snip.");
|
||||
root.dismiss();
|
||||
|
||||
@@ -62,6 +62,18 @@ Scope {
|
||||
GlobalStates.regionSelectorOpen = true
|
||||
}
|
||||
|
||||
function record() {
|
||||
root.action = RegionSelection.SnipAction.Record
|
||||
root.selectionMode = RegionSelection.SelectionMode.RectCorners
|
||||
GlobalStates.regionSelectorOpen = true
|
||||
}
|
||||
|
||||
function recordWithSound() {
|
||||
root.action = RegionSelection.SnipAction.RecordWithSound
|
||||
root.selectionMode = RegionSelection.SelectionMode.RectCorners
|
||||
GlobalStates.regionSelectorOpen = true
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "region"
|
||||
|
||||
@@ -71,10 +83,15 @@ Scope {
|
||||
function search() {
|
||||
root.search()
|
||||
}
|
||||
|
||||
function ocr() {
|
||||
root.ocr()
|
||||
}
|
||||
function record() {
|
||||
root.record()
|
||||
}
|
||||
function recordWithSound() {
|
||||
root.recordWithSound()
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
@@ -92,4 +109,14 @@ Scope {
|
||||
description: "Recognizes text in the selected region"
|
||||
onPressed: root.ocr()
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "regionRecord"
|
||||
description: "Records the selected region"
|
||||
onPressed: root.record()
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "regionRecordWithSound"
|
||||
description: "Records the selected region with sound"
|
||||
onPressed: root.recordWithSound()
|
||||
}
|
||||
}
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
getdate() {
|
||||
date '+%Y-%m-%d_%H.%M.%S'
|
||||
}
|
||||
getaudiooutput() {
|
||||
pactl list sources | grep 'Name' | grep 'monitor' | cut -d ' ' -f2
|
||||
}
|
||||
getactivemonitor() {
|
||||
hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name'
|
||||
}
|
||||
|
||||
xdgvideo="$(xdg-user-dir VIDEOS)"
|
||||
if [[ $xdgvideo = "$HOME" ]]; then
|
||||
unset xdgvideo
|
||||
fi
|
||||
mkdir -p "${xdgvideo:-$HOME/Videos}"
|
||||
cd "${xdgvideo:-$HOME/Videos}" || exit
|
||||
|
||||
# parse --region <value> without modifying $@ so other flags like --fullscreen still work
|
||||
ARGS=("$@")
|
||||
MANUAL_REGION=""
|
||||
SOUND_FLAG=0
|
||||
FULLSCREEN_FLAG=0
|
||||
for ((i=0;i<${#ARGS[@]};i++)); do
|
||||
if [[ "${ARGS[i]}" == "--region" ]]; then
|
||||
if (( i+1 < ${#ARGS[@]} )); then
|
||||
MANUAL_REGION="${ARGS[i+1]}"
|
||||
else
|
||||
notify-send "Recording cancelled" "No region specified for --region" -a 'Recorder' & disown
|
||||
exit 1
|
||||
fi
|
||||
elif [[ "${ARGS[i]}" == "--sound" ]]; then
|
||||
SOUND_FLAG=1
|
||||
elif [[ "${ARGS[i]}" == "--fullscreen" ]]; then
|
||||
FULLSCREEN_FLAG=1
|
||||
fi
|
||||
done
|
||||
|
||||
if pgrep wf-recorder > /dev/null; then
|
||||
notify-send "Recording Stopped" "Stopped" -a 'Recorder' &
|
||||
pkill wf-recorder &
|
||||
else
|
||||
if [[ $FULLSCREEN_FLAG -eq 1 ]]; then
|
||||
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
||||
if [[ $SOUND_FLAG -eq 1 ]]; then
|
||||
wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --audio="$(getaudiooutput)"
|
||||
else
|
||||
wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t
|
||||
fi
|
||||
else
|
||||
# If a manual region was provided via --region, use it; otherwise run slurp as before.
|
||||
if [[ -n "$MANUAL_REGION" ]]; then
|
||||
region="$MANUAL_REGION"
|
||||
else
|
||||
if ! region="$(slurp 2>&1)"; then
|
||||
notify-send "Recording cancelled" "Selection was cancelled" -a 'Recorder' & disown
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
||||
if [[ $SOUND_FLAG -eq 1 ]]; then
|
||||
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region" --audio="$(getaudiooutput)"
|
||||
else
|
||||
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
Reference in New Issue
Block a user