From 3a6ee79824c1ee45e053ab8f01cd903df08a0b73 Mon Sep 17 00:00:00 2001 From: tcsenpai Date: Sat, 7 Dec 2024 13:42:27 +0100 Subject: [PATCH] first commit --- .gitignore | 2 + README.md | 57 ++++++++++++ flatpak-menu-sync.py | 138 ++++++++++++++++++++++++++++++ flatpak-menu-sync.service.example | 17 ++++ integrate.sh | 46 ++++++++++ requirements.txt | 1 + 6 files changed, 261 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100755 flatpak-menu-sync.py create mode 100644 flatpak-menu-sync.service.example create mode 100755 integrate.sh create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3d17423 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.service +__pycache__ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ad06ffd --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +# flatpak-menu-sync + +A simple script to sync flatpak menu entries to dmenu. + +## Use case + +This script is useful for users who want to use flatpak and dmenu apps like `rofi` or `fuzzel`. +Once a flatpak app is installed, it will automatically appear in dmenu (while keeping the traditional applications). + +## Configuration + +You have to modify the `flatpak-menu-sync.service.example` file to your needs, then rename it to `flatpak-menu-sync.service`. +For most users, the default miniconda3 path should be in `/home/yourusername/miniconda3/` and thus the example file should be fine. +Remember to set your username in the `ExecStart` line. + +## Troubleshooting + +If the service doesn't start, check the logs with `journalctl -u flatpak-menu-sync.service`. + +### Error: "flatpak-menu-sync.service not found" + +This error occurs if the service file was not renamed to `flatpak-menu-sync.service`. Please see the previous section for more information. + +### Errors related to the `pyinotify` packages + +Please install the `pyinotify` package through your package manager. +Pip should be supported but if it doesn't work, please try: + +#### Ubuntu/Debian + +```bash +sudo apt install python3-pyinotify +``` + +#### Arch Linux + +```bash +sudo pacman -S python-pyinotify +``` + +#### Fedora + +```bash +sudo dnf install python3-pyinotify +``` + +And restart the service with `sudo systemctl restart flatpak-menu-sync.service`. + +## Installation + +Once you have cloned the repository and followed the configuration section, you can run the `integrate.sh` script to install the service. + +```bash +./integrate.sh +``` + +If you prefer, you can manually execute the script with the correct user and miniconda path. diff --git a/flatpak-menu-sync.py b/flatpak-menu-sync.py new file mode 100755 index 0000000..4300a06 --- /dev/null +++ b/flatpak-menu-sync.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 + +import os +import sys +import shutil +import time +import logging +from pathlib import Path + +try: + import pyinotify +except ImportError as e: + sys.exit( + f"Error: pyinotify module not found. Please install it using 'sudo apt install python3-pyinotify'\nDetails: {e}" + ) + +# Configure logging with more detailed error reporting +logging.basicConfig( + level=logging.DEBUG, # Changed to DEBUG for more detailed logs + format="%(asctime)s - %(levelname)s - %(message)s", + handlers=[ + logging.FileHandler("/var/log/flatpak-menu-sync.log"), + logging.StreamHandler(sys.stdout), # Also log to stdout + ], +) + +FLATPAK_DIR = "/var/lib/flatpak/exports/share/applications" +TARGET_DIR = "/usr/share/applications" + + +def check_directories(): + """Verify that required directories exist and are accessible""" + if not os.path.exists(FLATPAK_DIR): + logging.error(f"Flatpak directory does not exist: {FLATPAK_DIR}") + return False + if not os.path.exists(TARGET_DIR): + logging.error(f"Target directory does not exist: {TARGET_DIR}") + return False + if not os.access(FLATPAK_DIR, os.R_OK): + logging.error(f"Cannot read from Flatpak directory: {FLATPAK_DIR}") + return False + if not os.access(TARGET_DIR, os.W_OK): + logging.error(f"Cannot write to target directory: {TARGET_DIR}") + return False + return True + + +def sync_desktop_files(): + try: + if not check_directories(): + return False + + # Create target directory if it doesn't exist + Path(TARGET_DIR).mkdir(parents=True, exist_ok=True) + + # Get current files in both directories + flatpak_files = set(Path(FLATPAK_DIR).glob("*.desktop")) + target_files = set(Path(TARGET_DIR).glob("org.flatpak.*.desktop")) + + # Copy new files + for src_file in flatpak_files: + dst_file = Path(TARGET_DIR) / src_file.name + if not dst_file.exists(): + try: + shutil.copy2(src_file, dst_file) + os.chmod(dst_file, 0o644) + logging.info(f"Copied new file: {src_file.name}") + except Exception as e: + logging.error(f"Error copying {src_file.name}: {str(e)}") + + # Remove obsolete files + for target_file in target_files: + src_file = Path(FLATPAK_DIR) / target_file.name + if not src_file.exists(): + try: + target_file.unlink() + logging.info(f"Removed obsolete file: {target_file.name}") + except Exception as e: + logging.error(f"Error removing {target_file.name}: {str(e)}") + return True + except Exception as e: + logging.error(f"Unexpected error in sync_desktop_files: {str(e)}") + return False + + +class FlatpakWatcher(pyinotify.ProcessEvent): + def process_IN_CREATE(self, event): + if event.name.endswith(".desktop"): + logging.info(f"New file created: {event.name}") + sync_desktop_files() + + def process_IN_DELETE(self, event): + if event.name.endswith(".desktop"): + logging.info(f"File deleted: {event.name}") + sync_desktop_files() + + def process_IN_MODIFY(self, event): + if event.name.endswith(".desktop"): + logging.info(f"File modified: {event.name}") + sync_desktop_files() + + +def main(): + try: + logging.info("Flatpak menu sync service started") + + if not check_directories(): + sys.exit(1) + + # Initialize inotify + wm = pyinotify.WatchManager() + mask = pyinotify.IN_CREATE | pyinotify.IN_DELETE | pyinotify.IN_MODIFY + + # Create notifier + handler = FlatpakWatcher() + notifier = pyinotify.Notifier(wm, handler) + + # Add watch + wdd = wm.add_watch(FLATPAK_DIR, mask) + if FLATPAK_DIR not in wdd or not wdd[FLATPAK_DIR]: + logging.error(f"Failed to add watch for {FLATPAK_DIR}") + sys.exit(1) + + # Initial sync + if not sync_desktop_files(): + logging.error("Initial sync failed") + sys.exit(1) + + logging.info("Starting watch loop") + # Start watching + notifier.loop() + except Exception as e: + logging.error(f"Fatal error in main: {str(e)}") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/flatpak-menu-sync.service.example b/flatpak-menu-sync.service.example new file mode 100644 index 0000000..01d2696 --- /dev/null +++ b/flatpak-menu-sync.service.example @@ -0,0 +1,17 @@ +[Unit] +Description=Flatpak Menu Entry Sync Service +After=flatpak.service + +[Service] +Type=simple +ExecStart=/bin/bash -c 'source /home/youruser/miniconda3/bin/activate base && /usr/local/bin/flatpak-menu-sync.py' +Restart=on-failure +RestartSec=5 +User=root +Group=root +CPUQuota=20% +MemoryMax=50M +Nice=10 + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/integrate.sh b/integrate.sh new file mode 100755 index 0000000..845b71e --- /dev/null +++ b/integrate.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# Define paths +SCRIPT_PATH="/usr/local/bin/flatpak-menu-sync.py" +SERVICE_PATH="/etc/systemd/system/flatpak-menu-sync.service" +LOG_PATH="/var/log/flatpak-menu-sync.log" + +# Copy the Python script +echo "Copying Python script to $SCRIPT_PATH..." +sudo cp flatpak-menu-sync.py $SCRIPT_PATH +sudo chmod +x $SCRIPT_PATH + +# Copy the systemd service file +echo "Copying systemd service file to $SERVICE_PATH..." +if [ ! -f flatpak-menu-sync.service ]; then + echo "flatpak-menu-sync.service not found. Please see the example file flatpak-menu-sync.service.example and modify it to your needs, then rename it to flatpak-menu-sync.service and run this script again." + exit 1 +fi +sudo cp flatpak-menu-sync.service $SERVICE_PATH || echo "Failed to copy service file. Please check the permissions and try again." + +# Reload systemd daemon +echo "Reloading systemd daemon..." +sudo systemctl daemon-reload + +# Enable and start the service +echo "Enabling and starting the flatpak-menu-sync service..." +sudo systemctl enable flatpak-menu-sync +sudo systemctl start flatpak-menu-sync + +# Create log file if it doesn't exist +if [ ! -f "$LOG_PATH" ]; then + echo "Creating log file at $LOG_PATH..." + sudo touch $LOG_PATH + sudo chown root:root $LOG_PATH + sudo chmod 644 $LOG_PATH +fi + +# Check if the service is running +if sudo systemctl is-active flatpak-menu-sync.service > /dev/null 2>&1; then + echo "The flatpak-menu-sync service is now running." + echo "You can check the status of the service with 'systemctl status flatpak-menu-sync.service'" + echo "You can check the logs of the service with 'journalctl -u flatpak-menu-sync.service'" + echo "Happy flatpakbing!" +else + echo "The flatpak-menu-sync service failed to start. Please check the logs for more information." +fi \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7326c10 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pyinotify \ No newline at end of file