Merge branch 'linux' of https://github.com/techjarves/Local-AI-Image-Generator into linux
# Conflicts: # scripts/serve.cjs
This commit is contained in:
commit
fa20dc7706
7 files changed
+288
-57
No files matched your search
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Local AI Image Generator - Compile backends from source
|
||||
# For systems with GLIBC < 2.38 (e.g. Ubuntu 22.04)
|
||||
# For Linux systems with GLIBC < 2.38 or macOS systems that need a local Metal build.
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
@@ -9,8 +9,14 @@ set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
APP_DIR="$ROOT_DIR/app"
|
||||
BACKEND_DIR="$APP_DIR/backend/linux"
|
||||
PLATFORM="$(uname -s)"
|
||||
if [ "$PLATFORM" = "Darwin" ]; then
|
||||
BACKEND_DIR="$APP_DIR/backend/mac"
|
||||
else
|
||||
BACKEND_DIR="$APP_DIR/backend/linux"
|
||||
fi
|
||||
BUILD_DIR="$APP_DIR/tools/build-sd"
|
||||
JOBS="$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)"
|
||||
|
||||
# Pinned release tag
|
||||
PINNED_TAG="master-685-19bdfe2"
|
||||
@@ -35,12 +41,36 @@ git fetch origin
|
||||
git checkout -f "$PINNED_TAG"
|
||||
git submodule update --init --recursive
|
||||
|
||||
if [ "$PLATFORM" = "Darwin" ]; then
|
||||
echo ""
|
||||
echo "=== Building macOS Metal Backend ==="
|
||||
rm -rf build-metal && mkdir build-metal && cd build-metal
|
||||
cmake .. -DSD_METAL=ON -DSD_BUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build . --config Release -j"$JOBS"
|
||||
|
||||
echo "Copying macOS Metal backend..."
|
||||
mkdir -p "$BACKEND_DIR"
|
||||
if [ -f bin/sd-server ]; then
|
||||
cp bin/sd-server "$BACKEND_DIR/sd"
|
||||
elif [ -f bin/sd ]; then
|
||||
cp bin/sd "$BACKEND_DIR/sd"
|
||||
else
|
||||
echo "ERROR: bin/sd-server or bin/sd was not found!"
|
||||
exit 1
|
||||
fi
|
||||
find . -name "*.dylib" -exec cp {} "$BACKEND_DIR/" \; 2>/dev/null || true
|
||||
chmod +x "$BACKEND_DIR/sd"
|
||||
echo ""
|
||||
echo "=== macOS Metal build completed successfully! ==="
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 2. Build CPU Backend
|
||||
echo ""
|
||||
echo "=== Building CPU Backend ==="
|
||||
rm -rf build-cpu && mkdir build-cpu && cd build-cpu
|
||||
cmake .. -DSD_BUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build . --config Release -j$(nproc)
|
||||
cmake --build . --config Release -j"$JOBS"
|
||||
|
||||
echo "Copying CPU binaries..."
|
||||
mkdir -p "$BACKEND_DIR/cpu"
|
||||
@@ -61,6 +91,10 @@ SO_PATH=$(find . -name "libstable-diffusion.so" | head -n 1)
|
||||
if [ -n "$SO_PATH" ]; then
|
||||
cp "$SO_PATH" "$BACKEND_DIR/cpu/"
|
||||
echo "Copied libstable-diffusion.so to CPU directory."
|
||||
chmod +x "$BACKEND_DIR/cpu/sd-cpu" "$BACKEND_DIR/cpu/sd-server-cpu" 2>/dev/null || true
|
||||
if [ -f "$BACKEND_DIR/cpu/sd-cli-cpu" ]; then
|
||||
chmod +x "$BACKEND_DIR/cpu/sd-cli-cpu" 2>/dev/null || true
|
||||
fi
|
||||
else
|
||||
echo "WARNING: libstable-diffusion.so not found for CPU build!"
|
||||
fi
|
||||
@@ -71,7 +105,7 @@ echo ""
|
||||
echo "=== Building Vulkan Backend ==="
|
||||
if rm -rf build-vulkan && mkdir build-vulkan && cd build-vulkan && \
|
||||
cmake .. -DSD_VULKAN=ON -DSD_BUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release && \
|
||||
cmake --build . --config Release -j$(nproc); then
|
||||
cmake --build . --config Release -j"$JOBS"; then
|
||||
|
||||
echo "Copying Vulkan binaries..."
|
||||
mkdir -p "$BACKEND_DIR/vulkan"
|
||||
@@ -94,6 +128,10 @@ if rm -rf build-vulkan && mkdir build-vulkan && cd build-vulkan && \
|
||||
else
|
||||
echo "WARNING: libstable-diffusion.so not found for Vulkan build!"
|
||||
fi
|
||||
chmod +x "$BACKEND_DIR/vulkan/sd-vulkan" "$BACKEND_DIR/vulkan/sd-server-vulkan" 2>/dev/null || true
|
||||
if [ -f "$BACKEND_DIR/vulkan/sd-cli-vulkan" ]; then
|
||||
chmod +x "$BACKEND_DIR/vulkan/sd-cli-vulkan" 2>/dev/null || true
|
||||
fi
|
||||
else
|
||||
echo "WARNING: Vulkan backend build failed. Bypassing Vulkan backend compilation..."
|
||||
fi
|
||||
@@ -102,7 +140,7 @@ echo ""
|
||||
echo "=== Building CUDA Backend ==="
|
||||
rm -rf build-cuda && mkdir build-cuda && cd build-cuda
|
||||
cmake .. -DSD_CUDA=ON -DSD_BUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build . --config Release -j$(nproc)
|
||||
cmake --build . --config Release -j"$JOBS"
|
||||
|
||||
echo "Copying CUDA binaries..."
|
||||
mkdir -p "$BACKEND_DIR/cuda"
|
||||
@@ -122,6 +160,10 @@ SO_PATH_CUDA=$(find . -name "libstable-diffusion.so" | head -n 1)
|
||||
if [ -n "$SO_PATH_CUDA" ]; then
|
||||
cp "$SO_PATH_CUDA" "$BACKEND_DIR/cuda/"
|
||||
echo "Copied libstable-diffusion.so to CUDA directory."
|
||||
chmod +x "$BACKEND_DIR/cuda/sd-cuda" "$BACKEND_DIR/cuda/sd-server-cuda" 2>/dev/null || true
|
||||
if [ -f "$BACKEND_DIR/cuda/sd-cli-cuda" ]; then
|
||||
chmod +x "$BACKEND_DIR/cuda/sd-cli-cuda" 2>/dev/null || true
|
||||
fi
|
||||
else
|
||||
echo "WARNING: libstable-diffusion.so not found for CUDA build!"
|
||||
fi
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Local AI Image Generator - Linux Reset Script
|
||||
# Local AI Image Generator - Linux/macOS Reset Script
|
||||
# Resets portable app dependencies/builds while preserving user models and outputs.
|
||||
#
|
||||
|
||||
|
||||
+92
-13
@@ -28,10 +28,10 @@ const BACKEND_PATHS = {
|
||||
cuda: path.join(ROOT, "app", "backend", "win", "cuda", "sd-cuda.exe"),
|
||||
vulkan: path.join(ROOT, "app", "backend", "win", "vulkan", "sd-vulkan.exe"),
|
||||
mac: path.join(ROOT, "app", "backend", "mac", "sd"),
|
||||
linuxCpu: path.join(ROOT, "app", "backend", "linux", "cpu", "sd-cpu"),
|
||||
linuxVulkan: path.join(ROOT, "app", "backend", "linux", "vulkan", "sd-vulkan"),
|
||||
linuxRocm: path.join(ROOT, "app", "backend", "linux", "rocm", "sd-rocm"),
|
||||
linuxCuda: path.join(ROOT, "app", "backend", "linux", "cuda", "sd-cuda"),
|
||||
linuxCpu: path.join(ROOT, "app", "backend", "linux", "cpu", "sd-server-cpu"),
|
||||
linuxVulkan: path.join(ROOT, "app", "backend", "linux", "vulkan", "sd-server-vulkan"),
|
||||
linuxRocm: path.join(ROOT, "app", "backend", "linux", "rocm", "sd-server-rocm"),
|
||||
linuxCuda: path.join(ROOT, "app", "backend", "linux", "cuda", "sd-server-cuda"),
|
||||
};
|
||||
let BACKEND_PATH = "";
|
||||
const backendSupportsFlags = {};
|
||||
@@ -189,6 +189,21 @@ function getGpuInfo() {
|
||||
}
|
||||
}
|
||||
|
||||
if (osPlatform === "darwin") {
|
||||
try {
|
||||
const output = execSync(
|
||||
"system_profiler SPDisplaysDataType | awk -F': ' '/Chipset Model|Vendor/ {print $2; exit}'",
|
||||
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] },
|
||||
).trim();
|
||||
if (output) {
|
||||
cachedGpuInfo = { name: output };
|
||||
return cachedGpuInfo;
|
||||
}
|
||||
} catch (_) {}
|
||||
cachedGpuInfo = { name: "Apple Metal GPU" };
|
||||
return cachedGpuInfo;
|
||||
}
|
||||
|
||||
cachedGpuInfo = { name: "Unavailable" };
|
||||
return cachedGpuInfo;
|
||||
}
|
||||
@@ -589,8 +604,14 @@ function getBackendOptions() {
|
||||
const rocmInstalled = osPlatform === "linux" && fs.existsSync(BACKEND_PATHS.linuxRocm);
|
||||
const rocmAvailable = rocmInstalled && hasAmdGpu() && backendAccepts(BACKEND_PATHS.linuxRocm, "rocm");
|
||||
const cpuInstalled = osPlatform === "linux" && fs.existsSync(BACKEND_PATHS.linuxCpu);
|
||||
const metalInstalled = osPlatform === "darwin" && fs.existsSync(BACKEND_PATHS.mac);
|
||||
// Darwin release binaries are Metal builds. Treat an installed macOS backend
|
||||
// as Metal-capable so the UI does not default to CPU if a probe flag changes
|
||||
// upstream.
|
||||
const metalAvailable = metalInstalled;
|
||||
|
||||
const options = [{ id: "cpu", label: "CPU", available: true }];
|
||||
if (metalAvailable) options.push({ id: "metal", label: "Metal GPU", available: true });
|
||||
if (vulkanAvailable) options.push({ id: "vulkan", label: "Vulkan GPU", available: true });
|
||||
if (rocmAvailable) options.push({ id: "rocm", label: "ROCm GPU (AMD)", available: true });
|
||||
if (cudaAvailable) options.push({ id: "cuda", label: "CUDA GPU", available: true });
|
||||
@@ -605,9 +626,14 @@ function getBackendOptions() {
|
||||
if (rocmInstalled && !rocmAvailable) {
|
||||
unavailable.push({ id: "rocm", label: "ROCm GPU (AMD)", reason: "Installed, but ROCm backend validation failed." });
|
||||
}
|
||||
if (metalInstalled && !metalAvailable) {
|
||||
unavailable.push({ id: "metal", label: "Metal GPU", reason: "Installed, but Metal backend validation failed." });
|
||||
}
|
||||
|
||||
let defaultBackend = "cpu";
|
||||
if (cudaAvailable) {
|
||||
if (metalAvailable) {
|
||||
defaultBackend = "metal";
|
||||
} else if (cudaAvailable) {
|
||||
const gpuName = String(getGpuInfo().name).toLowerCase();
|
||||
const isGtxCard = gpuName.includes("gtx");
|
||||
if (isGtxCard && vulkanAvailable) {
|
||||
@@ -627,6 +653,7 @@ function getBackendOptions() {
|
||||
cudaAvailable,
|
||||
vulkanAvailable,
|
||||
rocmAvailable,
|
||||
metalAvailable,
|
||||
defaultBackendType: defaultBackend,
|
||||
};
|
||||
return cachedBackendOptions;
|
||||
@@ -635,17 +662,22 @@ function getBackendOptions() {
|
||||
function backendAccepts(binaryPath, backendName) {
|
||||
if (!binaryPath || !fs.existsSync(binaryPath)) return false;
|
||||
try {
|
||||
const cliBackendName = backendName === "metal" ? "metal0" : backendName;
|
||||
const probeArgs = [
|
||||
"--backend", backendName,
|
||||
"--params-backend", backendName,
|
||||
"--backend", cliBackendName,
|
||||
"--params-backend", cliBackendName,
|
||||
"--model", path.join(MODELS, "__backend_probe_missing__.safetensors"),
|
||||
"--listen-port", "18082",
|
||||
];
|
||||
const spawnEnv = { ...process.env };
|
||||
if (osPlatform === "linux") {
|
||||
const extraLibs = [path.dirname(binaryPath)];
|
||||
const dir = path.dirname(binaryPath);
|
||||
const existing = spawnEnv.LD_LIBRARY_PATH || "";
|
||||
spawnEnv.LD_LIBRARY_PATH = extraLibs.join(":") + (existing ? ":" + existing : "");
|
||||
spawnEnv.LD_LIBRARY_PATH = dir + (existing ? ":" + existing : "");
|
||||
} else if (osPlatform === "darwin") {
|
||||
const dir = path.dirname(binaryPath);
|
||||
const existing = spawnEnv.DYLD_LIBRARY_PATH || "";
|
||||
spawnEnv.DYLD_LIBRARY_PATH = dir + (existing ? ":" + existing : "");
|
||||
}
|
||||
let result = spawnSync(binaryPath, probeArgs, { env: spawnEnv, encoding: "utf8", timeout: 5000 });
|
||||
let output = `${result.stdout || ""}\n${result.stderr || ""}`;
|
||||
@@ -664,7 +696,7 @@ function backendAccepts(binaryPath, backendName) {
|
||||
}
|
||||
|
||||
const lower = output.toLowerCase();
|
||||
if (lower.includes("backend config failed") || output.includes(`backend '${backendName}' was not found`)) {
|
||||
if (lower.includes("backend config failed") || output.includes(`backend '${backendName}' was not found`) || output.includes(`backend '${cliBackendName}' was not found`)) {
|
||||
return false;
|
||||
}
|
||||
// Reject binaries that fail at the dynamic linker / glibc level.
|
||||
@@ -704,7 +736,11 @@ function selectBackendPath(useGpu, backendType = "auto") {
|
||||
if (fs.existsSync(BACKEND_PATHS.linuxCpu)) return BACKEND_PATHS.linuxCpu;
|
||||
return BACKEND_PATH;
|
||||
}
|
||||
// macOS fallback
|
||||
if (osPlatform === "darwin") {
|
||||
if (fs.existsSync(BACKEND_PATHS.mac)) return BACKEND_PATHS.mac;
|
||||
return BACKEND_PATH;
|
||||
}
|
||||
// generic fallback
|
||||
if (fs.existsSync(BACKEND_PATHS.mac)) return BACKEND_PATHS.mac;
|
||||
return BACKEND_PATH;
|
||||
}
|
||||
@@ -722,6 +758,7 @@ function getBackendMode(backendPath, useGpu, backendType = "auto") {
|
||||
if (name.includes("cuda")) return "CUDA GPU";
|
||||
if (name.includes("rocm")) return "ROCm GPU";
|
||||
if (name.includes("vulkan")) return "Vulkan GPU";
|
||||
if (osPlatform === "darwin" || backendType === "metal") return "Metal GPU";
|
||||
return "GPU";
|
||||
}
|
||||
|
||||
@@ -887,6 +924,11 @@ async function startBackend(settings = {}) {
|
||||
args.push("--backend", "rocm0", "--params-backend", "rocm0");
|
||||
}
|
||||
args.push("--rng", "cpu", "--sampler-rng", "cpu");
|
||||
} else if (requestedBackend === "metal") {
|
||||
if (supportsFlags) {
|
||||
args.push("--backend", "metal0", "--params-backend", "metal0");
|
||||
}
|
||||
args.push("--rng", "cpu", "--sampler-rng", "cpu");
|
||||
}
|
||||
|
||||
if (currentSettings.vaeTiling) {
|
||||
@@ -914,6 +956,10 @@ async function startBackend(settings = {}) {
|
||||
const existing = spawnEnv.LD_LIBRARY_PATH || "";
|
||||
spawnEnv.LD_LIBRARY_PATH = extraLibs.join(":") + (existing ? ":" + existing : "");
|
||||
}
|
||||
} else if (osPlatform === "darwin") {
|
||||
const existing = spawnEnv.DYLD_LIBRARY_PATH || "";
|
||||
const backendDir = path.dirname(BACKEND_PATHS.mac);
|
||||
spawnEnv.DYLD_LIBRARY_PATH = backendDir + (existing ? ":" + existing : "");
|
||||
}
|
||||
|
||||
console.log(" [backend] Starting:", path.basename(backendPath), args.join(" "));
|
||||
@@ -1008,6 +1054,11 @@ async function startBackend(settings = {}) {
|
||||
backendLoadState.device = deviceMatch[1].trim();
|
||||
currentSettings.backendDevice = backendLoadState.device;
|
||||
}
|
||||
const metalDeviceMatch = cleanOutput.match(/GPU name:\s*([^\r\n]+)/);
|
||||
if (metalDeviceMatch) {
|
||||
backendLoadState.device = metalDeviceMatch[1].trim();
|
||||
currentSettings.backendDevice = backendLoadState.device;
|
||||
}
|
||||
if (cleanOutput.includes("ggml_cuda_init")) {
|
||||
backendLoadState.backendMode = "CUDA GPU";
|
||||
currentSettings.backendMode = "CUDA GPU";
|
||||
@@ -1020,6 +1071,10 @@ async function startBackend(settings = {}) {
|
||||
backendLoadState.backendMode = "ROCm GPU";
|
||||
currentSettings.backendMode = "ROCm GPU";
|
||||
}
|
||||
if (cleanOutput.includes("ggml_metal")) {
|
||||
backendLoadState.backendMode = "Metal GPU";
|
||||
currentSettings.backendMode = "Metal GPU";
|
||||
}
|
||||
if (cleanOutput.includes("[ERROR]")) {
|
||||
const nextError = describeBackendError(cleanOutput.trim(), currentSettings.model);
|
||||
const hasSpecificFileError = String(backendError || "").includes("incomplete or corrupted");
|
||||
@@ -1035,7 +1090,7 @@ async function startBackend(settings = {}) {
|
||||
console.log(" [backend] exited with code", code);
|
||||
if (code !== null && code !== 0) {
|
||||
if (!backendError) {
|
||||
backendError = `exited with code ${code}`;
|
||||
backendError = describeBackendExitCode(code, currentSettings.backendBinary || BACKEND_PATH);
|
||||
}
|
||||
}
|
||||
backendLoadState.active = false;
|
||||
@@ -1390,6 +1445,30 @@ function describeBackendError(rawError, modelPath) {
|
||||
return `${raw}\n\nThe backend could not create the model context. Common causes are a corrupt/incomplete model file, unsupported checkpoint type, or not enough free RAM/VRAM. Delete and re-download the model, then try CPU or Vulkan mode at 512x512.`;
|
||||
}
|
||||
|
||||
function describeBackendExitCode(code, backendPath) {
|
||||
const numericCode = Number(code);
|
||||
if (osPlatform === "win32" && numericCode === 3221225781) {
|
||||
const backendName = path.basename(backendPath || BACKEND_PATH || "backend");
|
||||
const lowerBackend = backendName.toLowerCase();
|
||||
const isVulkan = lowerBackend.includes("vulkan");
|
||||
const isCuda = lowerBackend.includes("cuda");
|
||||
const likelyMissing = isVulkan
|
||||
? "the Vulkan runtime/driver DLL, such as vulkan-1.dll"
|
||||
: isCuda
|
||||
? "a CUDA runtime DLL or NVIDIA driver component"
|
||||
: "a required backend DLL";
|
||||
const driverHint = isVulkan
|
||||
? "Install or update the GPU vendor driver with Vulkan support, then run setup again so the Vulkan backend folder is repaired."
|
||||
: isCuda
|
||||
? "Install or update the NVIDIA driver, then run setup again so the CUDA backend folder is repaired."
|
||||
: "Update the GPU driver, then run setup again so the backend folder is repaired.";
|
||||
|
||||
return `exited with code ${code} (0xC0000135: required DLL not found).\n\nWindows could not start ${backendName} because ${likelyMissing} is missing or not loadable. ${driverHint}\n\nIf you are using an AMD/Intel GPU, update the AMD/Intel graphics driver first. If the GPU is too old for the current Vulkan backend, switch the backend to CPU.`;
|
||||
}
|
||||
|
||||
return `exited with code ${code}`;
|
||||
}
|
||||
|
||||
function getModelInfo(filename) {
|
||||
const safeFilename = path.basename(filename || "");
|
||||
const stats = fs.statSync(path.join(MODELS, safeFilename));
|
||||
@@ -1830,7 +1909,7 @@ const server = http.createServer(async (req, res) => {
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(PORT_FRONTEND, "127.0.0.1", () => {
|
||||
server.listen(PORT_FRONTEND, "0.0.0.0", () => {
|
||||
console.log("");
|
||||
console.log(" ============================================================");
|
||||
console.log(" LOCAL AI IMAGE GENERATOR | Running");
|
||||
|
||||
+87
-19
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Local AI Image Generator - Linux Setup Script
|
||||
# Local AI Image Generator - Linux/macOS Setup Script
|
||||
# Self-contained: no apt/yum/pacman, no global Node.js install.
|
||||
#
|
||||
|
||||
@@ -11,18 +11,40 @@ ROOT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
APP_DIR="$ROOT_DIR/app"
|
||||
FRONTEND_DIR="$APP_DIR/frontend"
|
||||
TOOLS_DIR="$APP_DIR/tools"
|
||||
NODE_DIR="$TOOLS_DIR/node-linux"
|
||||
DIST_DIR="$APP_DIR/dist"
|
||||
PLATFORM="$(uname -s)"
|
||||
ARCH="$(uname -m)"
|
||||
|
||||
if [[ "$PLATFORM" == "Darwin" ]]; then
|
||||
PLATFORM_LABEL="macOS"
|
||||
NODE_DIR="$TOOLS_DIR/node-mac"
|
||||
BACKEND_DIR="$APP_DIR/backend/mac"
|
||||
else
|
||||
PLATFORM_LABEL="Linux"
|
||||
NODE_DIR="$TOOLS_DIR/node-linux"
|
||||
BACKEND_DIR="$APP_DIR/backend/linux"
|
||||
fi
|
||||
|
||||
NODE_BIN="$NODE_DIR/bin/node"
|
||||
NPM_BIN="$NODE_DIR/bin/npm"
|
||||
DIST_DIR="$APP_DIR/dist"
|
||||
BACKEND_DIR="$APP_DIR/backend/linux"
|
||||
|
||||
# Release pins
|
||||
SD_RELEASE="master-685-19bdfe2"
|
||||
SD_SHORT_HASH="${SD_RELEASE##*-}"
|
||||
SD_BASE_URL="https://github.com/leejet/stable-diffusion.cpp/releases/download/$SD_RELEASE"
|
||||
NODE_VERSION="22.12.0"
|
||||
NODE_TARBALL="node-v${NODE_VERSION}-linux-x64.tar.xz"
|
||||
|
||||
if [[ "$PLATFORM" == "Darwin" ]]; then
|
||||
if [[ "$ARCH" == "arm64" ]]; then
|
||||
NODE_PLATFORM_ARCH="darwin-arm64"
|
||||
else
|
||||
NODE_PLATFORM_ARCH="darwin-x64"
|
||||
fi
|
||||
NODE_TARBALL="node-v${NODE_VERSION}-${NODE_PLATFORM_ARCH}.tar.gz"
|
||||
else
|
||||
NODE_PLATFORM_ARCH="linux-x64"
|
||||
NODE_TARBALL="node-v${NODE_VERSION}-${NODE_PLATFORM_ARCH}.tar.xz"
|
||||
fi
|
||||
NODE_URL="https://nodejs.org/dist/v${NODE_VERSION}/$NODE_TARBALL"
|
||||
|
||||
# Flags
|
||||
@@ -36,7 +58,7 @@ print_header() {
|
||||
clear 2>/dev/null || true
|
||||
echo ""
|
||||
echo " ============================================================"
|
||||
echo " LOCAL AI IMAGE GENERATOR - Linux First-Time Setup"
|
||||
echo " LOCAL AI IMAGE GENERATOR - $PLATFORM_LABEL First-Time Setup"
|
||||
echo " 100% Self-Contained | No System Install Required"
|
||||
echo " ============================================================"
|
||||
echo ""
|
||||
@@ -180,15 +202,39 @@ copy_binaries_from_extracted() {
|
||||
find "$extracted_dir" -type f -name "*.so" -exec cp {} "$dest_dir/" \; 2>/dev/null || true
|
||||
}
|
||||
|
||||
copy_macos_backend_from_extracted() {
|
||||
local extracted_dir="$1" dest_dir="$2"
|
||||
local target="$dest_dir/sd"
|
||||
local server_bin=""
|
||||
local cli_bin=""
|
||||
|
||||
server_bin="$(find "$extracted_dir" -type f -name "sd-server" | head -n 1)"
|
||||
cli_bin="$(find "$extracted_dir" -type f \( -name "sd" -o -name "sd-cli" \) | head -n 1)"
|
||||
|
||||
if [[ -n "$server_bin" ]]; then
|
||||
cp "$server_bin" "$target"
|
||||
elif [[ -n "$cli_bin" ]]; then
|
||||
cp "$cli_bin" "$target"
|
||||
else
|
||||
print_fail "No sd-server, sd, or sd-cli binary was found in the macOS backend archive."
|
||||
return 1
|
||||
fi
|
||||
|
||||
find "$extracted_dir" -type f \( -name "*.dylib" -o -name "*.metallib" \) -exec cp {} "$dest_dir/" \; 2>/dev/null || true
|
||||
chmod +x "$target" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
print_header
|
||||
|
||||
check_glibc
|
||||
if [[ "$PLATFORM" == "Linux" ]]; then
|
||||
check_glibc
|
||||
fi
|
||||
|
||||
TOTAL_STEPS=4
|
||||
|
||||
# ── Step 1: Portable Node.js ────────────────────────────────────────────────
|
||||
print_step 1 $TOTAL_STEPS "Setting up portable Node.js (app/tools/node-linux/)"
|
||||
print_step 1 $TOTAL_STEPS "Setting up portable Node.js ($NODE_DIR/)"
|
||||
|
||||
if [[ -x "$NODE_BIN" && -x "$NPM_BIN" ]]; then
|
||||
VERSION=$("$NODE_BIN" --version)
|
||||
@@ -201,7 +247,7 @@ else
|
||||
extract_tarxz "$NODE_TAR_PATH" "$TOOLS_DIR" "Node.js"
|
||||
rm -f "$NODE_TAR_PATH"
|
||||
|
||||
EXTRACTED_DIR="$(find "$TOOLS_DIR" -maxdepth 1 -type d -name 'node-v*-linux-x64' | head -n 1)"
|
||||
EXTRACTED_DIR="$(find "$TOOLS_DIR" -maxdepth 1 -type d -name "node-v*-${NODE_PLATFORM_ARCH}" | head -n 1)"
|
||||
if [[ -d "$EXTRACTED_DIR" ]]; then
|
||||
rm -rf "$NODE_DIR"
|
||||
mv "$EXTRACTED_DIR" "$NODE_DIR"
|
||||
@@ -217,14 +263,35 @@ else
|
||||
fi
|
||||
|
||||
# ── Step 2: stable-diffusion.cpp Backends ───────────────────────────────────
|
||||
VENDOR="$(detect_gpu_vendor)"
|
||||
print_step 2 $TOTAL_STEPS "Detecting GPU vendor: ${VENDOR:-none}"
|
||||
|
||||
mkdir -p "$BACKEND_DIR"
|
||||
|
||||
if [[ "$PLATFORM" == "Darwin" ]]; then
|
||||
print_step 2 $TOTAL_STEPS "Setting up stable-diffusion.cpp Metal backend (app/backend/mac/)"
|
||||
if [[ "$ARCH" != "arm64" ]]; then
|
||||
print_fail "The official macOS backend binary is Apple Silicon only (arm64)."
|
||||
print_info "Intel Macs need a local source build with Metal/OpenBLAS and a matching app/backend/mac/sd binary."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MAC_BACKEND="$BACKEND_DIR/sd"
|
||||
if [[ -x "$MAC_BACKEND" ]]; then
|
||||
print_ok "macOS Metal backend already ready."
|
||||
else
|
||||
MAC_ZIP="$TOOLS_DIR/sd-mac-metal.zip"
|
||||
download_file "$SD_BASE_URL/sd-master-${SD_SHORT_HASH}-bin-Darwin-macOS-15.7.7-arm64.zip" "$MAC_ZIP" "stable-diffusion.cpp Metal Backend (macOS arm64)"
|
||||
extract_zip "$MAC_ZIP" "$BACKEND_DIR/extracted" "macOS Metal Backend"
|
||||
rm -f "$MAC_ZIP"
|
||||
copy_macos_backend_from_extracted "$BACKEND_DIR/extracted" "$BACKEND_DIR"
|
||||
rm -rf "$BACKEND_DIR/extracted"
|
||||
print_ok "macOS Metal backend installed."
|
||||
fi
|
||||
else
|
||||
VENDOR="$(detect_gpu_vendor)"
|
||||
print_step 2 $TOTAL_STEPS "Detecting GPU vendor: ${VENDOR:-none}"
|
||||
|
||||
# CPU backend (always)
|
||||
CPU_BACKEND_DIR="$BACKEND_DIR/cpu"
|
||||
if [[ ! -f "$CPU_BACKEND_DIR/sd-cpu" ]]; then
|
||||
if [[ ! -f "$CPU_BACKEND_DIR/sd-cpu" || ! -f "$CPU_BACKEND_DIR/sd-server-cpu" ]]; then
|
||||
mkdir -p "$CPU_BACKEND_DIR"
|
||||
CPU_ZIP="$TOOLS_DIR/sd-cpu.zip"
|
||||
download_file "$SD_BASE_URL/sd-master-${SD_SHORT_HASH}-bin-Linux-Ubuntu-24.04-x86_64.zip" "$CPU_ZIP" "stable-diffusion.cpp CPU Backend (Linux x86_64)"
|
||||
@@ -232,15 +299,15 @@ if [[ ! -f "$CPU_BACKEND_DIR/sd-cpu" ]]; then
|
||||
rm -f "$CPU_ZIP"
|
||||
copy_binaries_from_extracted "$CPU_BACKEND_DIR/extracted" "$CPU_BACKEND_DIR" "sd-cpu" "sd-server-cpu"
|
||||
rm -rf "$CPU_BACKEND_DIR/extracted"
|
||||
chmod +x "$CPU_BACKEND_DIR/sd-cpu" 2>/dev/null || true
|
||||
print_ok "CPU backend installed."
|
||||
else
|
||||
print_ok "CPU backend already ready."
|
||||
fi
|
||||
chmod +x "$CPU_BACKEND_DIR/sd-cpu" "$CPU_BACKEND_DIR/sd-server-cpu" 2>/dev/null || true
|
||||
|
||||
# Vulkan backend (always - cross-vendor GPU fallback)
|
||||
VULKAN_BACKEND_DIR="$BACKEND_DIR/vulkan"
|
||||
if [[ ! -f "$VULKAN_BACKEND_DIR/sd-vulkan" ]]; then
|
||||
if [[ ! -f "$VULKAN_BACKEND_DIR/sd-vulkan" || ! -f "$VULKAN_BACKEND_DIR/sd-server-vulkan" ]]; then
|
||||
mkdir -p "$VULKAN_BACKEND_DIR"
|
||||
VULKAN_ZIP="$TOOLS_DIR/sd-vulkan.zip"
|
||||
download_file "$SD_BASE_URL/sd-master-${SD_SHORT_HASH}-bin-Linux-Ubuntu-24.04-x86_64-vulkan.zip" "$VULKAN_ZIP" "stable-diffusion.cpp Vulkan Backend (Linux x86_64)"
|
||||
@@ -248,16 +315,16 @@ if [[ ! -f "$VULKAN_BACKEND_DIR/sd-vulkan" ]]; then
|
||||
rm -f "$VULKAN_ZIP"
|
||||
copy_binaries_from_extracted "$VULKAN_BACKEND_DIR/extracted" "$VULKAN_BACKEND_DIR" "sd-vulkan" "sd-server-vulkan"
|
||||
rm -rf "$VULKAN_BACKEND_DIR/extracted"
|
||||
chmod +x "$VULKAN_BACKEND_DIR/sd-vulkan" 2>/dev/null || true
|
||||
print_ok "Vulkan backend installed."
|
||||
else
|
||||
print_ok "Vulkan backend already ready."
|
||||
fi
|
||||
chmod +x "$VULKAN_BACKEND_DIR/sd-vulkan" "$VULKAN_BACKEND_DIR/sd-server-vulkan" 2>/dev/null || true
|
||||
|
||||
# ROCm backend (optional --max-perf, or auto-detected AMD)
|
||||
ROCM_BACKEND_DIR="$BACKEND_DIR/rocm"
|
||||
if [[ $MAX_PERF -eq 1 ]] && [[ "$VENDOR" == "amd" || "$VENDOR" == "" ]]; then
|
||||
if [[ ! -f "$ROCM_BACKEND_DIR/sd-rocm" ]]; then
|
||||
if [[ ! -f "$ROCM_BACKEND_DIR/sd-rocm" || ! -f "$ROCM_BACKEND_DIR/sd-server-rocm" ]]; then
|
||||
mkdir -p "$ROCM_BACKEND_DIR"
|
||||
ROCM_ZIP="$TOOLS_DIR/sd-rocm.zip"
|
||||
print_warn "ROCm backend is ~1.2 GB. This may take a while..."
|
||||
@@ -266,11 +333,11 @@ if [[ $MAX_PERF -eq 1 ]] && [[ "$VENDOR" == "amd" || "$VENDOR" == "" ]]; then
|
||||
rm -f "$ROCM_ZIP"
|
||||
copy_binaries_from_extracted "$ROCM_BACKEND_DIR/extracted" "$ROCM_BACKEND_DIR" "sd-rocm" "sd-server-rocm"
|
||||
rm -rf "$ROCM_BACKEND_DIR/extracted"
|
||||
chmod +x "$ROCM_BACKEND_DIR/sd-rocm" 2>/dev/null || true
|
||||
print_ok "ROCm backend installed."
|
||||
else
|
||||
print_ok "ROCm backend already ready."
|
||||
fi
|
||||
chmod +x "$ROCM_BACKEND_DIR/sd-rocm" "$ROCM_BACKEND_DIR/sd-server-rocm" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# CUDA backend on Linux is intentionally disabled.
|
||||
@@ -284,6 +351,7 @@ if [[ $MAX_PERF -eq 1 ]] && [[ "$VENDOR" == "nvidia" ]]; then
|
||||
print_info "Linux CUDA backend is disabled pending a reliable upstream binary."
|
||||
print_info "NVIDIA GPUs on Linux will use the Vulkan backend instead."
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Step 3: npm install ─────────────────────────────────────────────────────
|
||||
print_step 3 $TOTAL_STEPS "Installing frontend dependencies (app/frontend/)"
|
||||
@@ -320,7 +388,7 @@ echo " Setup complete! Run ./start.sh to launch."
|
||||
echo " ============================================================"
|
||||
echo ""
|
||||
|
||||
if [[ $MAX_PERF -eq 0 ]] && [[ "$VENDOR" == "nvidia" || "$VENDOR" == "amd" ]]; then
|
||||
if [[ "$PLATFORM" == "Linux" && $MAX_PERF -eq 0 ]] && [[ "$VENDOR" == "nvidia" || "$VENDOR" == "amd" ]]; then
|
||||
echo " Tip: For maximum GPU performance, re-run with:"
|
||||
echo " ./scripts/setup.sh --max-perf"
|
||||
echo ""
|
||||
|
||||
Reference in new issue
Block a user