198 lines
6.1 KiB
Text
Executable file
198 lines
6.1 KiB
Text
Executable file
#! /usr/bin/env nix-shell
|
|
#! nix-shell -i python3 -p python312 python312Packages.tkinter python312Packages.requests libnotify
|
|
|
|
import argparse
|
|
import mimetypes
|
|
import os
|
|
import subprocess
|
|
from pathlib import Path
|
|
from shutil import which
|
|
from tkinter import Tk
|
|
from typing import Any
|
|
|
|
import requests # type: ignore
|
|
|
|
|
|
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:
|
|
root = Tk()
|
|
root.withdraw()
|
|
root.clipboard_clear()
|
|
root.clipboard_append(text)
|
|
root.update()
|
|
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(
|
|
file_path: Path,
|
|
instance_url: str,
|
|
application_name: str = None,
|
|
desktop_entry: str = None,
|
|
) -> Any:
|
|
token = read_secret_file("zipline")
|
|
if not token:
|
|
print("Secret file at /run/secrets/zipline either does not exist or is empty.")
|
|
raise FileNotFoundError(
|
|
"Secret file at /run/secrets/zipline either does not exist or is empty."
|
|
)
|
|
|
|
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.")
|
|
|
|
use_send_notify = False
|
|
if application_name and 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.")
|
|
|
|
if not which("notify-send"):
|
|
print("notify-send is not installed.")
|
|
raise FileNotFoundError("notify-send is not installed.")
|
|
|
|
use_send_notify = True
|
|
|
|
content_type = mimetypes.guess_type(file_path)[0] or "application/octet-stream"
|
|
|
|
try:
|
|
headers = {"authorization": token}
|
|
files = {
|
|
"file": (os.path.basename(file_path), open(file_path, "rb"), content_type)
|
|
}
|
|
response = requests.post(
|
|
f"{instance_url.rstrip('/')}/api/upload", headers=headers, files=files
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
response_data = response.json()
|
|
link = response_data.get("files", [None])[0]
|
|
|
|
if link:
|
|
copy_to_clipboard(text=link)
|
|
|
|
if use_send_notify:
|
|
notify(
|
|
application_name=application_name,
|
|
title="Upload Successful",
|
|
message=f"Link copied to clipboard: {link}",
|
|
urgency="low",
|
|
category="transfer.complete",
|
|
icon=file_path,
|
|
)
|
|
else:
|
|
raise ValueError("Invalid response format.")
|
|
else:
|
|
error_message = response.text
|
|
raise Exception(error_message)
|
|
|
|
except Exception as e:
|
|
if use_send_notify:
|
|
notify(
|
|
application_name=application_name,
|
|
title="Upload Failed",
|
|
message=f"An error occurred: {e}",
|
|
urgency="critical",
|
|
category="transfer.error",
|
|
icon=file_path,
|
|
)
|
|
raise e
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(
|
|
prog="zipline",
|
|
description="Upload a file to a Zipline instance.",
|
|
epilog="Example usage: zipline /path/to/file.txt",
|
|
)
|
|
parser.add_argument("file", help="The file to upload.")
|
|
parser.add_argument(
|
|
"--url",
|
|
help="The URL of the Zipline instance. Defaults to 'https://csw.im'.",
|
|
default="https://csw.im",
|
|
)
|
|
parser.add_argument(
|
|
"--application-name",
|
|
help="The name of the application that is uploading the file. Defaults to 'Zipline'.",
|
|
default="Zipline",
|
|
)
|
|
parser.add_argument(
|
|
"--desktop-entry",
|
|
help="The desktop entry file for the application that is uploading the file. If this is provided, send-notify will be invoked to display a notification if the upload succeeds or fails.",
|
|
default=None,
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
zipline(
|
|
file_path=args.file,
|
|
instance_url=args.url,
|
|
application_name=args.application_name,
|
|
desktop_entry=args.desktop_entry,
|
|
)
|