From 355dfdae2efd66b5283f2ab8cab0c453379a9719 Mon Sep 17 00:00:00 2001 From: arkohut <39525455+arkohut@users.noreply.github.com> Date: Tue, 1 Oct 2024 01:14:20 +0800 Subject: [PATCH] feat: enable and disable service in macos --- memos/commands.py | 144 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 135 insertions(+), 9 deletions(-) diff --git a/memos/commands.py b/memos/commands.py index 2397828..a176621 100644 --- a/memos/commands.py +++ b/memos/commands.py @@ -24,6 +24,9 @@ from .record import ( save_previous_hashes, ) import time # Add this import at the top of the file +import sys +import subprocess +import platform IS_THUMBNAIL = "is_thumbnail" @@ -97,6 +100,7 @@ def serve(): ts_success = init_typesense() if db_success and ts_success: from .server import run_server + run_server() else: print("Server initialization failed. Unable to start the server.") @@ -151,10 +155,10 @@ def show(library_id: int): async def loop_files(library_id, folder, folder_path, force, plugins): # Read .memosignore file ignore_spec = None - memosignore_path = Path(folder_path) / '.memosignore' + memosignore_path = Path(folder_path) / ".memosignore" if memosignore_path.exists(): - with open(memosignore_path, 'r') as ignore_file: - ignore_spec = pathspec.PathSpec.from_lines('gitwildmatch', ignore_file) + with open(memosignore_path, "r") as ignore_file: + ignore_spec = pathspec.PathSpec.from_lines("gitwildmatch", ignore_file) updated_file_count = 0 added_file_count = 0 @@ -163,16 +167,16 @@ async def loop_files(library_id, folder, folder_path, force, plugins): async with httpx.AsyncClient(timeout=60) as client: tasks = [] for root, _, files in os.walk(folder_path): - with tqdm( - total=len(files), desc=f"Scanning {root}", leave=True - ) as pbar: + with tqdm(total=len(files), desc=f"Scanning {root}", leave=True) as pbar: candidate_files = [] for file in files: file_path = Path(root) / file absolute_file_path = file_path.resolve() # Get absolute path relative_path = absolute_file_path.relative_to(folder_path) - if file in ignore_files or (ignore_spec and ignore_spec.match_file(str(relative_path))): + if file in ignore_files or ( + ignore_spec and ignore_spec.match_file(str(relative_path)) + ): continue scanned_files.add(str(absolute_file_path)) @@ -756,7 +760,9 @@ def bind( if response.status_code == 204: print("Plugin bound to library successfully") else: - print(f"Failed to bind plugin to library: {response.status_code} - {response.text}") + print( + f"Failed to bind plugin to library: {response.status_code} - {response.text}" + ) @plugin_app.command("unbind") @@ -891,5 +897,125 @@ def record( time.sleep(10) +def get_python_path(): + return sys.executable + + +def get_memos_dir(): + return Path.home() / ".memos" + + +def generate_launch_sh(): + memos_dir = get_memos_dir() + python_path = get_python_path() + content = f"""#!/bin/bash +# activate current python environment +if [ -f "$(dirname "$python_path")/activate" ]; then + source "$(dirname "$python_path")/activate" +fi + +# run memos record +{python_path} -m memos.commands record & + +# run memos serve +{python_path} -m memos.commands serve & + +# wait for all background processes +wait +""" + launch_sh_path = memos_dir / "launch.sh" + with open(launch_sh_path, "w") as f: + f.write(content) + launch_sh_path.chmod(0o755) + + +def generate_plist(): + memos_dir = get_memos_dir() + python_dir = os.path.dirname(get_python_path()) + plist_content = f""" + + + + Label + com.user.memos + ProgramArguments + + /bin/bash + {memos_dir}/launch.sh + + RunAtLoad + + KeepAlive + + StandardOutPath + /tmp/memos.log + StandardErrorPath + /tmp/memos.err + EnvironmentVariables + + PATH + {python_dir}:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + + + +""" + plist_path = Path.home() / "Library/LaunchAgents/com.user.memos.plist" + with open(plist_path, "w") as f: + f.write(plist_content) + return plist_path + + +def load_plist(plist_path): + subprocess.run(["launchctl", "unload", str(plist_path)], check=False) + subprocess.run(["launchctl", "load", str(plist_path)], check=True) + + +def is_macos(): + return platform.system() == "Darwin" + + +@app.command() +def enable(): + """Enable memos to run at startup""" + if not is_macos(): + typer.echo("Error: This feature is only supported on macOS.") + raise typer.Exit(code=1) + + if not sys.executable: + typer.echo("Error: Unable to detect Python environment.") + raise typer.Exit(code=1) + + memos_dir = get_memos_dir() + memos_dir.mkdir(parents=True, exist_ok=True) + + generate_launch_sh() + typer.echo(f"Generated launch script at {memos_dir}/launch.sh") + + plist_path = generate_plist() + typer.echo(f"Generated plist file at {plist_path}") + + load_plist(plist_path) + typer.echo("Loaded plist file. Memos will now run at startup.") + + +@app.command() +def disable(): + """Disable memos from running at startup""" + if not is_macos(): + typer.echo("Error: This feature is only supported on macOS.") + raise typer.Exit(code=1) + + plist_path = Path.home() / "Library/LaunchAgents/com.user.memos.plist" + if plist_path.exists(): + subprocess.run(["launchctl", "unload", str(plist_path)], check=False) + plist_path.unlink() + typer.echo( + "Unloaded and removed plist file. Memos will no longer run at startup." + ) + else: + typer.echo("Plist file does not exist. Memos is not set to run at startup.") + + if __name__ == "__main__": - app() \ No newline at end of file + app()