feat: nexus mods integration (#44)
* progress * working download * save details in db, display them * progress * progress * progress * progress * i guess it's shippable * final commit
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
VERSION="0.4.2"
|
||||
VERSION="0.5.0"
|
||||
|
||||
# --- Globals ---
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
ORANGE='\033[0;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
MODS_DIR=""
|
||||
@@ -16,14 +17,18 @@ MODPACKS_DB_FILE=""
|
||||
|
||||
H2CONFIG_DIR="${HOME}/.config/h2mm"
|
||||
H2PATH_FILE="${H2CONFIG_DIR}/h2path"
|
||||
API_KEY_FILE="${H2CONFIG_DIR}/api_key"
|
||||
UPDATES_AVAILABLE_FILE="${H2CONFIG_DIR}/updates_available"
|
||||
LAST_CHECKED_UPDATE_FILE="${H2CONFIG_DIR}/last_update"
|
||||
BACKUPS_DIR="${H2CONFIG_DIR}/backups"
|
||||
|
||||
VERSION_URL="https://raw.githubusercontent.com/v4n00/h2mm-cli/refs/heads/master/version"
|
||||
|
||||
breaking_changes_patches=(
|
||||
["2"]='sed -i "s/^\([0-9]\+\),/\1,ENABLED,/" "$1/mods.csv"'
|
||||
["3"]='sed -i "1 i\\3" "$1/mods.csv"'
|
||||
["4"]='tmp_file=$(mktemp) && awk '\''BEGIN {FS=OFS=","} NR==1 {print 4; next} {print NR-1, $2, $3, $4, $5}'\'' "$1/mods.csv" > "$tmp_file" && tee "$1/mods.csv" < "$tmp_file" > /dev/null && rm "$tmp_file"'
|
||||
["5"]='sed -i "s/^\([0-9]\+\),\(.*\),\(.*\),\(.*\)/\1,\2,\3,,,,\4/" "$1/mods.csv"; sed -i "1 s/4/5/" "$1/mods.csv"'
|
||||
)
|
||||
|
||||
# --- Utility Functions ---
|
||||
@@ -53,17 +58,37 @@ function get_extension() {
|
||||
}
|
||||
|
||||
function get_files_by_entry_from_db() {
|
||||
echo "$1" | cut -d',' -f4- | tr ',' ' ' | head -1
|
||||
echo "$1" | cut -d',' -f7- | tr ',' ' ' | head -1
|
||||
}
|
||||
|
||||
function get_name_by_entry_from_db() {
|
||||
echo "$1" | awk -F, '{print $3}'
|
||||
}
|
||||
|
||||
function get_index_by_entry_from_db() {
|
||||
echo "$1" | awk -F, '{print $1}'
|
||||
}
|
||||
|
||||
function get_nexus_mod_id_by_entry_from_db() {
|
||||
echo "$1" | awk -F, '{print $4}'
|
||||
}
|
||||
|
||||
function get_nexus_mod_file_id_by_entry_from_db() {
|
||||
echo "$1" | awk -F, '{print $5}'
|
||||
}
|
||||
|
||||
function get_nexus_mod_version_by_entry_from_db() {
|
||||
echo "$1" | awk -F, '{print $6}'
|
||||
}
|
||||
|
||||
function disable_all_modpacks() {
|
||||
sed -i 's/ENABLED/DISABLED/' "$MODPACKS_DB_FILE"
|
||||
}
|
||||
|
||||
function get_entry_from_db_by_nexus_mod_id() {
|
||||
echo "$(awk -F, -v id="$1" 'NR > 1 && $4 == id {print $0}' "$DB_FILE")"
|
||||
}
|
||||
|
||||
function parse_help_no_arguments() {
|
||||
display_help="$1"
|
||||
[[ "$2" == "--help" || "$2" == "-h" ]] && { $display_help; exit 0; }
|
||||
@@ -82,6 +107,10 @@ function log() {
|
||||
[[ "$silent" == "true" ]] && return
|
||||
echo -e "$*" >&2
|
||||
;;
|
||||
WARNING)
|
||||
[[ "$silent" == "true" ]] && return
|
||||
echo -e "${ORANGE}[!]${NC} $*" >&2
|
||||
;;
|
||||
ERROR)
|
||||
echo -e "${RED}[ERROR]${NC} $*" >&2
|
||||
;;
|
||||
@@ -96,6 +125,22 @@ function log() {
|
||||
|
||||
# --- Functions ---
|
||||
|
||||
function check_nexus_mod_arguments() {
|
||||
if [[ -n "$nexus_mod_id" || -n "$nexus_mod_file_id" || -n "$nexus_mod_version" ]]; then
|
||||
if [[ -z "$nexus_mod_id" || -z "$nexus_mod_file_id" || -z "$nexus_mod_version" ]]; then
|
||||
log ERROR "Nexus mod ID, file ID and version are required when specifing at least one of them."
|
||||
exit 1
|
||||
fi
|
||||
has_nexus_mod_arguments=true
|
||||
fi
|
||||
}
|
||||
|
||||
function get_nexus_api_key() {
|
||||
[[ ! -f "$API_KEY_FILE" ]] && { log ERROR "Nexus API key file not found. Please run h2mm nexus-setup."; exit 1; }
|
||||
api_key=$(cat "$API_KEY_FILE")
|
||||
[[ -z "$api_key" ]] && { log ERROR "Nexus API key is empty. Please run h2mm nexus-setup."; exit 1; }
|
||||
}
|
||||
|
||||
function remove_disabled_prefix() {
|
||||
local disabledFile="$1"
|
||||
while [[ "$disabledFile" == disabled_* ]]; do
|
||||
@@ -183,6 +228,11 @@ function initialize_config_directory() {
|
||||
touch "$LAST_CHECKED_UPDATE_FILE"
|
||||
[[ $? -ne 0 ]] && { log ERROR "Could not create last checked update file."; exit 1; }
|
||||
fi
|
||||
|
||||
if [[ ! -f "$UPDATES_AVAILABLE_FILE" ]]; then
|
||||
touch "$UPDATES_AVAILABLE_FILE"
|
||||
[[ $? -ne 0 ]] && { log ERROR "Could not create updates available file."; exit 1; }
|
||||
fi
|
||||
}
|
||||
|
||||
function initialize_directories() {
|
||||
@@ -216,26 +266,6 @@ function initialize_modpack_directories() {
|
||||
fi
|
||||
}
|
||||
|
||||
function check_for_updates() {
|
||||
last_update=$(cat "$LAST_CHECKED_UPDATE_FILE")
|
||||
if [[ -n "$last_update" && "$(date +%s)" -lt $(("$(date +%s -d "$last_update")" + 3600)) ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
latest_version=$(curl -sS "$VERSION_URL")
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log ERROR "Could not check for updates."
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$latest_version" != "$VERSION" ]]; then
|
||||
log INFO "A new version of h2mm is available: $VERSION -> $latest_version"
|
||||
log INFO "Run \"h2mm update\" to update."
|
||||
fi
|
||||
|
||||
echo "$(date)" > "$LAST_CHECKED_UPDATE_FILE"
|
||||
}
|
||||
|
||||
# --- Help Functions ---
|
||||
|
||||
function display_help_main() {
|
||||
@@ -248,15 +278,17 @@ Commands:
|
||||
l, list List all installed mods.
|
||||
e, enable Enable a mod.
|
||||
d, disable Disable a mod.
|
||||
o, order Change load order of a mod.
|
||||
ex, export Export installed mods to a zip file.
|
||||
im, import Import mods from a zip file.
|
||||
o, order Change load order of a mod.
|
||||
mc, modpack-create Create a modpack from the currently installed mods.
|
||||
ms, modpack-switch Switch to a modpack.
|
||||
ml, modpack-list List all installed modpacks.
|
||||
mc, modpack-delete Delete a modpack.
|
||||
mo, modpack-overwrite Overwrite a modpack.
|
||||
mr, modpack-reset Reset all installed modpacks.
|
||||
ns, nexus-setup Setup Nexus Mods integration.
|
||||
nu, nexus-update Start Nexus mods update process.
|
||||
up, update Update h2mm to the latest version.
|
||||
r, reset Reset all installed mods.
|
||||
help Display this help message.
|
||||
@@ -427,6 +459,24 @@ Options:
|
||||
EOF
|
||||
}
|
||||
|
||||
function display_help_nexus_setup() {
|
||||
cat << EOF
|
||||
Usage: h2mm nexus-setup
|
||||
Setup nexusmods integration.
|
||||
This will create a config file in ~/.config/h2mm/apikey with the API key.
|
||||
This will create a desktop entry in ~/.local/share/applications/h2mm.desktop.
|
||||
Run this again in case you change the API key or want to change the desktop entry.
|
||||
EOF
|
||||
}
|
||||
|
||||
function display_help_nexus_update() {
|
||||
cat << EOF
|
||||
Usage: h2mm nexus-update
|
||||
Display a list of Nexus mods that have updates available.
|
||||
Due to Nexus API limitations, this will prompt the user to access the Nexus website to download the updates.
|
||||
EOF
|
||||
}
|
||||
|
||||
# --- Main Functions ---
|
||||
|
||||
# Upgrade/downgrade logic
|
||||
@@ -677,6 +727,9 @@ function mod_install() {
|
||||
local mod_dir=()
|
||||
local mod_files=()
|
||||
local mod_zip=()
|
||||
local nexus_mod_version=""
|
||||
local nexus_mod_file_id=""
|
||||
local nexus_mod_id=""
|
||||
local is_rar=false
|
||||
|
||||
# parse arguments
|
||||
@@ -686,6 +739,18 @@ function mod_install() {
|
||||
[[ -z "$2" ]] && { log ERROR "Mod name is required."; exit 1; }
|
||||
mod_name="$2"; shift 2
|
||||
;;
|
||||
"--version")
|
||||
[[ -z "$2" ]] && { log ERROR "Nexus mod version is required."; exit 1; }
|
||||
nexus_mod_version="$2"; shift 2
|
||||
;;
|
||||
"--file-id")
|
||||
[[ -z "$2" ]] && { log ERROR "Nexus mod file ID is required."; exit 1; }
|
||||
nexus_mod_file_id="$2"; shift 2
|
||||
;;
|
||||
"--mod-id")
|
||||
[[ -z "$2" ]] && { log ERROR "Nexus mod ID is required."; exit 1; }
|
||||
nexus_mod_id="$2"; shift 2
|
||||
;;
|
||||
*)
|
||||
if [[ "$1" == *.zip || "$1" == *.rar ]]; then
|
||||
mod_zip+=("$1")
|
||||
@@ -700,6 +765,8 @@ function mod_install() {
|
||||
esac
|
||||
done
|
||||
|
||||
check_nexus_mod_arguments
|
||||
|
||||
# edge case when there is a combination of mod zips and directories, call function for all zips and dirs
|
||||
if [[ ${mod_zip} && ${mod_dir} ]]; then
|
||||
mod_install "${mod_zip[@]}"
|
||||
@@ -737,7 +804,7 @@ function mod_install() {
|
||||
|
||||
# if the name is not specified, use the name of the directory, last sed for making nexusmods names not have numbers
|
||||
if [[ -z "$mod_name" ]]; then
|
||||
mod_name=$(basename "$mod_zip" | sed -E 's/\.zip//' | awk -F/ '{print $NF}' | sed -E 's/-[0-9]+-.*//')
|
||||
mod_name=$(get_filename_without_path "$mod_zip" | sed -E 's/\.zip//' | sed -E 's/\.rar//' | sed -E 's/-[0-9]+-[^a-zA-Z]*-[0-9]+$//')
|
||||
fi
|
||||
|
||||
# mod_dir as a temporary directory
|
||||
@@ -820,8 +887,10 @@ function mod_install() {
|
||||
done
|
||||
|
||||
# verify duplicate mod names
|
||||
get_mod_name_and_index --do-not-exit
|
||||
[[ $mod_index -ne -1 ]] && { log ERROR "The mod '$mod_name' is already installed."; exit 1; }
|
||||
if [[ $has_nexus_mod_arguments == false ]]; then
|
||||
get_mod_name_and_index --do-not-exit
|
||||
[[ $mod_index -ne -1 ]] && { log ERROR "The mod '$mod_name' is already installed."; exit 1; }
|
||||
fi
|
||||
|
||||
# store the target files so we can put them in the database later
|
||||
target_files=()
|
||||
@@ -832,7 +901,6 @@ function mod_install() {
|
||||
base_name=$(get_basename "$file")
|
||||
extension=$(get_extension "$file")
|
||||
|
||||
|
||||
# count already installed patches
|
||||
count=$(ls "$MODS_DIR/${base_name}.patch_"* 2>/dev/null | grep -E '([0-9]+$)' 2>/dev/null | wc -l)
|
||||
|
||||
@@ -849,12 +917,14 @@ function mod_install() {
|
||||
|
||||
# verify installation worked
|
||||
[[ $? -ne 0 ]] && { log ERROR "Could not install mod file $file."; exit 1; }
|
||||
log INFO "Mod file ${ORANGE}$file${NC} installed at ${GREEN}\$MODS_DIR/$target_file${NC}."
|
||||
log INFO "Mod file ${ORANGE}$(echo "$file" | cut -d'/' -f4-)${NC} installed at ${GREEN}\$MODS_DIR/$target_file${NC}."
|
||||
done
|
||||
|
||||
# add entry to database
|
||||
next_id=$(awk -F, 'NR > 1 {last_id = $1} END {print last_id + 1}' "$DB_FILE")
|
||||
echo "$next_id,ENABLED,$mod_name,${target_files[*]}" >> "$DB_FILE"
|
||||
entry="$next_id,ENABLED,$mod_name,$nexus_mod_id,$nexus_mod_file_id,$nexus_mod_version,${target_files[*]}"
|
||||
|
||||
echo "$entry" >> "$DB_FILE"
|
||||
log INFO "Mod ${GREEN}successfully${NC} installed: $mod_name."
|
||||
|
||||
# disable any modpack
|
||||
@@ -937,15 +1007,33 @@ function mod_list() {
|
||||
|
||||
log INFO "Installed mods:"
|
||||
|
||||
awk -v GREEN="$GREEN" -v RED="$RED" -v NC="$NC" -v verbose="$verbose" -F, 'NR > 1 {
|
||||
color = ($2 == "DISABLED") ? RED : GREEN;
|
||||
if (verbose == "false") {
|
||||
$4 = "";
|
||||
awk -v GREEN="$GREEN" -v ORANGE="$ORANGE" -v BLUE="$BLUE" -v RED="$RED" -v NC="$NC" -v verbose="$verbose" -F, 'NR > 1 {
|
||||
mod_index = $1;
|
||||
mod_status = $2;
|
||||
mod_name = $3;
|
||||
mod_id = $4;
|
||||
mod_file_id = $5;
|
||||
mod_version = $6;
|
||||
mod_files = $7;
|
||||
mod_details = "";
|
||||
|
||||
STATUS_COLOR = (mod_status == "DISABLED") ? RED : GREEN;
|
||||
if (mod_id == "") {
|
||||
mod_type = "LOCAL";
|
||||
MOD_TYPE_COLOR = BLUE;
|
||||
mod_version = "";
|
||||
} else {
|
||||
gsub(/ /,"\n -> ", $4);
|
||||
$4 = "\n -> " $4;
|
||||
mod_type = "NEXUS";
|
||||
MOD_TYPE_COLOR = ORANGE;
|
||||
mod_version = "| ver: " mod_version;
|
||||
}
|
||||
|
||||
printf "%2s. [%s%s%s/%s%s%s] %s %s\n", mod_index, STATUS_COLOR, mod_status, NC, MOD_TYPE_COLOR, mod_type, NC, mod_name, mod_version;
|
||||
if (verbose == "true") {
|
||||
gsub(/ /,"\n -> ", mod_files);
|
||||
if (mod_id != "") printf " => Nexus mod ID: %s / Nexus file ID: %s\n", mod_id, mod_file_id;
|
||||
printf " -> %s\n", mod_files;
|
||||
}
|
||||
printf "%2s. [%s%s%s] %s %s\n", $1, color, $2, NC, $3, $4
|
||||
}' "$DB_FILE"
|
||||
}
|
||||
|
||||
@@ -1031,7 +1119,7 @@ function mod_import() {
|
||||
[[ -z "$db_version" || ! "$db_version" =~ ^[0-9]+$ ]] && { log ERROR "Invalid version number inside mods.csv from imported archive."; exit 1; }
|
||||
|
||||
if [[ "$db_version" != "$current_version" ]]; then
|
||||
log INFO "Import detected version 0.$db_version.x, current version is 0.$current_version.x."
|
||||
log INFO "Import version mismatch detected: ${ORANGE}0.$db_version.x${NC} -> ${GREEN}0.$current_version.x${NC}."
|
||||
|
||||
# iterate from installed major number to latest major number
|
||||
for ((i = db_version + 1; i <= current_version; i++)); do
|
||||
@@ -1039,7 +1127,7 @@ function mod_import() {
|
||||
# apply breaking changes patch
|
||||
eval $(echo "${breaking_changes_patches[$i]}" | sed "s:\$1:$MODS_EXPORT_DIR:g")
|
||||
else
|
||||
log INFO "No breaking changes for version $i."
|
||||
log INFO "No breaking changes for version 0.$i.x."
|
||||
continue
|
||||
fi
|
||||
|
||||
@@ -1049,7 +1137,7 @@ function mod_import() {
|
||||
|
||||
[[ "$response" != "y" && "$response" != "Y" && -n "$response" ]] && { log INFO "Exiting." ; exit 1; }
|
||||
else
|
||||
log INFO "Version upgrade fix ${GREEN}successfully${NC} applied for version $i."
|
||||
log INFO "Upgrade ${GREEN}successfully${NC} applied for mismatch: ${GREEN}0.$i.x${NC}."
|
||||
fi
|
||||
done
|
||||
fi
|
||||
@@ -1059,7 +1147,7 @@ function mod_import() {
|
||||
|
||||
[[ $? -ne 0 ]] && { log ERROR "Failed to import mods."; exit 1; }
|
||||
|
||||
log INFO "Mods ${GREEN}successfully${NC} imported."
|
||||
[[ $modpack == false ]] && log INFO "Mods ${GREEN}successfully${NC} imported."
|
||||
}
|
||||
|
||||
function mod_order {
|
||||
@@ -1127,7 +1215,7 @@ function mod_order {
|
||||
# step 2.1 - iterate over the basenames, find and store the files with the same basenames as the ones we want to upgrade/downgrade
|
||||
declare -A files_to_replace
|
||||
for base_name in "${!replace_count[@]}"; do
|
||||
IFS= files_to_replace["$base_name"]=$(echo "$entries" | awk -F, '{print $4}' | grep -o "$base_name\.patch_[^ ]*"); unset IFS
|
||||
IFS= files_to_replace["$base_name"]=$(echo "$entries" | awk -F, '{print $7}' | grep -o "$base_name\.patch_[^ ]*"); unset IFS
|
||||
done
|
||||
|
||||
# step 2.2 - reverse sort the files_to_replace if we are in descending order
|
||||
@@ -1290,7 +1378,7 @@ function modpack_create() {
|
||||
[[ $(wc -l < "$DB_FILE") -le 1 ]] && { log ERROR "No mods installed."; exit 1; }
|
||||
|
||||
# if the same modpack name already exists, exit
|
||||
[[ -f "$MODPACKS_DIR/$1.tar.gz" ]] && { log ERROR "Modpack $1 already exists."; exit 1; }
|
||||
[[ -f "$MODPACKS_DIR/${modpack_name}.tar.gz" ]] && { log ERROR "Modpack \"$modpack_name\" already exists."; exit 1; }
|
||||
|
||||
# let user select mods to save
|
||||
mod_list
|
||||
@@ -1373,9 +1461,7 @@ function modpack_switch() {
|
||||
get_modpack_name_and_index "$modpack_name" "$modpack_index"
|
||||
|
||||
log INFO "Switching modpacks mods will ${RED}reset${NC} your mods."
|
||||
silent=true
|
||||
mod_import --modpack "$MODPACKS_DIR/$modpack_name.tar.gz"
|
||||
silent=false
|
||||
|
||||
log INFO "Modpack ${GREEN}successfully${NC} switched: $modpack_name."
|
||||
|
||||
@@ -1491,6 +1577,288 @@ function self_update() {
|
||||
exit 0
|
||||
}
|
||||
|
||||
# --- Nexus Mods Integration ---
|
||||
|
||||
function nexus_setup() {
|
||||
parse_help_no_arguments display_help_nexus_setup "$@"
|
||||
local nexus_api_key=""
|
||||
|
||||
log INFO "This is the setup wizard for the Nexus Mods integration."
|
||||
log INFO "You need to provide your Nexus Mods API key to use this feature."
|
||||
log INFO "You can find your API key in your Nexus Mods by:"
|
||||
log INFO " -> Going to Site Preferences -> API Keys"
|
||||
log INFO " -> Going to https://next.nexusmods.com/settings/api-keys"
|
||||
log INFO "Scroll down and request/copy your API key."
|
||||
log INFO ""
|
||||
log PROMPT "Enter your Nexus Mods API key: "
|
||||
read -e nexus_api_key
|
||||
|
||||
[[ -z "$nexus_api_key" ]] && { log ERROR "Nexus Mods API key is required."; exit 1; }
|
||||
|
||||
# save api key to config dir, trim it
|
||||
echo "$nexus_api_key" | tr -d '[:space:]' > "$API_KEY_FILE"
|
||||
[[ $? -ne 0 ]] && { log ERROR "Could not save Nexus Mods API key."; exit 1; }
|
||||
|
||||
# test API key
|
||||
response=$(curl -sSIH "apikey: $nexus_api_key" -X GET "https://api.nexusmods.com/v1/users/validate.json")
|
||||
[[ $? -ne 0 ]] && { log ERROR "curl failed."; exit 1; }
|
||||
[[ ! "$response" =~ "HTTP/2 200" ]] && { log ERROR "Invalid Nexus Mods API key."; exit 1; }
|
||||
|
||||
log INFO "Nexus Mods API key ${GREEN}successfully${NC} tested and saved."
|
||||
log INFO ""
|
||||
|
||||
# ask for default terminal
|
||||
local terminal_command=""
|
||||
|
||||
log INFO "Now the manager needs to create a desktop entry for h2mm."
|
||||
log INFO "In order to do that, it needs to know which terminal you are using."
|
||||
log INFO "Here's a list of supported terminals:"
|
||||
log INFO " -> gnome-terminal (On GNOME systems like Ubuntu)"
|
||||
log INFO " -> konsole (On KDE systems like SteamOS, Steam Deck's default)"
|
||||
log INFO " -> alacritty"
|
||||
log INFO " -> kitty"
|
||||
log INFO ""
|
||||
log INFO "If you are not using any of these terminals, you need to provide"
|
||||
log INFO "the command to spawn your terminal along with the flags to run a command."
|
||||
log INFO "To test if the command is valid, the manager spawn a terminal."
|
||||
log INFO "For example, if you are using ghostty you need to provide this:"
|
||||
log INFO "ghostty -e <COMMAND>"
|
||||
log INFO ""
|
||||
log PROMPT "Enter your terminal (or terminal command): "
|
||||
read -e terminal_command
|
||||
|
||||
[[ -z "$terminal_command" ]] && { log ERROR "Terminal command is required."; exit 1; }
|
||||
command -v "$(echo "$terminal_command" | awk '{print $1}')" >/dev/null 2>&1 || { log ERROR "Terminal command does not work."; exit 1; }
|
||||
|
||||
# check the 4 supported terminals
|
||||
case "$terminal_command" in
|
||||
"gnome-terminal")
|
||||
terminal_command="$terminal_command -- <COMMAND>"
|
||||
;;
|
||||
"konsole"|"alacritty"|"kitty")
|
||||
terminal_command="$terminal_command -e <COMMAND>"
|
||||
;;
|
||||
*)
|
||||
# check if terminal even exists
|
||||
command -v "$(echo "$terminal_command" | awk '{print $1}')" >/dev/null 2>&1 || { log ERROR "Terminal command does not work."; exit 1; }
|
||||
# check if the format is valid
|
||||
[[ "$terminal_command" != *"<COMMAND>"* ]] && { log ERROR "Terminal command is invalid. You did not include <COMMAND>"; exit 1; }
|
||||
|
||||
# test terminal -e command
|
||||
wait_command="read -p 'Press any key to validate terminal...'"
|
||||
test_command=$(echo "$terminal_command" | sed "s/<COMMAND>/$wait_command/")
|
||||
|
||||
log INFO "Using test command: $test_command"
|
||||
$test_command
|
||||
[[ $? -ne 0 ]] && { log ERROR "Terminal command is invalid."; exit 1; }
|
||||
;;
|
||||
esac
|
||||
|
||||
# build terminal command
|
||||
terminal_command=$(echo "$terminal_command" | sed "s/<COMMAND>/h2mm nexus/")
|
||||
|
||||
# build the desktop entry
|
||||
local desktop_entry="[Desktop Entry]
|
||||
Name=h2mm
|
||||
Comment=Link application to be used with Nexus Mods
|
||||
Exec=$terminal_command
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Game;
|
||||
MimeType=x-scheme-handler/nxm;"
|
||||
|
||||
local desktop_file="$HOME/.local/share/applications/h2mm.desktop"
|
||||
|
||||
# create the desktop entry file
|
||||
echo "$desktop_entry" > "$desktop_file"
|
||||
[[ $? -ne 0 ]] && { log ERROR "Could not create desktop entry."; exit 1; }
|
||||
|
||||
log INFO ""
|
||||
log INFO "Desktop entry ${GREEN}successfully${NC} created."
|
||||
log INFO "Your system needs to refresh the desktop database to use the new entry."
|
||||
log INFO "This requires sudo permissions. Please enter your password."
|
||||
|
||||
sudo update-desktop-database
|
||||
[[ $? -ne 0 ]] && { log ERROR "Could not refresh desktop database."; exit 1; }
|
||||
|
||||
log INFO "You can now use the Nexus Mods integration."
|
||||
log INFO "To use it, go to a mod page and click on the \"Vortex\" or \"Download with Manager\" button."
|
||||
log INFO "Your browser will ask you to open the link with h2mm."
|
||||
}
|
||||
|
||||
function nexus_get_mod_info() {
|
||||
local mod_id="$1"
|
||||
local mod_file_id="$2"
|
||||
|
||||
[[ -z "$mod_id" || -z "$mod_file_id" ]] && { log ERROR "Mod ID and file ID are required."; exit 1; }
|
||||
|
||||
# api_key
|
||||
get_nexus_api_key
|
||||
|
||||
api_url="https://api.nexusmods.com/v1/games/helldivers2/mods/$mod_id/files/$mod_file_id.json"
|
||||
response=$(curl -sSw " http:%{http_code}" -H "apikey: $api_key" "$api_url")
|
||||
[[ $? -ne 0 ]] && { log ERROR "curl failed."; exit 1; }
|
||||
|
||||
# checks
|
||||
status="${response: -3}"
|
||||
[[ "$status" != "200" ]] && { log ERROR "Invalid response from Nexus Mods API."; exit 1; }
|
||||
|
||||
# extract info
|
||||
mod_name=$(echo "$response" | grep -oP '"name":"\K[^"]+')
|
||||
nexus_mod_version=$(echo "$response" | grep -oP '"version":"\K[^"]+')
|
||||
[[ -z "$mod_name" || -z "$nexus_mod_version" ]] && { log ERROR "Could not extract mod name and version."; exit 1; }
|
||||
}
|
||||
|
||||
function nexus_check_for_updates() {
|
||||
# get list of entries that have a nexus mod_id
|
||||
IFS=$'\n' nexus_mods=($(awk -F, 'NR > 1 && $4 != "" {print $0}' "$DB_FILE")); unset IFS
|
||||
[[ -z "$nexus_mods" ]] && return
|
||||
|
||||
local update_count=0
|
||||
[[ -f "$UPDATES_AVAILABLE_FILE" ]] && rm -f "$UPDATES_AVAILABLE_FILE"
|
||||
|
||||
# call the API for each mod_id and get the latest version
|
||||
for entry in "${nexus_mods[@]}"; do
|
||||
nexus_mod_id=$(get_nexus_mod_id_by_entry_from_db "$entry")
|
||||
nexus_mod_file_id=$(get_nexus_mod_file_id_by_entry_from_db "$entry")
|
||||
local_mod_version=$(get_nexus_mod_version_by_entry_from_db "$entry")
|
||||
|
||||
nexus_get_mod_info "$nexus_mod_id" "$nexus_mod_file_id"
|
||||
|
||||
# compare the latest version with the installed version
|
||||
if [[ "$nexus_mod_version" != "$local_mod_version" ]]; then
|
||||
mod_name=$(get_name_by_entry_from_db "$entry")
|
||||
|
||||
# store in a temp file (inside config dir) the mods that have updates
|
||||
echo "$mod_name,$local_mod_version,$nexus_mod_version,$nexus_mod_id,$nexus_mod_file_id" >> "$UPDATES_AVAILABLE_FILE"
|
||||
update_count=$((update_count + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
[[ $update_count -eq 0 ]] && return 0;
|
||||
log WARNING "Updates available for ${ORANGE}$update_count${NC} Nexus mods => run 'h2mm nexus-update'."
|
||||
}
|
||||
|
||||
function nexus_update_mods() {
|
||||
parse_help_no_arguments display_help_nexus_update "$@"
|
||||
|
||||
[[ $(wc -l < "$UPDATES_AVAILABLE_FILE") -eq 0 ]] && { log INFO "No updates available."; return 0; }
|
||||
|
||||
log INFO "Due to Nexus Mods API limitations, the update process is not automated."
|
||||
log INFO "Please visit the direct download links to install the latest version of your mods:"
|
||||
|
||||
awk -v GREEN="$GREEN" -v ORANGE="$ORANGE" -v NC="$NC" -F, '{
|
||||
printf "%2s. %s\n", NR, $1
|
||||
printf " => Update from %s%s%s -> %s%s%s\n", ORANGE, $2, NC, GREEN, $3, NC
|
||||
printf " -> Direct install: https://www.nexusmods.com/helldivers2/mods/%s?tab=files&file_id=%s&nmm=1\n", $4, $5
|
||||
printf " -> Mod page: https://www.nexusmods.com/helldivers2/mods/%s\n", $4
|
||||
}' "$UPDATES_AVAILABLE_FILE"
|
||||
}
|
||||
|
||||
|
||||
function nexus() {
|
||||
trap 'read -p "Press any key to continue..."' EXIT
|
||||
local mod_nxm_link="$1"
|
||||
|
||||
[[ -z "$mod_nxm_link" ]] && { log ERROR "Nexus Mods nxm link is required."; exit 1; }
|
||||
[[ ! "$mod_nxm_link" =~ ^nxm://helldivers2/mods/[0-9]+/files/[0-9]+\?key=.*\&expires=[0-9]+\&user_id=[0-9]+$ ]] && { log ERROR "Invalid Nexus Mods nxm link."; exit 1; }
|
||||
|
||||
log INFO "Received NXM link: $mod_nxm_link"
|
||||
|
||||
# api_key
|
||||
get_nexus_api_key
|
||||
|
||||
# extract info
|
||||
nexus_mod_id=$(echo "$mod_nxm_link" | awk -F/ '{print $5}')
|
||||
nexus_mod_file_id=$(echo "$mod_nxm_link" | awk -F/ '{print $7}' | cut -d\? -f1)
|
||||
key=$(echo "$mod_nxm_link" | awk -F= '{print $2}' | cut -d\& -f1)
|
||||
expires=$(echo "$mod_nxm_link" | awk -F= '{print $3}' | cut -d\& -f1)
|
||||
|
||||
api_url="https://api.nexusmods.com/v1/games/helldivers2/mods/$nexus_mod_id/files/$nexus_mod_file_id/download_link.json?key=$key&expires=$expires"
|
||||
log INFO "Using API URL: $api_url"
|
||||
|
||||
response=$(curl -sSw " http:%{http_code}" -H "apikey: $api_key" "$api_url")
|
||||
[[ $? -ne 0 ]] && { log ERROR "curl failed."; exit 1; }
|
||||
|
||||
# checks
|
||||
status="${response: -3}"
|
||||
[[ "$status" != "200" ]] && { log ERROR "Invalid response from Nexus Mods API."; exit 1; }
|
||||
log INFO "Received response from Nexus Mods API: $response"
|
||||
|
||||
# extract URI
|
||||
download_url=$(printf "$response" | sed -E 's/.*"URI":"([^"]+)".*/\1/' | sed 's/\ /%20/g')
|
||||
log INFO "Using download URL: $download_url"
|
||||
log INFO ""
|
||||
|
||||
# extract file name
|
||||
file_name=$(echo "$download_url" | awk -F/ '{print $6}' | cut -d\? -f1)
|
||||
output_file="$(mktemp -d)/$file_name"
|
||||
|
||||
# download the file
|
||||
log INFO "Download progress:"
|
||||
curl -Lo "$output_file" "$download_url"
|
||||
[[ $? -ne 0 ]] && { log ERROR "Download failed."; exit 1; }
|
||||
|
||||
log INFO "Download ${GREEN}successfully${NC} completed."
|
||||
log INFO ""
|
||||
|
||||
# get mod info
|
||||
nexus_get_mod_info "$nexus_mod_id" "$nexus_mod_file_id"
|
||||
|
||||
# check if the mod is already installed, if yes, update it, otherwise install it
|
||||
existing_mod_entry=$(get_entry_from_db_by_nexus_mod_id "$nexus_mod_id")
|
||||
if [[ -z "$existing_mod_entry" ]]; then
|
||||
# install the mod if it doesn't exist
|
||||
mod_install "$output_file" -n "$mod_name" --mod-id "$nexus_mod_id" --file-id "$nexus_mod_file_id" --version "$nexus_mod_version"
|
||||
else
|
||||
# replace the mod if it exists
|
||||
existing_mod_index=$(get_index_by_entry_from_db "$existing_mod_entry")
|
||||
mod_index=$(get_index_by_entry_from_db "$existing_mod_entry")
|
||||
|
||||
mod_uninstall -i "$mod_index"
|
||||
|
||||
mod_install "$output_file" -n "$mod_name" --mod-id "$nexus_mod_id" --file-id "$nexus_mod_file_id" --version "$nexus_mod_version"
|
||||
|
||||
# get the current mod index from what we just installed, last line of the db
|
||||
new_mod_index=$(get_index_by_entry_from_db "$(awk -F, 'END {print $1}' "$DB_FILE")")
|
||||
[[ -z "$new_mod_index" ]] && { log ERROR "Could not get current mod index."; exit 1; }
|
||||
|
||||
[[ $mod_index -ne $old_mod_index ]] && mod_order -i "$new_mod_index" "$mod_index"
|
||||
|
||||
log INFO "Mod ${GREEN}successfully${NC} updated: $mod_name."
|
||||
|
||||
disable_all_modpacks
|
||||
fi
|
||||
}
|
||||
|
||||
# --- Update Check ---
|
||||
|
||||
function check_h2mm_update() {
|
||||
latest_version=$(curl -sS "$VERSION_URL")
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log ERROR "Could not check for updates."
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$latest_version" != "$VERSION" ]]; then
|
||||
log WARNING "A new version of h2mm is available: ${ORANGE}$VERSION${NC} -> ${GREEN}$latest_version${NC} => run 'h2mm update'."
|
||||
fi
|
||||
}
|
||||
|
||||
function check_for_updates() {
|
||||
last_update=$(cat "$LAST_CHECKED_UPDATE_FILE")
|
||||
if [[ -n "$last_update" && "$(date +%s)" -lt $(("$(date +%s -d "$last_update")" + 7200)) ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
log INFO "Checking for updates..."
|
||||
|
||||
check_h2mm_update
|
||||
nexus_check_for_updates
|
||||
|
||||
echo "$(date)" > "$LAST_CHECKED_UPDATE_FILE"
|
||||
}
|
||||
|
||||
# --- Main ---
|
||||
|
||||
function main() {
|
||||
@@ -1545,6 +1913,15 @@ function main() {
|
||||
"modpack-reset"|"mr")
|
||||
modpack_reset "$@"
|
||||
;;
|
||||
"nexus-setup"|"ns")
|
||||
nexus_setup "$@"
|
||||
;;
|
||||
"nexus-update"|"nu")
|
||||
nexus_update_mods "$@"
|
||||
;;
|
||||
"nexus")
|
||||
nexus "$@"
|
||||
;;
|
||||
"reset"|"r")
|
||||
mod_reset "$@"
|
||||
;;
|
||||
|
||||
Reference in New Issue
Block a user