""" A script to randomly select files from a directory, open them, and allow the user to upload them to a Discord webhook, delete them, or skip to the next file. You can specify a file size limit, whether to descend into subdirectories, and provide blacklist/whitelist patterns for file selection. Here's an example usage: ./rand.py -fl 5 -d /path/to/directory --descend --blacklist "*.png" "*.mp4" """ import io import os import random import requests import subprocess import argparse import sys import tty import termios import fnmatch MEME_FOLDER = os.environ.get("RANDOM_FILE_PICKER_MEME_FOLDER", None) WEBHOOK_URL = os.environ.get("RANDOM_FILE_PICKER_WEBHOOK_URL", None) ALLOWED_EXTENSIONS = [ ".jpg", ".jpeg", ".webp", ".png", ".gif", ".mp4", ".mov", ".webm", ] IMG_EXTENSIONS = [ ".jpg", ".jpeg", ".webp", ".png", ".gif", ] def send_file(file_path: str): if WEBHOOK_URL is None: print("Error: RANDOM_FILE_PICKER_WEBHOOK_URL environment variable is not set.") return filename = os.path.basename(file_path) with open(file_path, "rb") as image_file: image_data = image_file.read() files = {'file': (filename, io.BytesIO(image_data))} response = requests.post(WEBHOOK_URL, files=files, data={}) response.raise_for_status() # Raise HTTPError for bad responses print(f"\nSuccessfully uploaded {filename}") def getch() -> str: fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) try: tty.setcbreak(sys.stdin.fileno()) ch = sys.stdin.read(1) finally: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) return ch def open_with_gwenview(maybe_process: subprocess.Popen[bytes] | None, file_path: str) -> subprocess.Popen[bytes]: if maybe_process is not None: maybe_process.terminate() maybe_process.wait() process = subprocess.Popen(["gwenview", file_path]) return process def open_in_folder(file_path: str): subprocess.Popen(["dolphin", "--select", file_path]) #subprocess.run(["dolphin", "--select", file_path]) def main(): parser = argparse.ArgumentParser(description="Random File Sender") parser.add_argument('-fl', '--file-limit', type=int, default=10, help='Maximum file size in MB') parser.add_argument('-d', '--directory', type=str, default='.', help='Directory to search for files') parser.add_argument('--descend', action='store_true', help='Whether to descend into subdirectories') #parser.add_argument('--direct', action='store_true', help='Whether to open files directly without xdg-open') parser._positionals.title = 'Positional arguments' parser._optionals.title = 'Optional arguments' # Globs for blacklisting and whitelisting, --blacklist "*.png" "*.mp4" parser.add_argument('--blacklist', nargs='*', help='List of glob patterns to blacklist files') parser.add_argument('--whitelist', nargs='*', help='List of glob patterns to whitelist files') args = parser.parse_args() file_limit: int = args.file_limit #open_directly: bool = args.direct directory: str = args.directory descend: bool = args.descend blacklist_patterns: list[str] = args.blacklist if args.blacklist else [] whitelist_patterns: list[str] = args.whitelist if args.whitelist else [] print(f"File size limit set to {file_limit} MB") #print(f"Open files directly: {open_directly}") if not os.path.exists(directory) or not os.path.isdir(directory): print(f"Directory '{directory}' does not exist or is not a directory.") return search_path = os.path.abspath(directory) # Collect files from the current directory usable_files: list[str] = [] for directory, _, files in os.walk(search_path): if directory != search_path and not descend: continue for f in files: file_path = os.path.join(directory, f) # Check against whitelist patterns if whitelist_patterns and not any(fnmatch.fnmatch(file_path, pattern) for pattern in whitelist_patterns): continue # Check against blacklist patterns if any(fnmatch.fnmatch(file_path, pattern) for pattern in blacklist_patterns): continue if not any(f.lower().endswith(ext) for ext in ALLOWED_EXTENSIONS): continue stat_res = os.stat(file_path) if stat_res.st_size < 1024 * 1024 * file_limit: usable_files.append(file_path) if not usable_files: print("No usable files found.") return pickFile = True gwenview_process = None while pickFile: file = random.choice(usable_files) print(f"Random file: {file}") if file.lower().endswith(tuple(IMG_EXTENSIONS)): gwenview_process = open_with_gwenview(gwenview_process, file) else: subprocess.run(["xdg-open", file]) selectionTime = True while selectionTime: print("Options: [u]pload, [n]ext, [d]elete, [f]older, [q]uit") choice = getch().lower() # Skip escape sequences (arrow keys) if choice.encode("utf-8") == b'\x1b': _ = getch() # Skip the next two characters _ = getch() continue #print(choice.encode("utf-8")) # For debugging purposes if choice == "u": print("\nUploading...") send_file(file) print("Upload complete.") #selectionTime = False elif choice == "d": print("\nDeleting...") os.remove(file) usable_files.remove(file) selectionTime = False elif choice == "n": print("\nNext file...") selectionTime = False usable_files.remove(file) elif choice == "f": open_in_folder(file) elif choice == "q": print("\nQuitting...") pickFile = False break elif choice == "m": print("\nMoving to meme folder...") if MEME_FOLDER is None: print("Error: RANDOM_FILE_PICKER_MEME_FOLDER environment variable is not set.") continue # First, make sure we're not in the meme folder already if os.path.abspath(os.path.dirname(file)) == os.path.abspath(MEME_FOLDER): print("File is already in the meme folder.") continue # Also, ensure the meme folder exists if not os.path.exists(MEME_FOLDER): print(f"Meme folder '{MEME_FOLDER}' does not exist.") continue base_name = os.path.basename(file) new_path = os.path.join(MEME_FOLDER, base_name) # Avoid overwriting existing files if os.path.exists(new_path): name, ext = os.path.splitext(base_name) counter = 1 while os.path.exists(new_path): new_path = os.path.join(MEME_FOLDER, f"{name}_{counter}{ext}") counter += 1 os.rename(file, new_path) usable_files.remove(file) selectionTime = False else: print(f"\n'{choice}' is an invalid option. Please choose again.") if __name__ == "__main__": main()