1 ‐ LINUX Install Scripts - SimonHeggie/FOSS-Artist-workflow-Guide GitHub Wiki
This is my opinion, the cream of the crop when it comes to a selection of the best free Linux apps for VFX work.
I have curated them together with what I believe to be some of the most powerful addons.
All this is tested with Nobara 42. Fedora and RockLinux users MAY require additional support.
Copy and paste the code into your terminal app ('Konsole' if you like KDE). Nice and simple, not too many clicks needed.
'INSTALL' contains your install script which also acts as a re-installer and updater.
'UNINSTALL' contains a script to remove all of that.
'ADDONS' Contains information on included and recommended addons.
INSTALL
# Prompt for admin password at the beginning
if [ "$EUID" -ne 0 ]; then
echo "This script requires administrative privileges. Please provide your password."
sudo -v || { echo "Failed to obtain sudo privileges. Exiting."; exit 1; }
fi
# Define variables
BLENDER_DIR="$HOME/.Applications/Blender/stable/blender-stable-baseline"
BLENDER_EXEC="$BLENDER_DIR/blender"
DESKTOP_SHORTCUT="$BLENDER_DIR/blender.desktop"
VERSION_FILE="$BLENDER_DIR/version.txt"
# Function to fetch the latest Blender version number
get_latest_version() {
curl -s https://www.blender.org/download/ | grep -oP 'blender-\K[0-9]+\.[0-9]+\.[0-9]+' | head -1
}
# Function to fetch the current installed Blender version
get_current_version() {
if [ -f "$BLENDER_EXEC" ]; then
$BLENDER_EXEC --version | head -n 1 | awk '{print $2}'
else
echo "0.0.0"
fi
}
# Function to detect GPU setup
detect_gpu_setup() {
if command -v nvidia-smi &> /dev/null; then
echo "proprietary"
elif lspci | grep -i 'VGA' | grep -i 'NVIDIA' &> /dev/null; then
echo "nouveau"
elif lspci | grep -i 'VGA' | grep -i 'AMD' &> /dev/null; then
echo "amd"
else
echo "unknown"
fi
}
# Function to fetch the latest Gather Resources addon release URL from GitHub
get_latest_gather_resources_url() {
curl -s https://api.github.com/repos/SimonHeggie/Blender-GatherResources/releases/latest |
grep "browser_download_url" |
grep ".zip" |
cut -d '"' -f 4
}
# Fetch the latest Blender version number
latest_version=$(get_latest_version)
current_version=$(get_current_version)
echo "Latest Blender version: $latest_version"
echo "Current Blender version: $current_version"
# Compare versions and download if necessary
if [ "$latest_version" != "$current_version" ]; then
echo "Updating Blender to version $latest_version..."
# Construct the download URL
download_url="https://mirror.clarkson.edu/blender/release/Blender${latest_version%.*}/blender-$latest_version-linux-x64.tar.xz"
# Download the latest Blender tarball
wget -O blender-latest.tar.xz $download_url
# Extract the downloaded tarball
mkdir -p $BLENDER_DIR
tar -xf blender-latest.tar.xz -C $HOME/.Applications/Blender/stable
mv $HOME/.Applications/Blender/stable/blender-$latest_version-linux-x64/* $BLENDER_DIR
# Clean up the tarball and temporary directory
rm -rf blender-latest.tar.xz
rm -rf $HOME/.Applications/Blender/stable/blender-$latest_version-linux-x64
# Save the current version to the version file
echo "$latest_version" > "$VERSION_FILE"
else
echo "Blender is already up-to-date. Refreshing desktop shortcut..."
fi
# Create a symbolic link to make Blender accessible from anywhere
sudo ln -sf $BLENDER_EXEC /usr/local/bin/blender
# Detect GPU setup and determine the correct Exec command
GPU_SETUP=$(detect_gpu_setup)
echo "GPU Setup detected: $GPU_SETUP"
case "$GPU_SETUP" in
"proprietary")
EXEC_COMMAND="__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia $BLENDER_EXEC"
;;
"nouveau"|"amd")
EXEC_COMMAND="DRI_PRIME=1 $BLENDER_EXEC"
;;
*)
EXEC_COMMAND="$BLENDER_EXEC"
;;
esac
echo "Using Exec command: $EXEC_COMMAND"
# Update the Exec line in the existing desktop shortcut
if [ -f "$DESKTOP_SHORTCUT" ]; then
sudo sed -i "s|^Exec=.*|Exec=$EXEC_COMMAND|" "$DESKTOP_SHORTCUT"
echo "Updated the Exec line in the desktop shortcut: $DESKTOP_SHORTCUT"
# Copy the .desktop file to ~/.local/share/applications for taskbar/menu use
cp "$DESKTOP_SHORTCUT" ~/.local/share/applications/
echo "Copied updated desktop shortcut to ~/.local/share/applications/"
# Refresh the desktop database to ensure changes take effect
sudo update-desktop-database ~/.local/share/applications
sudo update-desktop-database /usr/share/applications
echo "Desktop database refreshed."
else
echo "Desktop shortcut not found at $DESKTOP_SHORTCUT. Skipping update."
fi
echo "Verifying the desktop shortcut content:"
echo "Blender $latest_version installation and desktop shortcut refresh complete."
# ---- Dynamic Addon Directory Handling ----
# Scan for Blender version folders
blender_versions=($(ls -d $BLENDER_DIR/[0-9]* 2>/dev/null | awk -F'/' '{print $NF}' | sort -V))
# Remove all but the highest version folder
if [ ${#blender_versions[@]} -gt 1 ]; then
echo "Multiple Blender version directories detected: ${blender_versions[@]}"
for ((i = 0; i < ${#blender_versions[@]} - 1; i++)); do
echo "Removing old Blender version: ${blender_versions[i]}"
rm -rf "$BLENDER_DIR/${blender_versions[i]}"
done
fi
# Get the latest version directory
LATEST_BLENDER_VERSION="${blender_versions[-1]}"
echo "Using Blender version: $LATEST_BLENDER_VERSION"
# Construct the dynamic addons directory path
ADDONS_DIR="$BLENDER_DIR/$LATEST_BLENDER_VERSION/scripts/addons_core/"
mkdir -p "$ADDONS_DIR"
echo "Blender Addons Directory: $ADDONS_DIR"
# ---- Install Gather Resources Addon ----
# Get the latest release URL for Gather Resources
echo "Fetching latest Gather Resources addon..."
gather_resources_url=$(get_latest_gather_resources_url)
if [ -z "$gather_resources_url" ]; then
echo "Failed to fetch the Gather Resources addon URL. Skipping installation."
else
echo "Downloading Gather Resources from: $gather_resources_url"
wget -O GatherResources.zip "$gather_resources_url"
# Ensure the addons directory exists
mkdir -p "$ADDONS_DIR"
# Extract and install the addon
echo "Installing Gather Resources addon..."
unzip -o GatherResources.zip -d "$ADDONS_DIR"
rm GatherResources.zip
echo "Gather Resources addon installed successfully."
fi
echo "Installation complete! Blender is up to date, and Gather Resources addon is installed."
UNINSTALL
BLENDER_DIR="$HOME/.Applications/Blender/stable/blender-stable-baseline"
BLENDER_EXEC="$BLENDER_DIR/blender"
DESKTOP_ENTRY="$HOME/.local/share/applications/blender.desktop"
SYMLINK="/usr/local/bin/blender"
# Check if Blender directory exists
if [ -d "$BLENDER_DIR" ]; then
echo "Removing Blender directory: $BLENDER_DIR"
rm -rf "$BLENDER_DIR"
else
echo "Blender directory does not exist: $BLENDER_DIR"
fi
# Check if symbolic link exists
if [ -L "$SYMLINK" ]; then
echo "Removing symbolic link: $SYMLINK"
sudo rm "$SYMLINK"
else
echo "Symbolic link does not exist: $SYMLINK"
fi
# Check if desktop entry exists
if [ -f "$DESKTOP_ENTRY" ]; then
echo "Removing desktop entry: $DESKTOP_ENTRY"
rm "$DESKTOP_ENTRY"
else
echo "Desktop entry does not exist: $DESKTOP_ENTRY"
fi
echo "Blender uninstallation complete."
ADDONS
📦 Gather Resources: https://github.com/SimonHeggie/Blender-GatherResources
- Similar to 'Collect Resources' in After Effects. I made this.
🔧 https://github.com/Aodaruma/coa_tools2
- Opensource Alternative to Spine.
Blender is already quite capable, but these tools fill in gaps, allowing the artist to ignore more of the "must-have" paid options.
💵 Animation Layers – $30
https://blendermarket.com/products/animation-layers
- Blender’s animation layering is improving, but this is a pretty complete solution. Maya animators will appreciate it.
💵 Bake Wrangler – $25
https://blendermarket.com/products/bake-wrangler
- Fixes Blender’s baking tools and makes them even handier than other baking software. With this, you won’t really need Substance or Marmoset for baking.
💵 Better FBX Exporter – Starts at $28 for single indie users, up to $840 for unlimited studio seats. https://blendermarket.com/products/better-fbx-importer--exporter?search_id=36427533 -Essential for working with studios that don't support Gltf and USD, particularly if they are Autodesk users.
💵 Quad Remesher by Exoside – $25
https://exoside.com/
- The best auto re-topology tool for Blender that money can buy. Perfect for sculptors and fixing 3D scan data.
- This is the very same remesher used in Zbrush. One less reason to require it.

INSTALL
#!/usr/bin/env bash
# ─── Where to store install files (if run from $HOME) ────────────────
if [ "$PWD" = "$HOME" ]; then
cd "$HOME/Downloads" || exit 1
fi
bash -c '
set -euo pipefail
IFS=$'\''\n\t'\''
BOOT_DIR="$(pwd)"
INSTALL_DIR="$BOOT_DIR/krita_install"
INSTALL_SCRIPT="$INSTALL_DIR/install.sh"
UNINSTALL_SCRIPT="$INSTALL_DIR/uninstall.sh"
VERSION_FILE="$HOME/.Applications/Krita/version"
GITHUB_RELEASE_URL="https://github.com/SimonHeggie/FOSS-Artist-workflow-Guide/releases/download/v0.0.2/foss-artist-install.tar.gz"
ARCHIVE_TEMP="foss-artist-install.tar.gz"
# ─── Step 1: Ensure krita_install folder exists ─────────────────────
echo "[BOOT] Checking krita_install folder..."
if [ -d "$INSTALL_DIR" ]; then
echo "[INFO] Found existing ./krita_install — skipping download."
else
echo "[INFO] krita_install/ not found. Downloading v0.0.2 release..."
curl -L "$GITHUB_RELEASE_URL" -o "$ARCHIVE_TEMP"
echo "[INFO] Extracting installer..."
tar -xf "$ARCHIVE_TEMP" || {
echo "[ERROR] Failed to extract archive $ARCHIVE_TEMP" >&2
exit 1
}
rm -f "$ARCHIVE_TEMP"
if [ ! -d "$INSTALL_DIR" ]; then
echo "[ERROR] Expected folder '\''krita_install/'\'' not found after extraction." >&2
exit 1
fi
fi
# ─── Step 2: Validate install.sh exists ──────────────────────────────
if [ ! -f "$INSTALL_SCRIPT" ]; then
echo "[ERROR] Missing install.sh inside krita_install/ — aborting." >&2
ls -l "$INSTALL_DIR"
exit 1
fi
# ─── Step 3: Present menu ────────────────────────────────────────────
if [ ! -f "$VERSION_FILE" ]; then
echo "[INFO] Krita not installed."
echo "[I] Install Krita only"
echo "[AI] Install Krita + AI Diffusion"
echo "[AIC] Install Krita + AI + ComfyUI shortcut"
echo "[Q] Quit"
printf "Select an option: "
read -r option
case "$option" in
[Ii]) bash "$INSTALL_SCRIPT" --with-default ;;
[Aa][Ii]) bash "$INSTALL_SCRIPT" --with-ai ;;
[Aa][Ii][Cc]) bash "$INSTALL_SCRIPT" --with-ai --with-comfy ;;
[Qq]) echo "Goodbye!" && exit 0 ;;
*) echo "Invalid input." && exit 1 ;;
esac
else
echo "[INFO] Krita install detected."
echo "[INFO] Installed versions:"
grep -E '^(FOSS_INSTALL|KRITA|AIDIFFUSION|SEGMENTATIONTOOLS)=' "$VERSION_FILE"
echo ""
echo "[I] Install latest (overwrite, update version)"
echo "[AI] Add AI Diffusion"
echo "[AIC] Add AI Diffusion + ComfyUI shortcut"
echo "[D] Download archives only"
echo "[R] Repair (re-extract AppImage + reinstall addons)"
echo "[U] Uninstall (with ComfyUI backup)"
echo "[C] Complete uninstall (no backup)"
echo "[Q] Quit"
printf "Select an option: "
read -r option
case "$option" in
[Aa][Ii]) bash "$INSTALL_SCRIPT" --with-ai ;;
[Aa][Ii][Cc]) bash "$INSTALL_SCRIPT" --with-ai --with-comfy ;;
[Ii]) bash "$INSTALL_SCRIPT" --with-default ;;
[Dd]) bash "$INSTALL_SCRIPT" --download-only ;;
[Rr]) bash "$INSTALL_SCRIPT" --repair ;;
[Uu]) bash "$UNINSTALL_SCRIPT" --backup ;;
[Cc]) bash "$UNINSTALL_SCRIPT" --complete ;;
[Qq]) echo "Goodbye!" && exit 0 ;;
*) echo "Invalid input." && exit 1 ;;
esac
fi
'
AI GEN
If you want more than just segmentation (object) select with Krita...
AI generation requires some additional steps because of large download sizes, and varying set up requirements.
I still saved you from having the download the addon and activate the plugins within Krita yourself, the rest is manual.
It's not that tough to set up. But it keeps people happy who are not interested in dealing with AI.
---
1: In Krita, open a new document. Then go to: Settings/Dockers/AI Image Generation...
-That will open the docker, click 'configure' to then access the...
2: 'Connection' section.
-Powerful computer? (IE: RTX 4090)?, Don't trust your IP security on an online server?:
GO with: 'Local Managed Server'.
-Already got COMFYUI set up? Want to connect straight into your existing setup?:
Go with: 'Custom Server (local or remote)'.
-Don't have any good hardware for AI? Got some cash to spend on the cloud?
Go with : 'Online Service'
3: Server Path - If you don't know or care where you store this, leave this setting as default.
(home/USERNAME/.Applications/ComfyUI/server)
NOTE: within this path, /models will be stored, by far the biggest; so make sure you back this up.
Another folder is /venv which stores almost 10gb worth of isolated python. Backup, but be prepared to wipe if there's an issue.
The rest of this depends on how many features and control measures you'd like unlock.
You will need the core components, I recommend grabbing all workloads, then grab the models and the control-net models that you'd like.
If in doubt, if you have a decent internet connection; I recommend you tick it all and try out all these different features.
4: Hit install. If you get red text describing issues downloading something, try re-downloading, preferably with the best internet connection you can find. In totally there is almost 100gb worth of data you can download if you're serious about AI generation. Curated by ACLY.
DONE.
ADDONS
📦 krita-ai-tools/Krita Segmentation Tools: https://github.com/Acly/krita-ai-tools
- It's like Object select. AI tools for auto masking in Krita.
📦 [See above section] krita-ai-diffusion: https://github.com/Acly/krita-ai-diffusion
- AI generative art in Krita. Under the hood, this installs ComfyUI behind Krita.
- Go here for more information.

🔧 svg-merge-save:https://github.com/SimonHeggie/KritaSvgMerge/tree/main
- This just let's you export multiple layers of SVG into one flat SVG, so you don't have to flatten your work while working to export SVGs.
- I also made this :)
🔎 https://github.com/chartinger/krita-unofficial-spine-export
-Watch this space, a lot more to come.

INSTALL
# Check for Inkscape updates
sudo dnf check-update inkscape
# Optional: If you want to upgrade Inkscape specifically, uncomment the next line
# sudo dnf upgrade inkscape -y
# Installing Inkscape
echo "Installing Inkscape..."
sudo dnf install inkscape -y
# Defining Inkscape's extension directory
EXT_DIR="$HOME/.config/inkscape/extensions"
# Creating the directory if it doesn't exist
mkdir -p "$EXT_DIR"
# Downloading the InkSync addon
echo "Downloading InkSync addon..."
wget -O inksync.zip https://github.com/SimonHeggie/InkSync/archive/refs/heads/main.zip || { echo "Failed to download InkSync addon"; exit 1; }
# Unzipping and moving to Inkscape's extensions directory
echo "Installing InkSync addon..."
unzip inksync.zip -d inksync
cp -r inksync/InkSync-main/* "$EXT_DIR"
# Clean up downloaded files
rm -rf inksync
rm inksync.zip
# Creating a desktop shortcut for Inkscape with InkSync addon
echo "Creating desktop shortcut..."
if [ -d "$HOME/Desktop" ]; then
echo "[Desktop Entry]
Version=1.0
Type=Application
Name=Inkscape with InkSync
Icon=inkscape
Exec=inkscape %F
Comment=Create and edit Scalable Vector Graphics images with InkSync addon
Categories=Graphics;VectorGraphics;GTK;
Terminal=false
MimeType=image/svg+xml;image/x-eps;image/wmf;image/svg+xml-compressed;image/emf;application/pdf;
StartupNotify=true
Keywords=image;editor;vector;drawing;
TryExec=inkscape
" > $HOME/Desktop/inkscape.desktop
chmod +x $HOME/Desktop/inkscape.desktop
else
echo "Desktop directory not found. Skipping shortcut creation."
fi
echo "Installation complete! Inkscape with InkSync addon is ready."
UNINSTALL
# Uninstalling Inkscape
sudo dnf remove inkscape -y
# Removing Inkscape extensions
EXT_DIR="$HOME/.config/inkscape/extensions"
rm -rf "$EXT_DIR"
# Removing the desktop shortcut
rm -f $HOME/Desktop/inkscape.desktop
echo "Uninstallation complete! Inkscape and its extensions have been removed."
ADDONS
📦 InkSync: https://github.com/SimonHeggie/InkSync
- An exporter that focusing on making it easier to sync file embedded layers in other software (Like Krita)
- Useful for Layout of Inkscape content in Krita or similar for printing (for CMYK).
Worth looking at
📎 Inkscape ConfyUI Extension: https://inkscape.org/~nielowait/%E2%98%85inkscape-comfyui-extension
- Could be useful. (I have not checked it out)

INSTALL
INSTALL_DIR="$HOME/.Applications/FontForge"
ICON_DIR="$HOME/.local/share/icons/hicolor/scalable/apps"
DESKTOP_DIR="$HOME/.local/share/applications"
VERSION_FILE="$INSTALL_DIR/usr/share/metainfo/org.fontforge.FontForge.appdata.xml"
LOG_FILE="$INSTALL_DIR/fontforge_install.log"
# Ensure necessary directories exist
mkdir -p "$INSTALL_DIR" "$ICON_DIR" "$DESKTOP_DIR"
# Redirect stdout and stderr to the log file
exec > >(tee -a "$LOG_FILE") 2>&1
# Get the URL of the latest FontForge AppImage
FONTFORGE_URL=$(curl -s https://api.github.com/repos/fontforge/fontforge/releases/latest | jq -r '.assets[] | select(.name | test("AppImage")) | .browser_download_url')
# Get the latest version from the GitHub API
LATEST_VERSION=$(curl -s https://api.github.com/repos/fontforge/fontforge/releases/latest | jq -r '.tag_name')
# Function to get the installed version from the XML file using xmllint
get_installed_version() {
if [[ -f "$VERSION_FILE" ]]; then
xmllint --xpath 'string(//component/releases/release[1]/@version)' "$VERSION_FILE"
else
echo "none"
fi
}
# Check the installed version
INSTALLED_VERSION=$(get_installed_version)
# Download and install if the version is different
if [[ "$LATEST_VERSION" != "$INSTALLED_VERSION" ]]; then
echo "Downloading FontForge AppImage..."
curl -L "$FONTFORGE_URL" -o "$INSTALL_DIR/fontforge.AppImage"
chmod +x "$INSTALL_DIR/fontforge.AppImage"
# Extract AppImage
echo "Extracting AppImage..."
"$INSTALL_DIR/fontforge.AppImage" --appimage-extract
mv squashfs-root/* "$INSTALL_DIR/"
rm -rf squashfs-root "$INSTALL_DIR/fontforge.AppImage"
# Copy the icon to the appropriate directory
echo "Copying FontForge icon..."
ICON_SRC_PATH="$INSTALL_DIR/usr/share/icons/hicolor/scalable/apps/org.fontforge.FontForge.svg"
if [[ -f "$ICON_SRC_PATH" ]]; then
cp "$ICON_SRC_PATH" "$ICON_DIR/"
else
echo "FontForge icon not found at $ICON_SRC_PATH"
fi
# Create desktop entry
echo "Creating desktop entry..."
cat > "$DESKTOP_DIR/fontforge.desktop" <<EOL
[Desktop Entry]
Name=FontForge
Exec=$INSTALL_DIR/AppRun
Icon=$ICON_DIR/org.fontforge.FontForge.svg
Type=Application
Categories=Graphics;Font
EOL
echo "FontForge $LATEST_VERSION installed successfully."
else
echo "FontForge is already up to date (version $INSTALLED_VERSION)."
fi
# Update the icon cache if the index.theme file exists
if [[ -f "$HOME/.local/share/icons/hicolor/index.theme" ]]; then
echo "Updating icon cache..."
gtk-update-icon-cache "$HOME/.local/share/icons/hicolor"
else
echo "No theme index file found, skipping icon cache update."
fi
echo "Font Forge is installed and up to date."
UNINSTALL
# Set the installation directory to $HOME/.Applications/fontforge
INSTALL_DIR="$HOME/.Applications/FontForge"
ICON_DIR="$HOME/.local/share/icons/hicolor/scalable/apps"
DESKTOP_DIR="$HOME/.local/share/applications"
LOG_FILE="$INSTALL_DIR/fontforge_uninstall.log"
# Redirect stdout and stderr to the log file
exec > >(tee -a "$LOG_FILE") 2>&1
# Remove the FontForge installation directory
if [[ -d "$INSTALL_DIR" ]]; then
echo "Removing FontForge installation directory..."
rm -rf "$INSTALL_DIR"
else
echo "FontForge installation directory not found."
fi
# Remove the FontForge icon
ICON_PATH="$ICON_DIR/org.fontforge.FontForge.svg"
if [[ -f "$ICON_PATH" ]]; then
echo "Removing FontForge icon..."
rm "$ICON_PATH"
else
echo "FontForge icon not found."
fi
# Remove the desktop entry
DESKTOP_ENTRY="$DESKTOP_DIR/fontforge.desktop"
if [[ -f "$DESKTOP_ENTRY" ]]; then
echo "Removing FontForge desktop entry..."
rm "$DESKTOP_ENTRY"
else
echo "FontForge desktop entry not found."
fi
# Update the icon cache if the index.theme file exists
if [[ -f "$HOME/.local/share/icons/hicolor/index.theme" ]]; then
echo "Updating icon cache..."
gtk-update-icon-cache "$HOME/.local/share/icons/hicolor"
else
echo "No theme index file found, skipping icon cache update."
fi
echo "FontForge uninstallation completed."

INSTALL
MESHROOM_INSTALL_DIR="$HOME/.Applications/MeshRoom"
MESHROOM_VERSION_FILE="$MESHROOM_INSTALL_DIR/version.txt"
LOG_FILE="$MESHROOM_INSTALL_DIR/install_log.txt"
# Start logging
echo "Starting installation script..." > "$LOG_FILE"
echo "Log file: $LOG_FILE" >> "$LOG_FILE"
date >> "$LOG_FILE"
echo "==============================" >> "$LOG_FILE"
# Function to log and display messages
log() {
echo "$1" | tee -a "$LOG_FILE"
}
# Install ExifTool if not already installed
log "Checking for ExifTool installation..."
if ! command -v exiftool &> /dev/null; then
log "ExifTool not found. Installing ExifTool..."
sudo dnf install -y exiftool 2>> "$LOG_FILE"
if [ $? -ne 0 ]; then
log "Error: ExifTool installation failed."
exit 1
fi
log "ExifTool successfully installed."
else
log "ExifTool is already installed."
fi
# Function to fetch latest Meshroom release version from GitHub API
get_latest_meshroom_version() {
curl -s https://api.github.com/repos/alicevision/meshroom/releases/latest | grep -oP '"tag_name": "\K(.*)(?=")' 2>> "$LOG_FILE"
}
# Function to get the Meshroom version from the tarball filename
get_meshroom_version_from_tar() {
ls "$MESHROOM_INSTALL_DIR" | grep -oP 'Meshroom-\K[0-9]+\.[0-9]+\.[0-9]+(?=-linux.tar.gz)' | head -1 2>> "$LOG_FILE"
}
# Function to fetch current installed Meshroom version from version file
get_current_installed_version() {
if [ -f "$MESHROOM_VERSION_FILE" ]; then
cat "$MESHROOM_VERSION_FILE"
else
echo "0.0.0"
fi
}
# Fetch the latest Meshroom version number from GitHub
latest_meshroom_version=$(get_latest_meshroom_version | sed 's/^v//') # Remove 'v' prefix for filename matching
current_installed_version=$(get_current_installed_version)
log "Latest Meshroom version: $latest_meshroom_version"
log "Current installed Meshroom version: $current_installed_version"
# Log the list of tar files in the directory
log "Checking for existing tar files in $MESHROOM_INSTALL_DIR:"
ls -lah "$MESHROOM_INSTALL_DIR" | tee -a "$LOG_FILE"
# Define the tarball path
tarball_path="$HOME/Installs/Meshroom/Meshroom-$latest_meshroom_version-linux.tar.gz"
extract_dir="$MESHROOM_INSTALL_DIR/Meshroom-$latest_meshroom_version"
log "Tarball path: $tarball_path"
log "Extract directory: $extract_dir"
# Check if the tarball exists and if the extraction directory can be created
if [ "$latest_meshroom_version" != "$current_installed_version" ]; then
log "Updating Meshroom to version $latest_meshroom_version..."
# Skip download if tarball exists, otherwise download it
if [ -f "$tarball_path" ]; then
log "Tarball already exists at $tarball_path. Skipping download."
else
log "Downloading Meshroom..."
download_url="https://github.com/alicevision/meshroom/releases/download/v$latest_meshroom_version/Meshroom-$latest_meshroom_version-linux.tar.gz"
log "Download URL: $download_url"
wget -O "$tarball_path" "$download_url" 2>> "$LOG_FILE"
if [ $? -ne 0 ]; then
log "Error: Meshroom download failed."
exit 1
fi
fi
# Log before creating the extraction directory
log "Attempting to create extraction directory: $extract_dir"
mkdir -p "$extract_dir"
if [ $? -ne 0 ]; then
log "Error: Failed to create extraction directory."
exit 1
fi
log "Extraction directory created: $extract_dir"
# Log before extracting
log "Extracting Meshroom to $MESHROOM_INSTALL_DIR..."
tar -xzf "$tarball_path" -C "$MESHROOM_INSTALL_DIR" --strip-components=1 2>> "$LOG_FILE"
if [ $? -ne 0 ]; then
log "Error: Failed to extract Meshroom tarball."
exit 1
fi
log "Extraction completed."
# Save the current version to the version file
log "Saving current version to version file."
echo "$latest_meshroom_version" > "$MESHROOM_VERSION_FILE"
else
log "Meshroom is already up-to-date."
fi
# Create a symbolic link to make Meshroom accessible from anywhere
log "Creating symbolic link for Meshroom..."
sudo ln -sf "$MESHROOM_INSTALL_DIR/Meshroom" /usr/local/bin/meshroom 2>> "$LOG_FILE"
if [ $? -ne 0 ]; then
log "Error: Failed to create symbolic link."
exit 1
fi
# Locate the icon files and create the desktop entry
meshroom_icon_svg="$MESHROOM_INSTALL_DIR/lib/meshroom/ui/img/meshroom.svg"
meshroom_icon_ico="$MESHROOM_INSTALL_DIR/lib/meshroom/ui/img/meshroom.ico"
log "Checking for icon files..."
if [ -f "$meshroom_icon_svg" ] || [ -f "$meshroom_icon_ico" ]; then
log "Creating KDE desktop entry for Meshroom..."
# Prioritize SVG but use ICO if SVG is not found
icon_path="$meshroom_icon_svg"
[ -f "$meshroom_icon_ico" ] && icon_path="$meshroom_icon_ico"
DESKTOP_ENTRY="[Desktop Entry]
Version=1.0
Type=Application
Name=Meshroom
Exec=$MESHROOM_INSTALL_DIR/Meshroom
Icon=$icon_path
Terminal=false
Categories=Graphics;3DGraphics;
"
echo "$DESKTOP_ENTRY" > "$HOME/.local/share/applications/meshroom.desktop"
chmod +x "$HOME/.local/share/applications/meshroom.desktop"
log "Meshroom $latest_meshroom_version desktop entry created with icon $icon_path."
else
log "Meshroom icon not found. Skipping desktop entry creation."
fi
log "Script complete."
UNINSTALL
MESHROOM_INSTALL_DIR="$HOME/.Applications/MeshRoom"
MESHROOM_VERSION_FILE="$MESHROOM_INSTALL_DIR/version.txt"
LOG_FILE="$MESHROOM_INSTALL_DIR/install_log.txt"
MESHROOM_DESKTOP_ENTRY="$HOME/.local/share/applications/meshroom.desktop"
SYMLINK_PATH="/usr/local/bin/meshroom"
# Function to log and display messages
log() {
echo "$1"
}
log "Starting Meshroom uninstallation..."
# Check if Meshroom is installed
if [ -d "$MESHROOM_INSTALL_DIR" ]; then
log "Removing Meshroom installation directory: $MESHROOM_INSTALL_DIR"
rm -rf "$MESHROOM_INSTALL_DIR"
log "Meshroom directory removed."
else
log "Meshroom installation directory not found. Skipping removal."
fi
# Remove the symbolic link if it exists
if [ -L "$SYMLINK_PATH" ]; then
log "Removing Meshroom symbolic link: $SYMLINK_PATH"
sudo rm -f "$SYMLINK_PATH"
log "Symbolic link removed."
else
log "Meshroom symbolic link not found. Skipping removal."
fi
# Remove desktop entry if it exists
if [ -f "$MESHROOM_DESKTOP_ENTRY" ]; then
log "Removing Meshroom desktop entry: $MESHROOM_DESKTOP_ENTRY"
rm -f "$MESHROOM_DESKTOP_ENTRY"
log "Desktop entry removed."
else
log "Meshroom desktop entry not found. Skipping removal."
fi
# Remove the log file if it exists
if [ -f "$LOG_FILE" ]; then
log "Removing Meshroom log file: $LOG_FILE"
rm -f "$LOG_FILE"
log "Log file removed."
else
log "Meshroom log file not found. Skipping removal."
fi
log "Meshroom uninstallation complete."

INSTALL
LOGFILE="$HOME/.Applications/BlenderLauncher/install_log.txt"
INSTALL_DIR="$HOME/.Applications/BlenderLauncher"
LATEST_RELEASE_URL="https://api.github.com/repos/Victor-IX/Blender-Launcher-V2/releases/latest"
ICON_URL="https://github.com/Victor-IX/Blender-Launcher-V2/raw/main/docs/mkdocs/icons/favicon.ico"
DESKTOP_ENTRY="$HOME/.local/share/applications/blender-launcher.desktop"
# Function to log messages
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOGFILE"
}
log "Installing Blender Launcher to $INSTALL_DIR (hidden in your home directory)"
mkdir -p "$INSTALL_DIR" || { log "Failed to create install directory"; exit 1; }
cd "$INSTALL_DIR" || { log "Failed to change directory to install directory"; exit 1; }
log "Fetching latest Blender Launcher release information"
LATEST_RELEASE=$(curl -s $LATEST_RELEASE_URL)
LATEST_VERSION=$(echo $LATEST_RELEASE | grep -oP '"tag_name": "\K(.*)(?=")')
LATEST_DOWNLOAD_URL=$(echo $LATEST_RELEASE | grep -oP '"browser_download_url": "\K(.*Linux_x64.zip)(?=")')
if [ -z "$LATEST_VERSION" ] || [ -z "$LATEST_DOWNLOAD_URL" ]; then
log "Failed to fetch the latest version or download URL from $LATEST_RELEASE_URL"
exit 1
fi
log "Latest Blender Launcher version: $LATEST_VERSION"
log "Latest Blender Launcher download URL: $LATEST_DOWNLOAD_URL"
ZIP_FILE="Blender_Launcher_$LATEST_VERSION_Linux_x64.zip"
if [ -f "$ZIP_FILE" ]; then
log "The latest version ($LATEST_VERSION) is already downloaded."
else
log "Downloading Blender Launcher $LATEST_VERSION"
wget -O "$ZIP_FILE" "$LATEST_DOWNLOAD_URL" || { log "Failed to download Blender Launcher"; exit 1; }
fi
log "Extracting Blender Launcher"
unzip -o "$ZIP_FILE" -d "$INSTALL_DIR" || { log "Failed to extract Blender Launcher"; exit 1; }
EXECUTABLE_PATH="$INSTALL_DIR/Blender Launcher"
if [ ! -f "$EXECUTABLE_PATH" ]; then
log "Could not find the Blender Launcher executable at $EXECUTABLE_PATH"
exit 1
fi
log "Blender Launcher executable path: $EXECUTABLE_PATH"
log "Downloading icon"
ICON_PATH="$INSTALL_DIR/favicon.ico"
wget -O "$ICON_PATH" "$ICON_URL" || { log "Failed to download icon"; exit 1; }
log "Creating desktop entry"
cat > "$DESKTOP_ENTRY" <<EOL
[Desktop Entry]
Name=Blender Launcher
Exec="$EXECUTABLE_PATH"
Icon=$ICON_PATH
Type=Application
Categories=Graphics;
EOL
log "DONE! Installed Blender Launcher."
UNINSTALL
INSTALL_DIR="$HOME/.Applications/BlenderLauncher"
BLENDER_DIR="$HOME/.Applications/Blender"
DESKTOP_ENTRY="$HOME/.local/share/applications/blender-launcher.desktop"
# Function to log messages
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOGFILE"
}
log "Starting uninstallation of Blender Launcher"
# Prompt the user for whether to remove the Blender directory
read -p "Do you want to remove the entire Blender directory and all its versions? (Y/N): " remove_blender
if [ "$remove_blender" = "Y" ] || [ "$remove_blender" = "y" ]; then
log "Removing entire Blender directory: $BLENDER_DIR"
rm -rf "$BLENDER_DIR" || { log "Failed to remove Blender directory"; exit 1; }
else
log "Keeping Blender directory: $BLENDER_DIR"
fi
# Remove Blender Launcher installation directory
log "Removing Blender Launcher directory: $INSTALL_DIR"
rm -rf "$INSTALL_DIR" || { log "Failed to remove Blender Launcher directory"; exit 1; }
# Remove desktop entry
log "Removing desktop entry: $DESKTOP_ENTRY"
rm -f "$DESKTOP_ENTRY" || { log "Failed to remove desktop entry"; exit 1; }
log "DONE! Uninstalled Blender Launcher."
echo "Uninstallation complete. Please check the log file for details: $LOGFILE"
Note: This install script is not yet complete, will complete at some point.
INSTALL
# === PATHS ===
APPDIR="$HOME/.Applications/Tenacity"
EXTRACT_DIR="$APPDIR/squashfs-root"
APPIMAGE="$APPDIR/tenacity-linux-v1.3.4.AppImage"
DESKTOP_ENTRY="$HOME/.local/share/applications/tenacity.desktop"
ICON_SOURCE="$EXTRACT_DIR/share/pixmaps/tenacity32.xpm"
ICON_NAME="tenacity32"
LOGFILE="$APPDIR/install_log.txt"
# === LOGGING ===
mkdir -p "$APPDIR"
touch "$LOGFILE"
exec > >(tee -a "$LOGFILE") 2>&1
echo "[INFO] Installing Tenacity..."
# === CHECK APPIMAGE ===
if [ ! -f "$APPIMAGE" ]; then
echo "[ERROR] AppImage not found: $APPIMAGE"
exit 1
fi
# === MAKE EXECUTABLE ===
chmod +x "$APPIMAGE"
# === EXTRACT IF NEEDED ===
if [ ! -d "$EXTRACT_DIR" ]; then
echo "[INFO] Extracting AppImage..."
"$APPIMAGE" --appimage-extract
if [ ! -d squashfs-root ]; then
echo "[ERROR] AppImage extraction failed."
exit 1
fi
mv squashfs-root "$EXTRACT_DIR"
else
echo "[INFO] Using existing extracted AppImage directory."
fi
# === VERIFY AppRun ===
EXEC_PATH="$EXTRACT_DIR/AppRun"
if [ ! -f "$EXEC_PATH" ]; then
echo "[ERROR] AppRun not found at expected path."
exit 1
fi
# === ICON HANDLING ===
if [ -f "$ICON_SOURCE" ]; then
echo "[INFO] Found icon at: $ICON_SOURCE"
ICON_PATH="$ICON_SOURCE"
else
echo "[WARNING] Icon not found at expected location. Using fallback."
ICON_NAME="audio-x-generic"
fi
# === CREATE DESKTOP ENTRY ===
echo "[INFO] Creating desktop entry..."
mkdir -p "$(dirname "$DESKTOP_ENTRY")"
cat <<EOF > "$DESKTOP_ENTRY"
[Desktop Entry]
Name=Tenacity
Exec=$EXEC_PATH
Icon=${ICON_PATH:-$ICON_NAME}
Type=Application
Categories=AudioVideo;Audio;
Terminal=false
StartupNotify=true
EOF
chmod +x "$DESKTOP_ENTRY"
# === REFRESH MENU DATABASE ===
update-desktop-database ~/.local/share/applications/ > /dev/null 2>&1
echo "[INFO] Installation complete. Tenacity should now appear in your application menu."

INSTALL
pause_exit() {
echo "Press Enter to exit..."
read -r
exit "$1"
}
# Prompt for admin password at the beginning
if [ "$EUID" -ne 0 ]; then
echo "This script requires administrative privileges. Please provide your password."
sudo -v || { echo "Failed to obtain sudo privileges."; pause_exit 1; }
fi
# Function to detect the Linux distribution
detect_distro() {
if [ -f /etc/os-release ]; then
. /etc/os-release
echo "$ID"
else
echo "unknown"
fi
}
# Function to check hardware virtualization support
check_virtualization_support() {
VT_X=$(lscpu | grep -i "Virtualization" | awk '{print $2}')
VT_D=$(sudo dmesg | grep -i "IOMMU" | wc -l)
if [[ "$VT_X" == "VT-x" || "$VT_X" == "AMD-V" ]]; then
echo "Hardware virtualization ($VT_X) is enabled."
else
echo "No hardware virtualization detected. Enable VT-x/AMD-V in BIOS!"
echo "Restart your laptop, enter BIOS (F2 or Fn + F2 on Lenovo Legion), and enable:"
echo " - Intel Virtualization Technology (VT-x)"
echo " - Intel VT-d (IOMMU) for PCI passthrough (optional)"
pause_exit 1
fi
if [[ "$VT_D" -gt 0 ]]; then
echo "Intel VT-d (IOMMU) is enabled."
else
echo "Intel VT-d (IOMMU) not detected. Enable it in BIOS for PCI passthrough support."
fi
}
# Function to install KVM based on distribution
install_kvm() {
DISTRO=$(detect_distro)
echo "Detected Linux distribution: $DISTRO"
case "$DISTRO" in
"fedora"|"rocky"|"nobara")
sudo dnf install -y qemu-kvm libvirt virt-install virt-manager virt-viewer \
edk2-ovmf swtpm qemu-img guestfs-tools libosinfo tuned
;;
"ubuntu"|"debian")
sudo apt install -y qemu-system-x86 libvirt-daemon-system virtinst \
virt-manager virt-viewer ovmf swtpm qemu-utils guestfs-tools \
libosinfo-bin tuned
;;
"arch")
sudo pacman -S --noconfirm qemu-full libvirt virt-install virt-manager virt-viewer \
edk2-ovmf swtpm qemu-img guestfs-tools libosinfo
yay -S --noconfirm tuned
;;
*)
echo "Unsupported distribution. Install KVM manually."
pause_exit 1
;;
esac
}
# Function to enable and start libvirt service
enable_libvirt_services() {
echo "Enabling and starting Libvirt services..."
systemctl enable --now libvirtd
systemctl start libvirtd
}
# Function to configure TuneD for virtualization performance
optimize_with_tuned() {
echo "Configuring TuneD for virtualization..."
systemctl enable --now tuned
tuned-adm profile virtual-host
}
# Function to configure user permissions for libvirt
configure_user_permissions() {
echo "Granting user permissions for Libvirt..."
sudo usermod -aG libvirt $USER
echo "export LIBVIRT_DEFAULT_URI='qemu:///system'" >> ~/.bashrc
source ~/.bashrc
}
# Function to set proper ACL permissions for VM storage
set_acl_permissions() {
echo "Configuring ACL for VM storage directory..."
sudo setfacl -R -b /var/lib/libvirt/images
sudo setfacl -R -m u:$USER:rwX /var/lib/libvirt/images
sudo setfacl -m d:u:$USER:rwx /var/lib/libvirt/images
}
# Function to verify KVM installation
verify_kvm_installation() {
echo "Verifying KVM installation..."
sudo virt-host-validate qemu
}
# Main script execution
check_virtualization_support
install_kvm
enable_libvirt_services
optimize_with_tuned
configure_user_permissions
set_acl_permissions
verify_kvm_installation
echo "KVM installation and configuration complete. Reboot your system for changes to take effect."
pause_exit 0
# ---- Guide for New Users ----
cat <<EOF
======================================================
POST-INSTALLATION INSTRUCTIONS
======================================================
If your virtual machines are not working, ensure the
**libvirtd** service is running and enabled on boot.
To manually start the virtualization service, run:
sudo systemctl start libvirtd.service
To check if the service is running, use:
sudo systemctl status libvirtd.service
To enable it on boot so it starts automatically:
sudo systemctl enable libvirtd.service
If you encounter issues, try rebooting your system
after running the above commands.
EOF
UNINSTALL
pause_exit() {
echo "Press Enter to exit..."
read -r
exit "$1"
}
# Prompt for admin password at the beginning
if [ "$EUID" -ne 0 ]; then
echo "This script requires administrative privileges. Please provide your password."
sudo -v || { echo "Failed to obtain sudo privileges."; pause_exit 1; }
fi
# Function to detect the Linux distribution
detect_distro() {
if [ -f /etc/os-release ]; then
. /etc/os-release
echo "$ID"
else
echo "unknown"
fi
}
# Function to stop and disable libvirtd service
stop_libvirt_services() {
echo "Stopping and disabling libvirt services..."
sudo systemctl stop libvirtd
sudo systemctl disable libvirtd
}
# Function to remove KVM and related packages
uninstall_kvm() {
DISTRO=$(detect_distro)
echo "Detected Linux distribution: $DISTRO"
case "$DISTRO" in
"fedora"|"rocky"|"nobara")
sudo dnf remove -y qemu-kvm libvirt virt-install virt-manager virt-viewer \
edk2-ovmf swtpm qemu-img guestfs-tools libosinfo tuned
;;
"ubuntu"|"debian")
sudo apt remove -y qemu-system-x86 libvirt-daemon-system virtinst \
virt-manager virt-viewer ovmf swtpm qemu-utils guestfs-tools \
libosinfo-bin tuned
sudo apt autoremove -y
;;
"arch")
sudo pacman -Rns --noconfirm qemu-full libvirt virt-install virt-manager virt-viewer \
edk2-ovmf swtpm qemu-img guestfs-tools libosinfo tuned
;;
*)
echo "Unsupported distribution. Uninstall manually."
pause_exit 1
;;
esac
}
# Function to remove user from libvirt group
remove_libvirt_user() {
echo "Removing user from libvirt group..."
sudo gpasswd -d "$USER" libvirt
}
# Function to delete VM storage directories
remove_vm_storage() {
echo "Deleting VM storage directories..."
sudo rm -rf /var/lib/libvirt/images
sudo rm -rf ~/.config/libvirt
}
# Function to clean up configuration files
remove_config_files() {
echo "Removing libvirt and QEMU configuration files..."
sudo rm -rf /etc/libvirt
sudo rm -rf /etc/qemu
}
# Main script execution
stop_libvirt_services
uninstall_kvm
remove_libvirt_user
remove_vm_storage
remove_config_files
echo "KVM, QEMU, and all virtualization services have been uninstalled."
pause_exit 0