From 7cbb47bc0c94123e7322c353129772613b74544e Mon Sep 17 00:00:00 2001
From: Radical <66682497+Radiicall@users.noreply.github.com>
Date: Sun, 2 Jul 2023 00:33:42 +0200
Subject: [PATCH] Cross platform installer/uninstaller (#32)
Removed the old install/uninstall scripts
Added new cross platform install/uninstall python scripts!
Updated the README to reflect changes
---
.gitignore | 1 +
README.md | 5 +-
scripts/install-macos.sh | 196 ----------------------
scripts/install-win.bat | 113 -------------
scripts/installer.py | 328 +++++++++++++++++++++++++++++++++++++
scripts/uninstall-macos.sh | 12 --
scripts/uninstall-win.bat | 54 ------
scripts/uninstaller.py | 55 +++++++
8 files changed, 388 insertions(+), 376 deletions(-)
delete mode 100755 scripts/install-macos.sh
delete mode 100644 scripts/install-win.bat
create mode 100755 scripts/installer.py
delete mode 100755 scripts/uninstall-macos.sh
delete mode 100644 scripts/uninstall-win.bat
create mode 100755 scripts/uninstaller.py
diff --git a/.gitignore b/.gitignore
index dae5bcd..4872a8f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
Cargo.lock
main.json
/pkg
+.DS_Store
diff --git a/README.md b/README.md
index b2ec2b7..31335dd 100644
--- a/README.md
+++ b/README.md
@@ -40,7 +40,10 @@ Terminal Output:
- Discord
- Imgur API key (image support without port forwarding)
-Installers for Windows and macOS are available.
+Windows users will also need vcredist.
+
+**A cross-platform installer written in Python can be found here, press ctrl+s on the page to download.**
+**There's also a cross-platform uninstaller that can be found here.**
A fully filled out config would look something like this
diff --git a/scripts/install-macos.sh b/scripts/install-macos.sh
deleted file mode 100755
index d7ef5fe..0000000
--- a/scripts/install-macos.sh
+++ /dev/null
@@ -1,196 +0,0 @@
-#!/bin/zsh
-# A script to autoamtically install jellyfin-rpc on mac
-clear
-
-# Get the user's server information
-vared -p "Jellyfin Server URL (include http/https): " -c jellyfinurl
-vared -p "Jellyfin API Key (you can find this at ${jellyfinurl}/web/#!/apikeys.html): " -c jellyfinkey
-vared -p "Jellyfin Username: " -c jellyfinuser
-echo ""
-
-echo "The blacklist creation in this script is currently broken"
-echo "Check the README for details on how to set it up in the config "
-
-# Prompt the user for what libraries should be included/blocked
-responses=()
-
-vared -p "Include Movies in Discord Rich Presence? (y/n) " -c moviesEnabled
-if [[ $moviesEnabled == [Nn]* ]]; then
- responses+=( "movie" )
-fi
-
-vared -p "Include TV in Discord Rich Presence? (y/n) " -c tvEnabled
-if [[ $tvEnabled == [Nn]* ]]; then
- responses+=( "episode" )
-fi
-
-vared -p "Include Music in Discord Rich Presence? (y/n) " -c musicEnabled
-if [[ $musicEnabled == [Nn]* ]]; then
- responses+=( "music" )
-fi
-
-vared -p "Include Live TV in Discord Rich Presence? (y/n) " -c livetvEnabled
-if [[ $livetvEnabled == [Nn]* ]]; then
- responses+=( "livetv" )
-fi
-
-# Build the blocklist string
-if [[ ${#responses} -eq 0 ]]; then
- typeBlocklistString=''
-else
- typeBlocklistString='['
- for i in ${responses[@]}; do
- typeBlocklistString+="\"$i\", "
- done
- typeBlocklistString=${typeBlocklistString%?}
- typeBlocklistString=${typeBlocklistString%?}
- typeBlocklistString+=']'
-fi
-echo ""
-
-# Prompt for libraries to block
-echo "Separated by only commas, type the names of any libraries to be excluded from Jellyfin RPC. Press return when finished."
-echo "For example, typing 'Anime,Anime Movies' will filter out media from libraries named 'Anime' and 'Anime Movies'."
-vared -p "Leave blank to enable all libraries. This will not disable filtering by media type. " -c libBlocklist
-
-# Build the blocklist string
-libBlocklistArray=(${(@s:,:)libBlocklist})
-if [[ ${#libBlocklistArray} -eq 0 ]]; then
- libBlocklistString=''
-else
- libBlocklistString='['
- for i in ${libBlocklistArray[@]}; do
- libBlocklistString+="\"$i\", "
- done
- libBlocklistString=${libBlocklistString%?}
- libBlocklistString=${libBlocklistString%?}
- libBlocklistString+=']'
-fi
-echo ""
-
-# Get discord application ID, or use default if left blank
-vared -p "Discord Application ID (leave blank if you're unsure): " -c discordAppId
-if [ -z $discordAppId ]; then
- discordAppId="1053747938519679018"
-fi
-
-# Enable or disable image uploading
-configImagesEnabled="false"
-configImgurEnabled="false"
-vared -p "Display media images in discord? (y/n) " -c artworkEnabled
-if [[ $artworkEnabled == [Yy]* ]]; then
- configImagesEnabled="true"
-
- # Get Imgur client ID & enable Imgur uploading
- echo "If the server is not port-forwarded, you must enable uploading artwork to Imgur or images will not work."
- vared -p "Upload media images to Imgur to be displayed in discord? Selecting \"n\" suggests that your server is port-forwarded! (y/n) " -c imgurEnabled
- if [[ $imgurEnabled == [Yy]* ]]; then
- configImgurEnabled="true"
- echo "To get an Imgur client ID, go to https://api.imgur.com/oauth2/addclient"
- echo "Name can be anything, authorization type must be 'OAuth 2 authorization without a callback URL'"
- echo "Press submit to get your client ID"
- vared -p "Imgur client ID (): " -c imgurId
- fi
-fi
-
-# Put together the config file
-
-configFileContents=""
-configFileContents+="$(cat < ~/.config/jellyfin-rpc/main.json
-
-# Prompt user to install Jellyfin RPC
-vared -p "Download latest release? (y/n) " -c downloadlatest
-if [[ $downloadlatest == [Yy]* ]]; then
- # download file to binary directory and give execution permissions
- curl -o /usr/local/bin/jellyfin-rpc -L https://github.com/Radiicall/jellyfin-rpc/releases/latest/download/jellyfin-rpc-x86_64-darwin
- chmod +x /usr/local/bin/jellyfin-rpc
-
- # Prompt user to enabled Jellyfin RPC running at login
- vared -p "Set Jellyfin-RPC to run at login? (y/n) " -c runAtLogin
- if [[ $runAtLogin == [Yy]* ]]; then
-
- if pgrep -xq -- "jellyfin-rpc"; then
- killall jellyfin-rpc # Kill jellyfin-rpc if it is running
- fi
- if launchctl list | grep Jellyfin-RPC &> /dev/null; then
- launchctl remove Jellyfin-RPC # Unload Jellyfin-RPC LaunchAgent if it is loaded
- fi
-
- # Create LaunchAgent file
- cat > ~/Library/LaunchAgents/jellyfinrpc.local.plist<< EOF
-
-
-
-
- Label
- Jellyfin-RPC
- Program
- /usr/local/bin/jellyfin-rpc
- RunAtLoad
-
- StandardErrorPath
- /tmp/jellyfinrpc.local.stderr.txt
- StandardOutPath
- /tmp/jellyfinrpc.local.stdout.txt
-
-
-EOF
- chmod 644 ~/Library/LaunchAgents/jellyfinrpc.local.plist # Give LaunchAgent proper permissions
- launchctl load ~/Library/LaunchAgents/jellyfinrpc.local.plist # Load LaunchAgent
- fi
- echo "Jellyfin RPC is now set up to start at login."
- echo "If needed, you can run Jellyfin RPC at any time by running 'jellyfin-rpc' in a terminal."
-fi
-
-exit
\ No newline at end of file
diff --git a/scripts/install-win.bat b/scripts/install-win.bat
deleted file mode 100644
index 63a5bf7..0000000
--- a/scripts/install-win.bat
+++ /dev/null
@@ -1,113 +0,0 @@
-@echo off
-
-REM Set paths
-set EXE_PATH=%APPDATA%\jellyfin-rpc\jellyfin-rpc.exe
-set JSON_PATH=%APPDATA%\jellyfin-rpc\main.json
-set DOWNLOAD_URL=https://github.com/Radiicall/jellyfin-rpc/releases/latest/download/jellyfin-rpc.exe
-set DOWNLOAD_DIR=%APPDATA%\jellyfin-rpc
-
-REM set
-set JELLYFIN_URL=https://example.com
-set JELLYFIN_API_KEY=abcdef0123456789
-set JELLYFIN_USERNAME=admin
-set DISCORD_APPLICATION_ID=1053747938519679018
-set DISCORD_ENABLE_IMAGES=false
-
-echo ===============================================================================
-echo JELLYFIN-RPC INSTALLATION
-echo ===============================================================================
-echo.
-
-REM Check if jellyfin-rpc folder exist
-if not exist "%DOWNLOAD_DIR%" mkdir "%DOWNLOAD_DIR%"
-
-REM Check if jellyfin-rpc.exe is present
-if exist "%EXE_PATH%" (
- echo jellyfin-rpc.exe is already present. & timeout /t 3 /nobreak >nul
-) else (
- REM Downloading jellyfin-rpc binary
- echo Downloading jellyfin-rpc binary from GitHub... & timeout /t 3 /nobreak >nul
- curl -L %DOWNLOAD_URL% -o "%DOWNLOAD_DIR%\jellyfin-rpc.exe"
-)
-
-rem Prompt the user for input
-set /p JELLYFIN_URL=Enter Jellyfin URL "[%JELLYFIN_URL%]":
-set /p JELLYFIN_API_KEY=Enter Jellyfin API key "[%JELLYFIN_API_KEY%]":
-set /p JELLYFIN_USERNAME=Enter Jellyfin username "[%JELLYFIN_USERNAME%]":
-set /p DISCORD_APPLICATION_ID=Enter Discord application ID "[%DISCORD_APPLICATION_ID%]":
-set /p IMGUR_CLIENT_ID=Enter Imgur client ID (Leave empty if not using) "[%IMGUR_CLIENT_ID%]":
-set /p IMAGES_ENABLE_IMAGES=Enable images (true/false) [%IMAGES_ENABLE_IMAGES%]:
-set /p IMAGES_IMGUR_IMAGES=Enable images from Imgur (true/false) [%IMAGES_IMGUR_IMAGES%]:
-
-
-
-rem Output the JSON data to the file
-echo { > main.json
-echo "jellyfin": { >> main.json
-echo "url": "%JELLYFIN_URL%", >> main.json
-echo "api_key": "%JELLYFIN_API_KEY%", >> main.json
-echo "username": "%JELLYFIN_USERNAME%" >> main.json
-echo }, >> main.json
-echo "discord": { >> main.json
-echo "application_id": "%DISCORD_APPLICATION_ID%" >> main.json
-echo }, >> main.json
-echo "imgur": { >> main.json
-echo "client_id": "%IMGUR_CLIENT_ID%" >> main.json
-echo }, >> main.json
-echo "images": { >> main.json
-echo "enable_images": %IMAGES_ENABLE_IMAGES%, >> main.json
-echo "imgur_images": %IMAGES_IMGUR_IMAGES% >> main.json
-echo } >> main.json
-echo } >> main.json
-
-REM Check if main.json is present
-if exist "%JSON_PATH%" (
- echo main.json file is already present & timeout /t 3 /nobreak >nul
- del "main.json"
-) else (
- move "main.json" "%DOWNLOAD_DIR%\" >nul
-)
-
-REM Check if NSSM is already installed
-if exist "%DOWNLOAD_DIR%\nssm-2.24\win64\nssm.exe" (
- echo NSSM is already installed. & timeout /t 3 /nobreak >nul
-) else (
- REM Download NSSM installer
- echo Downloading and unzipping NSSM installer... & timeout /t 3 /nobreak >nul
- curl -L https://nssm.cc/release/nssm-2.24.zip -o nssm.zip
-
- REM Unzip NSSM
- powershell -Command "Expand-Archive -LiteralPath nssm.zip -DestinationPath ."
- move /Y "nssm-2.24" "%DOWNLOAD_DIR%\" >nul
- echo Deleting unnecessary nssm.zip file
- del "nssm.zip"
-)
-
-REM Install NSSM
-echo Installing jellyfin-rpc service... & timeout /t 3 /nobreak >nul
-%DOWNLOAD_DIR%\nssm-2.24\win64\nssm.exe install jellyfin-rpc "%EXE_PATH%" "-c %JSON_PATH% -i %DOWNLOAD_DIR%\urls.json"
-
-REM Start the executable using NSSM
-echo Starting jellyfin-rpc service... & timeout /t 3 /nobreak >nul
-set "psCommand=powershell -Command "Start-Process %DOWNLOAD_DIR%\nssm-2.24\win64\nssm.exe -Verb RunAs -ArgumentList 'start','jellyfin-rpc'""
-powershell -NoProfile -ExecutionPolicy Bypass -Command "%psCommand%"
-
-
-REM Coded by xenoncolt.tk
-
-REM Check if the service is running
-tasklist /fi "imagename eq jellyfin-rpc.exe" | find ":" > nul
-if %errorlevel%==0 (
- echo ===============================================================================
- echo JELLYFIN-RPC SERVICE IS RUNNING
- echo ===============================================================================
-) else (
- echo jellyfin-rpc service failed to start.
-)
-timeout /t 5
-
-echo.
-echo ===============================================================================
-echo INSTALLATION COMPLETE!
-echo ===============================================================================
-pause >nul
diff --git a/scripts/installer.py b/scripts/installer.py
new file mode 100755
index 0000000..406f71d
--- /dev/null
+++ b/scripts/installer.py
@@ -0,0 +1,328 @@
+#!/usr/bin/env python3
+
+# Thanks to https://github.com/pogmommy for making the original macOS installer
+# Thanks to https://github.com/xenoncolt for making the original Windows installer
+# Their contributions made this universal script a lot easier to produce.
+
+import os
+import subprocess
+import platform
+from time import sleep
+
+path = ""
+
+if platform.system() != "Windows":
+ if os.environ.get("XDG_CONFIG_HOME"):
+ path = os.environ["XDG_CONFIG_HOME"].removesuffix("/") + "/jellyfin-rpc/main.json"
+ else:
+ path = os.environ["HOME"].removesuffix("/") + "/.config/jellyfin-rpc/main.json"
+
+ subprocess.run(["mkdir", "-p", path.removesuffix("main.json")])
+else:
+ path = os.environ["APPDATA"].removesuffix("\\") + "\jellyfin-rpc\main.json"
+ subprocess.run(["powershell", "-Command", f'mkdir "{path.removesuffix("main.json")}"'], stdout=subprocess.DEVNULL)
+
+print("""
+Welcome to the Jellyfin-RPC installer
+[https://github.com/Radiicall/jellyfin-rpc#Setup]
+""")
+
+current = ""
+
+if os.path.isfile(path):
+ print(f"Found existing config: {path}")
+ while True:
+ current = input("Use existing config? (y/N): ").lower()
+ if current == "n" or current == "y" or current == "":
+ break
+ print("Invalid input, please type y or n")
+
+if current == "n" or current == "":
+ content = "{"
+
+ print("----------Jellyfin----------")
+ url = input("URL (include http/https): ")
+ api_key = input("API key: ")
+ username = input("username: ")
+
+ content += f' "jellyfin": {{ "url": "{url}", "api_key": "{api_key}", "username": "{username}"'
+
+ print("If you dont want anything else you can just press enter through all of these")
+
+ while True:
+ val = input("Do you want to customize music display? (y/N): ").lower()
+
+ if val == "n" or val == "":
+ break
+ elif val != "y":
+ print("Invalid input, please type y or n")
+ continue
+
+ print("Enter what you would like to be shown in a comma seperated list")
+ print("Remember that it will show in the order you type it in")
+ print("Valid options are year, album and/or genres")
+ display = input("[Default: genres]: ")
+
+ print("Choose the separator between the artist name and the info")
+ separator = input("[Default: -]: ")
+
+ if display != "" and separator != "":
+ content += f', "music": {{ "display": "{display}", "separator": "{separator}" }}'
+ elif display != "" and separator == "":
+ content += f', "music": {{ "display": "{display}" }}'
+ elif display == "" and separator != "":
+ content += f', "music": {{ "separator": "{separator}" }}'
+
+ break
+
+ while True:
+ val = input("Do you want to blacklist media types or libraries? (y/N): ").lower()
+
+ if val == "n" or val == "":
+ content += " }"
+ break
+ elif val != "y":
+ print("Invalid input, please type y or n")
+ continue
+
+ print("You will first type in what media types to blacklist, this should be a comma separated list WITHOUT SPACES")
+ print("then after that you can choose what libraries to blacklist, this should ALSO be a comma separated list,")
+ print("there should be no spaces before or after the commas but there can be spaces in the names of libraries")
+ sleep(2)
+
+ print("Media types 1/2")
+ media_types = input("Valid types are music, movie, episode and/or livetv [Default: ]: ").split(",")
+
+ print("Libraries 2/2")
+ libraries = input("Enter libraries to blacklist [Default: ]: ").split(",")
+
+ content += ', "blacklist": { "media_types": [ '
+ for i in media_types:
+ content += f'"{i}", '
+
+ content = content.removesuffix(", ")
+
+ content += ' ], "libraries": ['
+ for i in libraries:
+ content += f'"{i}", '
+
+ content = content.removesuffix(", ")
+ content += " ] } }"
+
+ break
+
+ print("----------Discord----------")
+
+ content += ', "discord": {'
+
+ appid = input("Enter your discord application ID [Default: 1053747938519679018]: ")
+ if appid != "":
+ content += f' "application_id": "{appid}"'
+
+ while True:
+ val = input("Do you want custom buttons? (y/N): ").lower()
+
+ if val == "n" or val == "":
+ content += " }"
+ break
+ elif val != "y":
+ print("Invalid input, please type y or n")
+ continue
+
+ if appid != "":
+ content += ","
+
+ content += ' "buttons": [ '
+
+ print("If you want one button to continue being dynamic then you have to specifically enter dynamic into both fields")
+ print("If you dont want any buttons to appear then you can leave everything blank here and it wont show anything.")
+
+ print("Button 1/2")
+ name = input("Choose what the button will show [Default: dynamic]: ")
+ url = input("Choose where the button will direct to [Default: dynamic]: ")
+
+ button1 = False
+ if name != "" and url != "":
+ content += f'{{ "name": "{name}", "url": "{url}" }}'
+ button1 = True
+
+ print("Button 2/2")
+ name = input("Choose what the button will show [Default: dynamic]: ")
+ url = input("Choose where the button will direct to [Default: dynamic]: ")
+
+ if name != "" and url != "" and button1 == True:
+ content += ", "
+ if name != "" and url != "":
+ content += f'{{ "name": "{name}", "url": "{url}" }}'
+
+ content += " ] }"
+ break
+
+ print("----------Images----------")
+
+ while True:
+ val = input("Do you want images? (y/N): ").lower()
+
+ if val == "n" or val == "":
+ break
+ elif val != "y":
+ print("Invalid input, please type y or n")
+ continue
+
+ val2 = input("Do you want imgur images? (y/N): ").lower()
+ client_id = ""
+
+ if val2 == "y":
+ client_id = input("Enter your imgur client id: ")
+ elif val2 != "n" and val2 != "":
+ print("Invalid input, please type y or n")
+ continue
+
+ if val2 == "y":
+ content += f', "imgur": {{ "client_id": "{client_id}" }}, "images": {{ "enable_images": true, "imgur_images": true }}'
+ else:
+ content += f', "images": {{ "enable_images": true }}'
+
+ break
+
+ content += " }"
+
+ print(f"\nPlacing config in '{path}'")
+
+ file = open(path, "w")
+ file.write(content)
+ file.close()
+
+
+print("\nDownloading Jellyfin-RPC")
+
+if platform.system() == "Windows":
+ path = path.removesuffix("main.json")
+ subprocess.run(["curl", "-o", path + "jellyfin-rpc.exe", "-L", "https://github.com/Radiicall/jellyfin-rpc/releases/latest/download/jellyfin-rpc.exe"])
+ while True:
+ val = input("Do you want to autostart Jellyfin-RPC at login? (y/N): ").lower()
+
+ if val == "n" or val == "":
+ break
+ if val != "y":
+ print("Invalid input, please type y or n")
+ continue
+
+ if os.path.isfile(path + "winsw.exe"):
+ print("The script will prompt for administrator to remove the already installed service")
+ sleep(1)
+ subprocess.run[path + "winsw.exe", "uninstall"]
+
+ subprocess.run(["curl", "-o", path + "winsw.exe", "-L", "https://github.com/winsw/winsw/releases/latest/download/WinSW-x64.exe"])
+
+ content = f"""
+ jellyfin-rpc
+ Jellyfin-RPC
+ This service is running Jellyfin-RPC for rich presence support
+ {path}jellyfin-rpc.exe
+ -c {path}main.json -i {path}urls.json
+"""
+
+ file = open(path + "winsw.xml", "w")
+ file.write(content)
+ file.close()
+
+ print("The program will now ask you for administrator rights twice, this is so the service can be installed!")
+ print("waiting 5 seconds")
+ sleep(5)
+
+ subprocess.run([path + "winsw.exe", "install"])
+ subprocess.run([path + "winsw.exe", "start"])
+
+ print("Autostart has been set up, jellyfin-rpc should now launch at login\nas long as there are no issues with the configuration")
+
+elif platform.system() == "Darwin":
+ subprocess.run(["curl", "-o", "/usr/local/bin/jellyfin-rpc", "-L", "https://github.com/Radiicall/jellyfin-rpc/releases/latest/download/jellyfin-rpc-x86_64-linux"])
+ subprocess.run(["chmod", "+x", "/usr/local/bin/jellyfin-rpc"])
+
+ while True:
+ val = input("Do you want to autostart Jellyfin-RPC at login? (y/N): ").lower()
+
+ if val == "n" or val == "":
+ break
+ if val != "y":
+ print("Invalid input, please type y or n")
+ continue
+
+ if subprocess.run(["pgrep", "-xq", "--", "'jellyfin-rpc'"]).returncode == 0:
+ subprocess.run(["killall", "jellyfin-rpc"])
+
+ if "Jellyfin-RPC" in subprocess.Popen("launchctl list", shell=True, stdout=subprocess.PIPE).stdout.read().decode():
+ subprocess.run(["launchctl", "remove", "Jellyfin-RPC"])
+
+ content = """
+
+
+
+ Label
+ Jellyfin-RPC
+ Program
+ /usr/local/bin/jellyfin-rpc
+ RunAtLoad
+
+ StandardErrorPath
+ /tmp/jellyfinrpc.local.stderr.txt
+ StandardOutPath
+ /tmp/jellyfinrpc.local.stdout.txt
+
+"""
+
+ path = os.environ["HOME"] + "/Library/LaunchAgents/jellyfinrpc.local.plist"
+
+ file = open(path, "w")
+ file.write(content)
+ file.close()
+
+ subprocess.run(["chmod", "644", path])
+ subprocess.run(["launchctl", "load", path])
+
+ print("Jellyfin RPC is now set up to start at login.")
+ print("If needed, you can run Jellyfin RPC at any time by running 'jellyfin-rpc' in a terminal.")
+ break
+else:
+ subprocess.run(["mkdir", "-p", os.environ["HOME"].removesuffix("/") + "/.local/bin"])
+ subprocess.run(["curl", "-o", os.environ["HOME"].removesuffix("/") + "/.local/bin/jellyfin-rpc", "-L", "https://github.com/Radiicall/jellyfin-rpc/releases/latest/download/jellyfin-rpc-x86_64-linux"])
+ subprocess.run(["chmod", "+x", os.environ["HOME"].removesuffix("/") + "/.local/bin/jellyfin-rpc"])
+
+ if os.environ.get("XDG_CONFIG_HOME"):
+ path = os.environ["XDG_CONFIG_HOME"].removesuffix("/") + "/systemd/user/jellyfin-rpc.service"
+ else:
+ path = os.environ["HOME"].removesuffix("/") + "/.config/systemd/user/jellyfin-rpc.service"
+
+ while True:
+ val = input("Do you want to autostart Jellyfin-RPC at login using Systemd? (y/N): ").lower()
+
+ if val == "n" or val == "":
+ break
+ if val != "y":
+ print("Invalid input, please type y or n")
+ continue
+
+ print(f"\nSetting up service file in {path}")
+
+ subprocess.run(["mkdir", "-p", path.removesuffix("jellyfin-rpc.service")])
+
+ content = f"""[Unit]
+Description=Jellyfin-RPC Service
+Documentation=https://github.com/Radiicall/jellyfin-rpc
+After=network.target
+
+[Service]
+Type=simple
+ExecStart={os.environ["HOME"].removesuffix("/") + "/.local/bin/jellyfin-rpc"}
+Restart=on-failure
+
+[Install]
+WantedBy=default.target"""
+
+ file = open(path, "w")
+ file.write(content)
+ file.close()
+
+ subprocess.run(["systemctl", "--user", "daemon-reload"])
+ subprocess.run(["systemctl", "--user", "enable", "--now", "jellyfin-rpc.service"])
diff --git a/scripts/uninstall-macos.sh b/scripts/uninstall-macos.sh
deleted file mode 100755
index bcea96a..0000000
--- a/scripts/uninstall-macos.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/zsh
-# A script to autoamtically uninstall jellyfin-rpc on mac
-if pgrep -xq -- "jellyfin-rpc"; then
- killall jellyfin-rpc # Kill jellyfin-rpc if it is running
-fi
-if launchctl list | grep Jellyfin-RPC; then
- launchctl remove Jellyfin-RPC # Unload Jellyfin-RPC launchagent if it is loaded
-fi
-rm ~/Library/LaunchAgents/jellyfinrpc.local.plist #remove launch agent
-rm -rf ~/.config/jellyfin-rpc #remove config file
-rm /usr/local/bin/jellyfin-rpc #remove binary
-echo "Uninstall complete!"
diff --git a/scripts/uninstall-win.bat b/scripts/uninstall-win.bat
deleted file mode 100644
index b1ffe7e..0000000
--- a/scripts/uninstall-win.bat
+++ /dev/null
@@ -1,54 +0,0 @@
-@echo off
-
-REM Check if running with administrator privileges
-net session >nul 2>&1
-if %errorlevel% == 0 (
- echo Running with administrator privileges
-) else (
- echo ERROR: This batch file must be run with administrator privileges.
- echo Please right-click on the batch file and select "Run as administrator"
- pause >nul
- exit /b
-)
-
-
-
-echo ===============================================================================
-echo JELLYFIN-RPC UNINSTALLATION
-echo ===============================================================================
-echo.
-timeout /t 3 >nul
-
-REM set path
-set NSSM_PATH=%APPDATA%\jellyfin-rpc\nssm-2.24\win64\nssm.exe
-set MAIN_PATH=%APPDATA%\jellyfin-rpc
-
-
-
-echo Stopping jellyfin-rpc from service & timeout /t 5 >nul
-%NSSM_PATH% stop jellyfin-rpc
-
-timeout /t 2 >nul
-
-echo ===============================================================================
-echo JELLYFIN-RPC STOPPED!
-echo ===============================================================================
-echo.
-
-
-set /p =Hit ENTER to continue uninstallation...
-echo Removing jellyfin-rpc from service...
-%NSSM_PATH% remove jellyfin-rpc
-
-timeout /t 5 >nul
-
-echo Removing jellyfin-rpc folder...
-rd /s /q "%APPDATA%\jellyfin-rpc"
-echo jellyfin-rpc folder removed successfully.
-
-echo.
-echo ===============================================================================
-echo UNINSTALLATION COMPLETE!
-echo ===============================================================================
-pause >nul
-
diff --git a/scripts/uninstaller.py b/scripts/uninstaller.py
new file mode 100755
index 0000000..008c161
--- /dev/null
+++ b/scripts/uninstaller.py
@@ -0,0 +1,55 @@
+import platform
+import os
+import subprocess
+from time import sleep
+import shutil
+
+# Thanks to https://github.com/pogmommy for making the original macOS uninstaller
+# Thanks to https://github.com/xenoncolt for making the original Windows uninstaller
+
+print("Welcome to the Jellyfin-RPC uninstaller")
+input("Hit enter to continue...")
+
+if platform.system() != "Windows":
+ if os.environ.get("XDG_CONFIG_HOME"):
+ path = os.environ["XDG_CONFIG_HOME"].removesuffix("/") + "/jellyfin-rpc/"
+ else:
+ path = os.environ["HOME"].removesuffix("/") + "/.config/jellyfin-rpc/"
+else:
+ path = os.environ["APPDATA"].removesuffix("\\") + "\\jellyfin-rpc\\"
+
+if platform.system() == "Windows":
+ if os.path.isfile(path + "winsw.exe"):
+ print("The script will ask for admin rights to remove the autostart service")
+ print("waiting 5 seconds")
+ sleep(5)
+ subprocess.run([path + "winsw.exe", "uninstall"])
+
+ shutil.rmtree(path)
+elif platform.system() == "Darwin":
+ if subprocess.run(["pgrep", "-xq", "--", "'jellyfin-rpc'"]).returncode == 0:
+ subprocess.run(["killall", "jellyfin-rpc"])
+
+ if "Jellyfin-RPC" in subprocess.Popen("launchctl list", shell=True, stdout=subprocess.PIPE).stdout.read().decode():
+ subprocess.run(["launchctl", "remove", "Jellyfin-RPC"])
+
+ servicepath = os.environ["HOME"].removesuffix("/") + "/Library/LaunchAgents/jellyfinrpc.local.plist"
+ if os.path.isfile(servicepath):
+ os.remove(servicepath)
+ shutil.rmtree(path)
+ os.remove("/usr/local/bin/jellyfin-rpc")
+else:
+ if "jellyfin-rpc.service" in subprocess.Popen("systemctl --user list-units", shell=True, stdout=subprocess.PIPE).stdout.read().decode():
+ subprocess.run(["systemctl", "--user", "disable", "--now", "jellyfin-rpc.service"])
+
+ if subprocess.run(["pgrep", "-xq", "--", "'jellyfin-rpc'"]).returncode == 0:
+ subprocess.run(["killall", "jellyfin-rpc"])
+
+ servicepath = path.removesuffix("jellyfin-rpc/") + "systemd/user/jellyfin-rpc.service"
+ if os.path.isfile(servicepath):
+ subprocess.run(["rm", servicepath])
+ shutil.rmtree(path)
+ os.remove(os.environ["HOME"].removesuffix("/") + "/.local/bin/jellyfin-rpc")
+
+print("Uninstall complete!")
+sleep(5)