add/change a few python scripts
This commit is contained in:
parent
de0e43ef6a
commit
7e1ed655ce
6 changed files with 207 additions and 79 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
hosts/configuration.nix
|
hosts/configuration.nix
|
||||||
|
__pycache__
|
||||||
|
|
0
scripts/__init__.py
Normal file
0
scripts/__init__.py
Normal file
0
scripts/common/__init__.py
Normal file
0
scripts/common/__init__.py
Normal file
71
scripts/common/common.py
Normal file
71
scripts/common/common.py
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
from shutil import which
|
||||||
|
|
||||||
|
|
||||||
|
def notify(
|
||||||
|
application_name: str,
|
||||||
|
title: str,
|
||||||
|
message: str,
|
||||||
|
urgency: str = "low",
|
||||||
|
category: str | None = None,
|
||||||
|
icon: Path | None = None,
|
||||||
|
desktop_entry: str | None = None,
|
||||||
|
) -> None:
|
||||||
|
args = ["notify-send", "-a", application_name, "-u", urgency]
|
||||||
|
if category:
|
||||||
|
args.append("-c")
|
||||||
|
args.append(category)
|
||||||
|
if icon:
|
||||||
|
args.append("-i")
|
||||||
|
args.append(str(icon))
|
||||||
|
if desktop_entry:
|
||||||
|
args.append("-h")
|
||||||
|
args.append(f"string:desktop-entry:{desktop_entry}")
|
||||||
|
args.append(title)
|
||||||
|
args.append(message)
|
||||||
|
print(args)
|
||||||
|
subprocess.run(args)
|
||||||
|
|
||||||
|
|
||||||
|
def read_secret_file(secret: str) -> str:
|
||||||
|
with open(f"/run/secrets/{secret}", "r") as f:
|
||||||
|
return f.read().strip()
|
||||||
|
|
||||||
|
|
||||||
|
def does_desktop_entry_exist(desktop_entry: str) -> bool:
|
||||||
|
if not desktop_entry:
|
||||||
|
raise ValueError("Please provide the full filename of the desktop entry.")
|
||||||
|
|
||||||
|
if not desktop_entry.endswith(".desktop"):
|
||||||
|
desktop_entry += ".desktop"
|
||||||
|
|
||||||
|
entry_paths = []
|
||||||
|
|
||||||
|
if which("qtpaths"):
|
||||||
|
result = subprocess.run(
|
||||||
|
["qtpaths", "--paths", "ApplicationsLocation"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
entry_paths = result.stdout.strip().split(":")
|
||||||
|
else:
|
||||||
|
print("qtpaths is not installed, falling back to XDG_DATA_DIRS.")
|
||||||
|
xdg_data_dirs = os.getenv("XDG_DATA_DIRS", "/usr/share:/usr/local/share").split(
|
||||||
|
":"
|
||||||
|
)
|
||||||
|
entry_paths = [os.path.join(path, "applications") for path in xdg_data_dirs]
|
||||||
|
entry_paths.append(os.path.expanduser("~/.local/share/applications"))
|
||||||
|
|
||||||
|
print(f"Checking the following paths for {desktop_entry}:\n{entry_paths}\n{'-'*20}")
|
||||||
|
|
||||||
|
for entry_path in entry_paths:
|
||||||
|
entry_file = Path(entry_path) / f"{desktop_entry}"
|
||||||
|
print(f"Checking for {entry_file}")
|
||||||
|
if entry_file.is_file():
|
||||||
|
print(f"{desktop_entry} found in {entry_path}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
print(f"Desktop entry {desktop_entry} does not exist.")
|
||||||
|
return False
|
118
scripts/spectacle-screenshot
Executable file
118
scripts/spectacle-screenshot
Executable file
|
@ -0,0 +1,118 @@
|
||||||
|
#! /usr/bin/env nix-shell
|
||||||
|
#! nix-shell -i python -p python312
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
from shutil import which
|
||||||
|
|
||||||
|
from common.common import notify
|
||||||
|
|
||||||
|
|
||||||
|
def spectacle_screenshot(
|
||||||
|
url: str | None = None, record: bool = False, file_path: Path | None = None
|
||||||
|
) -> None:
|
||||||
|
try:
|
||||||
|
if not which("spectacle"):
|
||||||
|
raise FileNotFoundError("spectacle is not installed.")
|
||||||
|
|
||||||
|
if file_path and Path(file_path).exists():
|
||||||
|
raise FileExistsError(
|
||||||
|
'File already exists. Please provide a different file path, or use the zipline function to upload the file.\nExample: zipline "{file_path}"'
|
||||||
|
)
|
||||||
|
|
||||||
|
if not file_path:
|
||||||
|
# Spectacle actually defaults to .webm for video recordings,
|
||||||
|
# but Zipline doesn't behave well with .webm files so we use .mp4 instead.
|
||||||
|
use_temp_file = True
|
||||||
|
suffix = ".mp4" if record else ".png"
|
||||||
|
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=suffix)
|
||||||
|
file_path = temp_file.name
|
||||||
|
temp_file.close()
|
||||||
|
else:
|
||||||
|
use_temp_file = False
|
||||||
|
|
||||||
|
command = [
|
||||||
|
"spectacle",
|
||||||
|
"--nonotify",
|
||||||
|
"--background",
|
||||||
|
"--pointer",
|
||||||
|
"--copy-image",
|
||||||
|
"--output",
|
||||||
|
file_path,
|
||||||
|
]
|
||||||
|
|
||||||
|
if record:
|
||||||
|
command.append("--record=region")
|
||||||
|
else:
|
||||||
|
command.append("--region")
|
||||||
|
|
||||||
|
try:
|
||||||
|
subprocess.run(command, check=True)
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
if Path(file_path).exists() and use_temp_file:
|
||||||
|
os.remove(file_path)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
if not Path(file_path).stat().st_size:
|
||||||
|
os.remove(file_path)
|
||||||
|
raise FileNotFoundError("The file was not created properly.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
opts = [
|
||||||
|
"/etc/nixos/scripts/zipline",
|
||||||
|
file_path,
|
||||||
|
"--application-name",
|
||||||
|
"Spectacle",
|
||||||
|
"--desktop-entry",
|
||||||
|
"org.kde.spectacle",
|
||||||
|
]
|
||||||
|
if url:
|
||||||
|
opts.extend(["--url", url])
|
||||||
|
subprocess.run(opts)
|
||||||
|
finally:
|
||||||
|
if Path(file_path).exists() and use_temp_file:
|
||||||
|
os.remove(file_path)
|
||||||
|
except Exception as e:
|
||||||
|
notify(
|
||||||
|
application_name="Spectacle",
|
||||||
|
title="An error occurred",
|
||||||
|
message=str(e),
|
||||||
|
urgency="critical",
|
||||||
|
category="transfer.error",
|
||||||
|
desktop_entry="org.kde.spectacle",
|
||||||
|
)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog="spectacle-screenshot",
|
||||||
|
description="Take a screenshot or recording with Spectacle and automatically upload it to a Zipline instance.",
|
||||||
|
epilog="Example usage: spectacle-screenshot",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--url",
|
||||||
|
help="The URL of the Zipline instance to upload the screenshot or recording to. Defaults to 'https://csw.im'.",
|
||||||
|
default="https://csw.im",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--record",
|
||||||
|
help="If this is set, Spectacle will record the region instead of taking a screenshot.",
|
||||||
|
action="store_true",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--file-path",
|
||||||
|
help="The path to save the screenshot or recording to. If not provided, the screenshot or recording will be saved to a temporary file.",
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
spectacle_screenshot(
|
||||||
|
url=args.url,
|
||||||
|
record=args.record,
|
||||||
|
file_path=args.file_path,
|
||||||
|
)
|
|
@ -1,5 +1,5 @@
|
||||||
#! /usr/bin/env nix-shell
|
#! /usr/bin/env nix-shell
|
||||||
#! nix-shell -i python3 -p python312 python312Packages.tkinter python312Packages.requests libnotify
|
#! nix-shell -i python -p python312 python312Packages.tkinter python312Packages.requests libnotify
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import mimetypes
|
import mimetypes
|
||||||
|
@ -11,53 +11,18 @@ from tkinter import Tk
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import requests # type: ignore
|
import requests # type: ignore
|
||||||
|
from common.common import does_desktop_entry_exist, notify, read_secret_file
|
||||||
|
|
||||||
def read_secret_file(secret: str) -> str:
|
|
||||||
with open(f"/run/secrets/{secret}", "r") as f:
|
|
||||||
return f.read().strip()
|
|
||||||
|
|
||||||
|
|
||||||
def does_desktop_entry_exist(desktop_entry: str) -> bool:
|
|
||||||
if not desktop_entry:
|
|
||||||
raise ValueError("Please provide the full filename of the desktop entry.")
|
|
||||||
|
|
||||||
if not desktop_entry.endswith(".desktop"):
|
|
||||||
desktop_entry += ".desktop"
|
|
||||||
|
|
||||||
entry_paths = []
|
|
||||||
|
|
||||||
# Check if qtpaths is available
|
|
||||||
if which("qtpaths"):
|
|
||||||
result = subprocess.run(
|
|
||||||
["qtpaths", "--paths", "ApplicationsLocation"],
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
text=True,
|
|
||||||
)
|
|
||||||
entry_paths = result.stdout.strip().split(":")
|
|
||||||
else:
|
|
||||||
print("qtpaths is not installed, falling back to XDG_DATA_DIRS.")
|
|
||||||
xdg_data_dirs = os.getenv("XDG_DATA_DIRS", "/usr/share:/usr/local/share").split(
|
|
||||||
":"
|
|
||||||
)
|
|
||||||
entry_paths = [os.path.join(path, "applications") for path in xdg_data_dirs]
|
|
||||||
entry_paths.append(os.path.expanduser("~/.local/share/applications"))
|
|
||||||
|
|
||||||
print(f"Checking the following paths for {desktop_entry}:\n{entry_paths}\n{'-'*20}")
|
|
||||||
|
|
||||||
# Search for the desktop entry file
|
|
||||||
for entry_path in entry_paths:
|
|
||||||
entry_file = Path(entry_path) / f"{desktop_entry}"
|
|
||||||
print(f"Checking for {entry_file}")
|
|
||||||
if entry_file.is_file():
|
|
||||||
print(f"{desktop_entry} found in {entry_path}")
|
|
||||||
return True
|
|
||||||
|
|
||||||
print(f"Desktop entry {desktop_entry} does not exist.")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def copy_to_clipboard(text: str) -> None:
|
def copy_to_clipboard(text: str) -> None:
|
||||||
|
if which("xclip"):
|
||||||
|
subprocess.run(
|
||||||
|
["xclip", "-selection", "clipboard", "-t", "text/plain", "-i"],
|
||||||
|
input=text.encode(),
|
||||||
|
)
|
||||||
|
elif which("wl-copy"):
|
||||||
|
subprocess.run(["wl-copy", "--type", "text/plain"], input=text.encode())
|
||||||
|
else:
|
||||||
root = Tk()
|
root = Tk()
|
||||||
root.withdraw()
|
root.withdraw()
|
||||||
root.clipboard_clear()
|
root.clipboard_clear()
|
||||||
|
@ -66,30 +31,6 @@ def copy_to_clipboard(text: str) -> None:
|
||||||
root.destroy()
|
root.destroy()
|
||||||
|
|
||||||
|
|
||||||
def notify(
|
|
||||||
application_name: str,
|
|
||||||
title: str,
|
|
||||||
message: str,
|
|
||||||
urgency: str = "low",
|
|
||||||
category: str | None = None,
|
|
||||||
icon: Path | None = None,
|
|
||||||
desktop_entry: str | None = None,
|
|
||||||
) -> None:
|
|
||||||
args = ["notify-send" "-a", application_name, "-u", urgency]
|
|
||||||
if category:
|
|
||||||
args.append("-c")
|
|
||||||
args.append(category)
|
|
||||||
if icon:
|
|
||||||
args.append("-i")
|
|
||||||
args.append(str(icon))
|
|
||||||
if desktop_entry:
|
|
||||||
args.append("-h")
|
|
||||||
args.append(f"string:desktop-entry:{desktop_entry}")
|
|
||||||
args.append(title)
|
|
||||||
args.append(message)
|
|
||||||
subprocess.run(args)
|
|
||||||
|
|
||||||
|
|
||||||
def zipline(
|
def zipline(
|
||||||
file_path: Path,
|
file_path: Path,
|
||||||
instance_url: str,
|
instance_url: str,
|
||||||
|
@ -98,23 +39,19 @@ def zipline(
|
||||||
) -> Any:
|
) -> Any:
|
||||||
token = read_secret_file("zipline")
|
token = read_secret_file("zipline")
|
||||||
if not token:
|
if not token:
|
||||||
print("Secret file at /run/secrets/zipline either does not exist or is empty.")
|
|
||||||
raise FileNotFoundError(
|
raise FileNotFoundError(
|
||||||
"Secret file at /run/secrets/zipline either does not exist or is empty."
|
"Secret file at /run/secrets/zipline either does not exist or is empty."
|
||||||
)
|
)
|
||||||
|
|
||||||
if not os.path.isfile(file_path):
|
if not os.path.isfile(file_path):
|
||||||
print(f"File at {file_path} does not exist.")
|
|
||||||
raise FileNotFoundError(f"File at {file_path} does not exist.")
|
raise FileNotFoundError(f"File at {file_path} does not exist.")
|
||||||
|
|
||||||
use_send_notify = False
|
use_send_notify = False
|
||||||
if application_name and desktop_entry:
|
if application_name and desktop_entry:
|
||||||
if not does_desktop_entry_exist(desktop_entry=desktop_entry):
|
if not does_desktop_entry_exist(desktop_entry=desktop_entry):
|
||||||
print("Desktop entry does not exist.")
|
|
||||||
raise FileNotFoundError("Desktop entry does not exist.")
|
raise FileNotFoundError("Desktop entry does not exist.")
|
||||||
|
|
||||||
if not which("notify-send"):
|
if not which("notify-send"):
|
||||||
print("notify-send is not installed.")
|
|
||||||
raise FileNotFoundError("notify-send is not installed.")
|
raise FileNotFoundError("notify-send is not installed.")
|
||||||
|
|
||||||
use_send_notify = True
|
use_send_notify = True
|
||||||
|
@ -136,6 +73,7 @@ def zipline(
|
||||||
|
|
||||||
if link:
|
if link:
|
||||||
copy_to_clipboard(text=link)
|
copy_to_clipboard(text=link)
|
||||||
|
print(f"Link copied to clipboard: {link}")
|
||||||
|
|
||||||
if use_send_notify:
|
if use_send_notify:
|
||||||
notify(
|
notify(
|
||||||
|
|
Loading…
Reference in a new issue