Bash fixes and improvements

General fixes to improve some of the bash logic and error handling. Thanks to the commit "S.B" made in their fork, I manually cherry-picked some of their bash fixes (while making a few changes along the way)
https://github.com/s-b-repo/SpotX-Bash/commit/1d023dc8a6193ba564221a15d255d5f1f14b14cf

Co-authored-by: "S.B" <30941141+s-b-repo@users.noreply.github.com>
This commit is contained in:
jetfir3
2026-06-15 14:36:24 -04:00
committed by GitHub
parent 5b5674949b
commit 6edea7e909
+56 -43
View File
@@ -8,6 +8,11 @@ latestB_A="55"
rollbackB_X="262" rollbackB_X="262"
rollbackB_A="262" rollbackB_A="262"
clr='\033[0m'
green='\033[0;32m'
red='\033[0;31m'
yellow='\033[0;33m'
command -v perl >/dev/null || { echo -e "\n${red}Error:${clr} perl command not found.\nInstall perl on your system then try again.\n" >&2; exit 1; } command -v perl >/dev/null || { echo -e "\n${red}Error:${clr} perl command not found.\nInstall perl on your system then try again.\n" >&2; exit 1; }
case $(uname | tr '[:upper:]' '[:lower:]') in case $(uname | tr '[:upper:]' '[:lower:]') in
@@ -15,11 +20,6 @@ case $(uname | tr '[:upper:]' '[:lower:]') in
*) platformType='Linux' ;; *) platformType='Linux' ;;
esac esac
clr='\033[0m'
green='\033[0;32m'
red='\033[0;31m'
yellow='\033[0;33m'
show_help() { show_help() {
echo -e \ echo -e \
"Options: "Options:
@@ -293,7 +293,7 @@ linux_client_variant() {
} }
linux_deb_prepare() { linux_deb_prepare() {
command -v apt >/dev/null || { echo -e "${red}Error:${clear} Debian-based Linux distro with APT support is required." >&2; exit 1; } command -v apt >/dev/null || { echo -e "${red}Error:${clr} Debian-based Linux distro with APT support is required.\n" >&2; exit 1; }
installPath=/usr/share/spotify installPath=/usr/share/spotify
installOutput="${installPath}" installOutput="${installPath}"
linux_client_variant linux_client_variant
@@ -327,17 +327,10 @@ linux_no_client() {
} }
linux_search_path() { linux_search_path() {
local timeout=6
local paths=("/opt" "/usr/share" "/var/lib/flatpak" "$HOME/.local/share" "/") local paths=("/opt" "/usr/share" "/var/lib/flatpak" "$HOME/.local/share" "/")
for path in "${paths[@]}"; do for path in "${paths[@]}"; do
local path="${path}" installPath=$(timeout 6 find "${path}" -type f -path "*/spotify*Apps/*" -not -path "*snapd/snap*" -not -path "*snap/spotify*" -not -path "*snap/bin*" -not -path "*flatpak/.removed*" -name "xpui.spa" -size -20M -size +3M -print -quit 2>/dev/null | rev | cut -d/ -f3- | rev)
local timeLimit=$(($(date +%s) + timeout)) [[ -n "${installPath}" ]] && return 0
while (( $(date +%s) < "${timeLimit}" )); do
installPath=$(find "${path}" -type f -path "*/spotify*Apps/*" -not -path "*snapd/snap*" -not -path "*snap/spotify*" -not -path "*snap/bin*" -not -path "*flatpak/.removed*" -name "xpui.spa" -size -20M -size +3M -print -quit 2>/dev/null | rev | cut -d/ -f3- | rev)
[[ -n "${installPath}" ]] && return 0
pgrep -x find > /dev/null || break
sleep 1
done
done done
return 1 return 1
} }
@@ -445,15 +438,14 @@ run_prepare() {
(($(ver "${clientVer}") > $(ver "${legacyMaxVer}"))) && macos_legacy_notice "toohigh" (($(ver "${clientVer}") > $(ver "${legacyMaxVer}"))) && macos_legacy_notice "toohigh"
client_version_output client_version_output
ver_check ver_check
command pgrep [sS]potify 2>/dev/null | xargs kill -9 2>/dev/null command pkill -9 '[sS]potify' 2>/dev/null
[[ -f "${appBinary}" ]] && cleanAB=$(perl -ne '$found1 = 1 if /\x00\x73\x6C\x6F\x74\x73\x00/; $found2 = 1 if /\x2D\x70\x72\x65\x72\x6F\x6C\x6C/; END { print "true" if $found1 && $found2 }' "${appBinary}") [[ -f "${appBinary}" ]] && cleanAB=$(perl -ne '$found1 = 1 if /\x00\x73\x6C\x6F\x74\x73\x00/; $found2 = 1 if /\x2D\x70\x72\x65\x72\x6F\x6C\x6C/; END { print "true" if $found1 && $found2 }' "${appBinary}")
} }
check_write_permission() { check_write_permission() {
local paths=("$@") local target_user="${SUDO_USER:-$(id -un)}"
for path in "${paths[@]}"; do for path_to_check in "$@"; do
local path="${path}" [[ ! -w "${path_to_check}" ]] && {
[[ ! -w "${path}" ]] && {
sudo -n true 2>/dev/null || { sudo -n true 2>/dev/null || {
echo -e "${yellow}Warning:${clr} SpotX-Bash does not have write permission in client directory.\nRequesting sudo permission..." >&2 echo -e "${yellow}Warning:${clr} SpotX-Bash does not have write permission in client directory.\nRequesting sudo permission..." >&2
sudo -v || { sudo -v || {
@@ -461,16 +453,17 @@ check_write_permission() {
exit 1 exit 1
} }
} }
sudo chmod -R a+wr "${appPath}" sudo chown -R "${target_user}" "${path_to_check}"
sudo chmod -R u+rwX,go-w "${path_to_check}"
} }
done done
} }
uninstall_spotx() { uninstall_spotx() {
rm "${appBinary}" 2>/dev/null rm -f "${appBinary}" 2>/dev/null
mv "${appBak}" "${appBinary}" mv -f "${appBak}" "${appBinary}"
rm "${xpuiSpa}" 2>/dev/null rm -f "${xpuiSpa}" 2>/dev/null
mv "${xpuiBak}" "${xpuiSpa}" mv -f "${xpuiBak}" "${xpuiSpa}"
rm -rf "${xpuiDir}" 2>/dev/null rm -rf "${xpuiDir}" 2>/dev/null
} }
@@ -483,8 +476,8 @@ run_uninstall_check() {
check_write_permission "${appPath}" "${appBinary}" "${xpuiPath}" "${xpuiSpa}" check_write_permission "${appPath}" "${appBinary}" "${xpuiPath}" "${xpuiSpa}"
[[ "${cleanAB}" ]] && { [[ "${cleanAB}" ]] && {
echo -e "${yellow}Warning:${clr} SpotX-Bash has detected abnormal behavior.\nClient reinstallation may be required...\n" >&2 echo -e "${yellow}Warning:${clr} SpotX-Bash has detected abnormal behavior.\nClient reinstallation may be required...\n" >&2
rm "${appBak}" 2>/dev/null rm -f "${appBak}" 2>/dev/null
rm "${xpuiBak}" 2>/dev/null rm -f "${xpuiBak}" 2>/dev/null
} || { } || {
uninstall_spotx uninstall_spotx
} }
@@ -508,7 +501,7 @@ perlvar() {
read_yn() { read_yn() {
local yn local yn
while : ; do while : ; do
read -rp "$*" yn read -rp "$*" yn || { echo; return 1; }
case "$yn" in case "$yn" in
[Yy]* ) return 0 ;; [Yy]* ) return 0 ;;
[Nn]* ) return 1 ;; [Nn]* ) return 1 ;;
@@ -524,7 +517,7 @@ run_interactive_check() {
[[ "${platformType}" == "macOS" && -z "${clientVer+x}" ]] && clientVer="${versionVar}" [[ "${platformType}" == "macOS" && -z "${clientVer+x}" ]] && clientVer="${versionVar}"
[[ "${platformType}" == "macOS" && -z "${legacyMac+x}" && -z "${installMac+x}" ]] && { read_yn "Download & install client ${versionVar}? " && { installClient='true'; installMac='true'; }; } [[ "${platformType}" == "macOS" && -z "${legacyMac+x}" && -z "${installMac+x}" ]] && { read_yn "Download & install client ${versionVar}? " && { installClient='true'; installMac='true'; }; }
[[ "${platformType}" == "macOS" ]] && { read_yn "Block client auto-updates? " && blockUpdates='true'; } [[ "${platformType}" == "macOS" ]] && { read_yn "Block client auto-updates? " && blockUpdates='true'; }
[[ "${platformType}" == "Linux" && -z "${installDeb+x}" && "${notInstalled}" ]] && { read_yn "Download & install client ${downloadVer} deb pkg? " && installDeb='true' clientVer="${downloadVer}" || installClient='false'; } [[ "${platformType}" == "Linux" && -z "${installDeb+x}" && "${notInstalled}" ]] && { read_yn "Download & install client ${downloadVer} deb pkg? " && installDeb='true' clientVer="${downloadVer}" || unset installClient; }
[[ -d "${cachePath}" ]] && read_yn "Clear client app cache? " && clearCache='true' [[ -d "${cachePath}" ]] && read_yn "Clear client app cache? " && clearCache='true'
(($(ver "${clientVer}") >= $(ver "1.1.93.896") && $(ver "${clientVer}") <= $(ver "1.2.13.661"))) && { read_yn "Enable new home screen UI? " || oldUi='true'; } (($(ver "${clientVer}") >= $(ver "1.1.93.896") && $(ver "${clientVer}") <= $(ver "1.2.13.661"))) && { read_yn "Enable new home screen UI? " || oldUi='true'; }
(($(ver "${clientVer}") > $(ver "1.1.99.878"))) && { read_yn "Enable developer mode? " && devMode='true'; } (($(ver "${clientVer}") > $(ver "1.1.99.878"))) && { read_yn "Enable developer mode? " && devMode='true'; }
@@ -567,6 +560,11 @@ linux_deb_install() {
"0F0UjRXQDJWeWNTW" \ "0F0UjRXQDJWeWNTW" \
| rev | base64 --decode | base64 --decode) | rev | base64 --decode | base64 --decode)
eval "${lc01}"; eval "${lc02}" eval "${lc01}"; eval "${lc02}"
dpkg-deb --info "${workDir}/${fileVar}" &>/dev/null || {
rm "${workDir}/${fileVar}" 2>/dev/null
echo -e "\n${red}Error:${clr} Downloaded client package is corrupt or incomplete. Exiting...\n" >&2
exit 1
}
printf "\xE2\x9C\x94\x20\x44\x6F\x77\x6E\x6C\x6F\x61\x64\x65\x64\x20\x61\x6E\x64\x20\x69\x6E\x73\x74\x61\x6C\x6C\x69\x6E\x67\x20\x53\x70\x6F\x74\x69\x66\x79\n" printf "\xE2\x9C\x94\x20\x44\x6F\x77\x6E\x6C\x6F\x61\x64\x65\x64\x20\x61\x6E\x64\x20\x69\x6E\x73\x74\x61\x6C\x6C\x69\x6E\x67\x20\x53\x70\x6F\x74\x69\x66\x79\n"
[[ -f "${appBak}" ]] && sudo rm "${appBak}" 2>/dev/null [[ -f "${appBak}" ]] && sudo rm "${appBak}" 2>/dev/null
[[ -f "${xpuiBak}" ]] && sudo rm "${xpuiBak}" 2>/dev/null [[ -f "${xpuiBak}" ]] && sudo rm "${xpuiBak}" 2>/dev/null
@@ -607,9 +605,14 @@ macos_client_install() {
"alHZyIWeChFT0F0UjRXQDJWeWNTW" \ "alHZyIWeChFT0F0UjRXQDJWeWNTW" \
| rev | base64 --decode | base64 --decode) | rev | base64 --decode | base64 --decode)
eval "${mc01}"; eval "${mc02}" eval "${mc01}"; eval "${mc02}"
tar -tf "$HOME/Downloads/${fileVar}" >/dev/null 2>&1 || {
rm "$HOME/Downloads/${fileVar}" 2>/dev/null
echo -e "\n${red}Error:${clr} Downloaded client archive is corrupt or incomplete. Exiting...\n" >&2
exit 1
}
printf "\xE2\x9C\x94\x20\x44\x6F\x77\x6E\x6C\x6F\x61\x64\x65\x64\x20\x61\x6E\x64\x20\x69\x6E\x73\x74\x61\x6C\x6C\x69\x6E\x67\x20\x53\x70\x6F\x74\x69\x66\x79\n" printf "\xE2\x9C\x94\x20\x44\x6F\x77\x6E\x6C\x6F\x61\x64\x65\x64\x20\x61\x6E\x64\x20\x69\x6E\x73\x74\x61\x6C\x6C\x69\x6E\x67\x20\x53\x70\x6F\x74\x69\x66\x79\n"
rm -rf "${appPath}" 2>/dev/null rm -rf "${appPath}" 2>/dev/null
mkdir "${appPath}" mkdir -p "${appPath}"
tar -xpf "$HOME/Downloads/${fileVar}" -C "${appPath}" && unset notInstalled versionFailed || { tar -xpf "$HOME/Downloads/${fileVar}" -C "${appPath}" && unset notInstalled versionFailed || {
rm "$HOME/Downloads/${fileVar}" 2>/dev/null rm "$HOME/Downloads/${fileVar}" 2>/dev/null
echo -e "\n${red}Error:${clr} Client install failed. Exiting...\n" >&2 echo -e "\n${red}Error:${clr} Client install failed. Exiting...\n" >&2
@@ -629,12 +632,14 @@ run_install_check() {
run_cache_check() { run_cache_check() {
[[ "${clearCache}" ]] && { [[ "${clearCache}" ]] && {
rm -rf "${cachePath}/Browser" 2>/dev/null [[ -n "${cachePath}" && -d "${cachePath}" ]] && {
rm -rf "${cachePath}/Data" 2>/dev/null rm -rf "${cachePath}/Browser" 2>/dev/null
rm -rf "${cachePath}/Default/Local Storage/leveldb" 2>/dev/null rm -rf "${cachePath}/Data" 2>/dev/null
rm -rf "${cachePath}/public.ldb" 2>/dev/null rm -rf "${cachePath}/Default/Local Storage/leveldb" 2>/dev/null
rm "${cachePath}/LocalPrefs.json" 2>/dev/null rm -rf "${cachePath}/public.ldb" 2>/dev/null
printf "\xE2\x9C\x94\x20\x43\x6C\x65\x61\x72\x65\x64\x20\x61\x70\x70\x20\x63\x61\x63\x68\x65\n" rm "${cachePath}/LocalPrefs.json" 2>/dev/null
printf "\xE2\x9C\x94\x20\x43\x6C\x65\x61\x72\x65\x64\x20\x61\x70\x70\x20\x63\x61\x63\x68\x65\n"
} || echo -e "${yellow}Warning:${clr} Cache directory not found, skipping cache clear.\n" >&2
} }
} }
@@ -664,14 +669,14 @@ perlVar() {
xpui_detect() { xpui_detect() {
[[ (-f "${appBak}" || -f "${xpuiBak}") && "${cleanAB}" ]] && { [[ (-f "${appBak}" || -f "${xpuiBak}") && "${cleanAB}" ]] && {
rm "${appBak}" 2>/dev/null; rm "${xpuiBak}" 2>/dev/null rm -f "${appBak}" 2>/dev/null; rm -f "${xpuiBak}" 2>/dev/null
cp "${xpuiSpa}" "${xpuiBak}"; cp "${appBinary}" "${appBak}" cp "${xpuiSpa}" "${xpuiBak}"; cp "${appBinary}" "${appBak}"
printf "\xE2\x9C\x94\x20\x43\x72\x65\x61\x74\x65\x64\x20\x62\x61\x63\x6B\x75\x70\n" printf "\xE2\x9C\x94\x20\x43\x72\x65\x61\x74\x65\x64\x20\x62\x61\x63\x6B\x75\x70\n"
return return
} }
[[ (-f "${appBak}" || -f "${xpuiBak}") && "${forceSpotx}" ]] && { [[ (-f "${appBak}" || -f "${xpuiBak}") && "${forceSpotx}" ]] && {
[[ -f "${appBak}" ]] && { rm "${appBinary}"; cp "${appBak}" "${appBinary}"; } [[ -f "${appBak}" ]] && { rm -f "${appBinary}"; cp "${appBak}" "${appBinary}"; }
[[ -f "${xpuiBak}" ]] && { rm "${xpuiSpa}"; cp "${xpuiBak}" "${xpuiSpa}"; } [[ -f "${xpuiBak}" ]] && { rm -f "${xpuiSpa}"; cp "${xpuiBak}" "${xpuiSpa}"; }
printf "\xE2\x9C\x94\x20\x44\x65\x74\x65\x63\x74\x65\x64\x20\x26\x20\x72\x65\x73\x74\x6F\x72\x65\x64\x20\x62\x61\x63\x6B\x75\x70\n" printf "\xE2\x9C\x94\x20\x44\x65\x74\x65\x63\x74\x65\x64\x20\x26\x20\x72\x65\x73\x74\x6F\x72\x65\x64\x20\x62\x61\x63\x6B\x75\x70\n"
return return
} }
@@ -741,7 +746,11 @@ snapshot_check() {
xpui_open() { xpui_open() {
mkdir -p "${xpuiDir}" mkdir -p "${xpuiDir}"
unzip -qq "${xpuiSpa}" -d "${xpuiDir}" unzip -qq "${xpuiSpa}" -d "${xpuiDir}" || {
rm -rf "${xpuiDir}" 2>/dev/null
echo -e "\n${red}Error:${clr} Failed to unpack xpui.spa. Reinstall client. Exiting...\n" >&2
exit 1
}
snapshot_check snapshot_check
[[ "${versionFailed}" && -z "${forceVer+x}" || -z "${forceVer+x}" && "${debug}" && "${devMode}" && "${t}" ]] && { [[ "${versionFailed}" && -z "${forceVer+x}" || -z "${forceVer+x}" && "${debug}" && "${devMode}" && "${t}" ]] && {
clientVer=$(perl -ne '/[Vv]ersion[:=,\x22]{1,3}(1\.[0-9]+\.[0-9]+\.[0-9]+)\.g[0-9a-f]+/ && print "$1"' "${xpuiJs}") clientVer=$(perl -ne '/[Vv]ersion[:=,\x22]{1,3}(1\.[0-9]+\.[0-9]+\.[0-9]+)\.g[0-9a-f]+/ && print "$1"' "${xpuiJs}")
@@ -775,7 +784,7 @@ run_core_start() {
final_setup_check final_setup_check
check_write_permission "${appPath}" "${appBinary}" "${xpuiPath}" "${xpuiSpa}" check_write_permission "${appPath}" "${appBinary}" "${xpuiPath}" "${xpuiSpa}"
xpui_detect xpui_detect
[[ "${xpuiSkip}" ]] && { printf "\xE2\x9C\x94\x20\x46\x69\x6E\x69\x73\x68\x65\x64\n\n"; exit 1; } [[ "${xpuiSkip}" ]] && { printf "\xE2\x9C\x94\x20\x46\x69\x6E\x69\x73\x68\x65\x64\n\n"; exit 0; }
xpui_open xpui_open
(($(ver "${clientVer}") > $(ver "1.2.56.9999"))) && vendorXpuiJs="${xpuiJs}" (($(ver "${clientVer}") > $(ver "1.2.56.9999"))) && vendorXpuiJs="${xpuiJs}"
} }
@@ -831,8 +840,12 @@ run_patches() {
run_finish() { run_finish() {
echo -e "\n//# SpotX was here" >> "${xpuiJs}" echo -e "\n//# SpotX was here" >> "${xpuiJs}"
rm "${xpuiSpa}" rm -f "${xpuiSpa}"
(cd "${xpuiDir}" || exit; zip -qq -r ../xpui.spa .) (cd "${xpuiDir}" && zip -qq -r ../xpui.spa .) || {
echo -e "\n${red}Error:${clr} Failed to repackage client." >&2
echo -e "Spotify is now in a broken state. Please reinstall client.\n" >&2
exit 1
}
rm -rf "${xpuiDir}" rm -rf "${xpuiDir}"
[[ "${platformType}" == "macOS" ]] && { [[ "${platformType}" == "macOS" ]] && {
[[ "${skipCodesign}" ]] && /usr/bin/xattr -cr "${appPath}" 2>/dev/null || { [[ "${skipCodesign}" ]] && /usr/bin/xattr -cr "${appPath}" 2>/dev/null || {