mirror of
https://github.com/SpotX-Official/SpotX-Bash.git
synced 2026-08-09 17:41:07 +10:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4b9f7d20f | ||
|
|
852acb2acb | ||
|
|
7f190c6660 |
@@ -107,7 +107,7 @@ while getopts ':BcdefF:hilopP:SvV:-:' flag; do
|
||||
noexp) excludeExp='true' ;;
|
||||
oldui) oldUi='true' ;;
|
||||
premium) paidPremium='true' ;;
|
||||
rollback) [[ "${platformType}" == "macOS" ]] && rollback='true'; installMac='true' ;;
|
||||
rollback) [[ "${platformType}" == "macOS" ]] && { rollback='true'; installMac='true'; } ;;
|
||||
skipcodesign) [[ "${platformType}" == "macOS" ]] && skipCodesign='true' ;;
|
||||
stable) [[ "${platformType}" == "Linux" ]] && stableVar='true' ;;
|
||||
uninstall) uninstallSpotx='true' ;;
|
||||
@@ -486,9 +486,22 @@ run_prepare() {
|
||||
}
|
||||
|
||||
check_write_permission() {
|
||||
local target_user="${SUDO_USER:-$(id -un)}"
|
||||
local writePath
|
||||
[[ "${platformType}" == "Linux" && -z "${SPOTX_BUILD_MODE}" ]] && ((EUID == 0)) && {
|
||||
stagedInstall='true'
|
||||
protectedInstall='true'
|
||||
}
|
||||
for path_to_check in "$@"; do
|
||||
[[ ! -w "${path_to_check}" ]] && {
|
||||
[[ -d "${path_to_check}" ]] && writePath="${path_to_check}" || writePath="${path_to_check%/*}"
|
||||
[[ ! -w "${path_to_check}" ]] && stagedInstall='true'
|
||||
[[ ! -w "${writePath}" ]] && {
|
||||
stagedInstall='true'
|
||||
protectedInstall='true'
|
||||
((EUID == 0)) && continue
|
||||
command -v sudo >/dev/null || {
|
||||
echo -e "\n${red}Error:${clr} sudo command not found. Install sudo or run this script as root.\n" >&2
|
||||
exit 1
|
||||
}
|
||||
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
|
||||
sudo -v || {
|
||||
@@ -496,17 +509,172 @@ check_write_permission() {
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
sudo chown -R "${target_user}" "${path_to_check}"
|
||||
sudo chmod -R u+rwX,go-w "${path_to_check}"
|
||||
}
|
||||
done
|
||||
}
|
||||
|
||||
sudo_run() {
|
||||
if ((EUID == 0)) || [[ -z "${protectedInstall+x}" ]]; then
|
||||
command "$@"
|
||||
else
|
||||
sudo "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
protected_stage_copy() {
|
||||
local source="${1}" destination="${2}"
|
||||
sudo_run cat -- "${source}" > "${destination}" || {
|
||||
rm -f -- "${destination}"
|
||||
return 1
|
||||
}
|
||||
chmod 600 "${destination}"
|
||||
}
|
||||
|
||||
protected_stage_prepare() {
|
||||
targetAppBinary="${appBinary}"
|
||||
targetAppBak="${appBak}"
|
||||
targetXpuiPath="${xpuiPath}"
|
||||
targetXpuiSpa="${xpuiSpa}"
|
||||
targetXpuiBak="${xpuiBak}"
|
||||
linux_working_dir
|
||||
appPath="${workDir}/client"
|
||||
appBinary="${appPath}/spotify"
|
||||
appBak="${appBinary}.bak"
|
||||
xpuiPath="${appPath}/Apps"
|
||||
xpuiBak="${xpuiPath}/xpui.bak"
|
||||
xpuiDir="${xpuiPath}/xpui"
|
||||
xpuiSpa="${xpuiPath}/xpui.spa"
|
||||
dwpPanelSectionJs="${xpuiDir}/dwp-panel-section.js"
|
||||
homeHptoJs="${xpuiDir}/home-hpto.js"
|
||||
homeV2Js="${xpuiDir}/home-v2.js"
|
||||
indexHtml="${xpuiDir}/index.html"
|
||||
vendorXpuiJs="${xpuiDir}/vendor~xpui.js"
|
||||
xpuiCss="${xpuiDir}/xpui.css"
|
||||
xpuiDesktopModalsJs="${xpuiDir}/xpui-desktop-modals.js"
|
||||
xpuiJs="${xpuiDir}/xpui.js"
|
||||
xpuiSnapshotJs="${xpuiDir}/xpui-snapshot.js"
|
||||
mkdir -p "${xpuiPath}" || exit 1
|
||||
protected_stage_copy "${targetAppBinary}" "${appBinary}" || exit 1
|
||||
protected_stage_copy "${targetXpuiSpa}" "${xpuiSpa}" || exit 1
|
||||
[[ -f "${targetAppBak}" ]] && protected_stage_copy "${targetAppBak}" "${appBak}"
|
||||
[[ -f "${targetXpuiBak}" ]] && protected_stage_copy "${targetXpuiBak}" "${xpuiBak}"
|
||||
}
|
||||
|
||||
protected_prepare_file() {
|
||||
local source="${1}" destination="${2}" reference="${3}" tempFile
|
||||
tempFile=$(sudo_run mktemp "${destination}.spotx.XXXXXXXX") || return 1
|
||||
sudo_run cp -a -- "${reference}" "${tempFile}" &&
|
||||
sudo_run cp -- "${source}" "${tempFile}" &&
|
||||
sudo_run chown --reference="${reference}" "${tempFile}" &&
|
||||
sudo_run chmod --reference="${reference}" "${tempFile}" &&
|
||||
sudo_run touch -r "${reference}" "${tempFile}" || {
|
||||
sudo_run rm -f -- "${tempFile}"
|
||||
return 1
|
||||
}
|
||||
printf '%s\n' "${tempFile}"
|
||||
}
|
||||
|
||||
protected_save_destination() {
|
||||
local destination="${1}" tempFile
|
||||
[[ -e "${destination}" ]] || return 0
|
||||
tempFile=$(sudo_run mktemp "${destination}.spotx-rollback.XXXXXXXX") || return 1
|
||||
sudo_run cp -a -- "${destination}" "${tempFile}" || {
|
||||
sudo_run rm -f -- "${tempFile}"
|
||||
return 1
|
||||
}
|
||||
printf '%s\n' "${tempFile}"
|
||||
}
|
||||
|
||||
protected_restore_destination() {
|
||||
local backup="${1}" destination="${2}" existed="${3}"
|
||||
[[ "${existed}" ]] && sudo_run mv -f -- "${backup}" "${destination}" || sudo_run rm -f -- "${destination}"
|
||||
}
|
||||
|
||||
protected_remove_files() {
|
||||
local file
|
||||
for file in "$@"; do
|
||||
[[ -n "${file}" ]] && sudo_run rm -f -- "${file}"
|
||||
done
|
||||
}
|
||||
|
||||
protected_commit() {
|
||||
local uninstall="${1:-}" appFile spaFile appBakFile='' xpuiBakFile=''
|
||||
local appRollback='' spaRollback='' appBakRollback='' xpuiBakRollback=''
|
||||
local appExisted='' spaExisted='' appBakExisted='' xpuiBakExisted=''
|
||||
local appBakReference="${targetAppBinary}" xpuiBakReference="${targetXpuiSpa}"
|
||||
[[ -f "${appBinary}" && -f "${xpuiSpa}" ]] || return 1
|
||||
unzip -tqq "${xpuiSpa}" || return 1
|
||||
appFile=$(protected_prepare_file "${appBinary}" "${targetAppBinary}" "${targetAppBinary}") || return 1
|
||||
spaFile=$(protected_prepare_file "${xpuiSpa}" "${targetXpuiSpa}" "${targetXpuiSpa}") || {
|
||||
sudo_run rm -f -- "${appFile}"
|
||||
return 1
|
||||
}
|
||||
[[ -z "${uninstall}" ]] && {
|
||||
[[ -e "${targetAppBak}" ]] && appBakReference="${targetAppBak}"
|
||||
[[ -e "${targetXpuiBak}" ]] && xpuiBakReference="${targetXpuiBak}"
|
||||
appBakFile=$(protected_prepare_file "${appBak}" "${targetAppBak}" "${appBakReference}") || {
|
||||
sudo_run rm -f -- "${appFile}" "${spaFile}"
|
||||
return 1
|
||||
}
|
||||
xpuiBakFile=$(protected_prepare_file "${xpuiBak}" "${targetXpuiBak}" "${xpuiBakReference}") || {
|
||||
sudo_run rm -f -- "${appFile}" "${spaFile}" "${appBakFile}"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
[[ -e "${targetAppBinary}" ]] && appExisted='true'
|
||||
[[ -e "${targetXpuiSpa}" ]] && spaExisted='true'
|
||||
[[ -e "${targetAppBak}" ]] && appBakExisted='true'
|
||||
[[ -e "${targetXpuiBak}" ]] && xpuiBakExisted='true'
|
||||
{
|
||||
appRollback=$(protected_save_destination "${targetAppBinary}") &&
|
||||
spaRollback=$(protected_save_destination "${targetXpuiSpa}")
|
||||
[[ "${uninstall}" ]] || {
|
||||
appBakRollback=$(protected_save_destination "${targetAppBak}") &&
|
||||
xpuiBakRollback=$(protected_save_destination "${targetXpuiBak}")
|
||||
}
|
||||
} || {
|
||||
protected_remove_files "${appFile}" "${spaFile}" "${appBakFile}" "${xpuiBakFile}"
|
||||
protected_remove_files "${appRollback}" "${spaRollback}" "${appBakRollback}" "${xpuiBakRollback}"
|
||||
return 1
|
||||
}
|
||||
{
|
||||
{ [[ "${uninstall}" ]] || sudo_run mv -f -- "${appBakFile}" "${targetAppBak}"; } &&
|
||||
{ [[ "${uninstall}" ]] || sudo_run mv -f -- "${xpuiBakFile}" "${targetXpuiBak}"; } &&
|
||||
sudo_run mv -f -- "${appFile}" "${targetAppBinary}" &&
|
||||
sudo_run mv -f -- "${spaFile}" "${targetXpuiSpa}"
|
||||
} || {
|
||||
protected_restore_destination "${appRollback}" "${targetAppBinary}" "${appExisted}"
|
||||
protected_restore_destination "${spaRollback}" "${targetXpuiSpa}" "${spaExisted}"
|
||||
[[ "${uninstall}" ]] || protected_restore_destination "${appBakRollback}" "${targetAppBak}" "${appBakExisted}"
|
||||
[[ "${uninstall}" ]] || protected_restore_destination "${xpuiBakRollback}" "${targetXpuiBak}" "${xpuiBakExisted}"
|
||||
protected_remove_files "${appFile}" "${spaFile}" "${appBakFile}" "${xpuiBakFile}"
|
||||
return 1
|
||||
}
|
||||
protected_remove_files "${appRollback}" "${spaRollback}" "${appBakRollback}" "${xpuiBakRollback}"
|
||||
[[ "${uninstall}" ]] && sudo_run rm -f -- "${targetAppBak}" "${targetXpuiBak}"
|
||||
return 0
|
||||
}
|
||||
|
||||
atomic_copy() {
|
||||
local source="${1}" destination="${2}" tempFile result
|
||||
copyTempDir=$(mktemp -d "${destination}.spotx.XXXXXXXX") || return 1
|
||||
tempFile="${copyTempDir}/${destination##*/}"
|
||||
cp "${source}" "${tempFile}" && mv -f "${tempFile}" "${destination}"
|
||||
result=$?
|
||||
rm -rf "${copyTempDir}" 2>/dev/null
|
||||
[[ ! -d "${copyTempDir}" ]] && unset copyTempDir
|
||||
return "${result}"
|
||||
}
|
||||
|
||||
backup_spotx() {
|
||||
atomic_copy "${xpuiSpa}" "${xpuiBak}" && atomic_copy "${appBinary}" "${appBak}" && return 0
|
||||
rm -f "${appBak}" "${xpuiBak}" 2>/dev/null
|
||||
return 1
|
||||
}
|
||||
|
||||
uninstall_spotx() {
|
||||
rm -f "${appBinary}" 2>/dev/null
|
||||
mv -f "${appBak}" "${appBinary}"
|
||||
rm -f "${xpuiSpa}" 2>/dev/null
|
||||
mv -f "${xpuiBak}" "${xpuiSpa}"
|
||||
atomic_copy "${appBak}" "${appBinary}" && atomic_copy "${xpuiBak}" "${xpuiSpa}" || return 1
|
||||
rm -f "${appBak}" "${xpuiBak}" 2>/dev/null
|
||||
rm -rf "${xpuiDir}" 2>/dev/null
|
||||
}
|
||||
|
||||
@@ -517,12 +685,23 @@ run_uninstall_check() {
|
||||
exit 1
|
||||
}
|
||||
check_write_permission "${appPath}" "${appBinary}" "${xpuiPath}" "${xpuiSpa}"
|
||||
[[ "${platformType}" == "Linux" && "${stagedInstall}" ]] && protected_stage_prepare
|
||||
[[ "${cleanAB}" ]] && {
|
||||
echo -e "${yellow}Warning:${clr} SpotX-Bash has detected abnormal behavior.\nClient reinstallation may be required...\n" >&2
|
||||
rm -f "${appBak}" 2>/dev/null
|
||||
rm -f "${xpuiBak}" 2>/dev/null
|
||||
[[ "${platformType}" == "Linux" && "${stagedInstall}" ]] && sudo_run rm -f -- "${targetAppBak}" "${targetXpuiBak}"
|
||||
} || {
|
||||
uninstall_spotx
|
||||
uninstall_spotx || {
|
||||
echo -e "\n${red}Error:${clr} Failed to restore client. Backups were preserved.\n" >&2
|
||||
exit 1
|
||||
}
|
||||
[[ "${platformType}" == "Linux" && "${stagedInstall}" ]] && {
|
||||
protected_commit 'true' || {
|
||||
echo -e "\n${red}Error:${clr} Failed to restore client. Original files restored.\n" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
}
|
||||
printf "\xE2\x9C\x94\x20\x46\x69\x6E\x69\x73\x68\x65\x64\x20\x75\x6E\x69\x6E\x73\x74\x61\x6C\x6C\n\n"
|
||||
exit 0
|
||||
@@ -584,7 +763,26 @@ sudo_check() {
|
||||
}
|
||||
}
|
||||
|
||||
linux_working_dir() { [[ -d "/tmp" ]] && workDir="/tmp" || workDir="$HOME"; }
|
||||
cleanup_temp_dirs() {
|
||||
[[ -n "${workDir:-}" && -d "${workDir}" && "${workDir##*/}" == spotx-bash.* ]] && rm -rf -- "${workDir}"
|
||||
[[ -n "${copyTempDir:-}" && -d "${copyTempDir}" && "${copyTempDir##*/}" == *.spotx.* ]] && rm -rf -- "${copyTempDir}"
|
||||
[[ -n "${spaTempDir:-}" && -d "${spaTempDir}" && "${spaTempDir##*/}" == .spotx-spa.* ]] && rm -rf -- "${spaTempDir}"
|
||||
[[ "${xpuiTempCreated:-}" && -n "${xpuiDir:-}" && -d "${xpuiDir}" && "${xpuiDir##*/}" == "xpui" ]] && rm -rf -- "${xpuiDir}"
|
||||
}
|
||||
|
||||
linux_working_dir() {
|
||||
local tempBase="${TMPDIR:-/tmp}"
|
||||
[[ -n "${workDir:-}" && -d "${workDir}" && "${workDir##*/}" == spotx-bash.* ]] && return
|
||||
[[ "${tempBase}" == /* && -d "${tempBase}" && -w "${tempBase}" ]] || tempBase="/tmp"
|
||||
workDir=$(mktemp -d "${tempBase%/}/spotx-bash.XXXXXXXX") || {
|
||||
echo -e "${red}Error:${clr} Failed to create temporary working directory.\n" >&2
|
||||
exit 1
|
||||
}
|
||||
chmod 700 "${workDir}" || {
|
||||
echo -e "${red}Error:${clr} Failed to secure temporary working directory.\n" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
linux_deb_install() {
|
||||
sudo_check
|
||||
@@ -712,26 +910,34 @@ perlVar() {
|
||||
|
||||
xpui_detect() {
|
||||
[[ (-f "${appBak}" || -f "${xpuiBak}") && "${cleanAB}" ]] && {
|
||||
rm -f "${appBak}" 2>/dev/null; rm -f "${xpuiBak}" 2>/dev/null
|
||||
cp "${xpuiSpa}" "${xpuiBak}"; cp "${appBinary}" "${appBak}"
|
||||
backup_spotx || {
|
||||
echo -e "\n${red}Error:${clr} Failed to create client backup. Exiting...\n" >&2
|
||||
exit 1
|
||||
}
|
||||
printf "\xE2\x9C\x94\x20\x43\x72\x65\x61\x74\x65\x64\x20\x62\x61\x63\x6B\x75\x70\n"
|
||||
return
|
||||
}
|
||||
[[ (-f "${appBak}" || -f "${xpuiBak}") && "${forceSpotx}" ]] && {
|
||||
[[ -f "${appBak}" ]] && { rm -f "${appBinary}"; cp "${appBak}" "${appBinary}"; }
|
||||
[[ -f "${xpuiBak}" ]] && { rm -f "${xpuiSpa}"; cp "${xpuiBak}" "${xpuiSpa}"; }
|
||||
{ [[ ! -f "${appBak}" ]] || atomic_copy "${appBak}" "${appBinary}"; } &&
|
||||
{ [[ ! -f "${xpuiBak}" ]] || atomic_copy "${xpuiBak}" "${xpuiSpa}"; } || {
|
||||
echo -e "\n${red}Error:${clr} Failed to restore client backup. Exiting...\n" >&2
|
||||
exit 1
|
||||
}
|
||||
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
|
||||
}
|
||||
[[ (-f "${appBak}" || -f "${xpuiBak}") && -z "${forceSpotx+x}" ]] && {
|
||||
rm -rf "${xpuiDir}" 2>/dev/null
|
||||
xpuiSkip='true'
|
||||
printf "\xE2\x9C\x94\x20\x44\x65\x74\x65\x63\x74\x65\x64\x20\x62\x61\x63\x6B\x75\x70\n"
|
||||
echo -e "\n${yellow}Warning:${clr} SpotX-Bash has already been installed." >&2
|
||||
echo -e "Use the '-f' flag to force SpotX-Bash to run.\n" >&2
|
||||
return
|
||||
}
|
||||
cp "${xpuiSpa}" "${xpuiBak}"
|
||||
cp "${appBinary}" "${appBak}"
|
||||
backup_spotx || {
|
||||
echo -e "\n${red}Error:${clr} Failed to create client backup. Exiting...\n" >&2
|
||||
exit 1
|
||||
}
|
||||
printf "\xE2\x9C\x94\x20\x43\x72\x65\x61\x74\x65\x64\x20\x62\x61\x63\x6B\x75\x70\n"
|
||||
}
|
||||
|
||||
@@ -788,7 +994,9 @@ snapshot_check() {
|
||||
}
|
||||
|
||||
xpui_open() {
|
||||
rm -rf "${xpuiDir}" 2>/dev/null
|
||||
mkdir -p "${xpuiDir}"
|
||||
xpuiTempCreated='true'
|
||||
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
|
||||
@@ -826,6 +1034,7 @@ xpui_open() {
|
||||
run_core_start() {
|
||||
final_setup_check
|
||||
check_write_permission "${appPath}" "${appBinary}" "${xpuiPath}" "${xpuiSpa}"
|
||||
[[ "${platformType}" == "Linux" && "${stagedInstall}" ]] && protected_stage_prepare
|
||||
xpui_detect
|
||||
[[ "${xpuiSkip}" ]] && { printf "\xE2\x9C\x94\x20\x46\x69\x6E\x69\x73\x68\x65\x64\n\n"; exit 0; }
|
||||
xpui_open
|
||||
@@ -882,14 +1091,38 @@ run_patches() {
|
||||
}
|
||||
|
||||
run_finish() {
|
||||
local spaTemp
|
||||
echo -e "\n//# SpotX was here" >> "${xpuiJs}"
|
||||
rm -f "${xpuiSpa}"
|
||||
(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
|
||||
spaTempDir=$(mktemp -d "${xpuiPath}/.spotx-spa.XXXXXXXX") || {
|
||||
uninstall_spotx && \
|
||||
echo -e "\n${red}Error:${clr} Failed to create temporary SPA directory. Original client restored.\n" >&2 || \
|
||||
echo -e "\n${red}Error:${clr} Failed to create temporary SPA directory or restore client. Backups were preserved.\n" >&2
|
||||
exit 1
|
||||
}
|
||||
spaTemp="${spaTempDir}/xpui.spa"
|
||||
(cd "${xpuiDir}" && zip -qq -r "${spaTemp}" .) &&
|
||||
unzip -tqq "${spaTemp}" &&
|
||||
mv -f "${spaTemp}" "${xpuiSpa}" || {
|
||||
rm -rf "${spaTempDir}" 2>/dev/null
|
||||
uninstall_spotx && {
|
||||
echo -e "\n${red}Error:${clr} Failed to repackage client." >&2
|
||||
echo -e "Original client restored.\n" >&2
|
||||
} || {
|
||||
echo -e "\n${red}Error:${clr} Failed to repackage or restore client." >&2
|
||||
echo -e "Backups were preserved.\n" >&2
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
rm -rf "${spaTempDir}" 2>/dev/null
|
||||
[[ ! -d "${spaTempDir}" ]] && unset spaTempDir
|
||||
rm -rf "${xpuiDir}"
|
||||
unset xpuiTempCreated
|
||||
[[ "${platformType}" == "Linux" && "${stagedInstall}" ]] && protected_commit || {
|
||||
[[ "${platformType}" == "Linux" && "${stagedInstall}" ]] && {
|
||||
echo -e "\n${red}Error:${clr} Failed to install patched client. Original files restored.\n" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
[[ "${platformType}" == "macOS" ]] && {
|
||||
[[ "${skipCodesign}" ]] && /usr/bin/xattr -cr "${appPath}" 2>/dev/null || {
|
||||
/usr/bin/xattr -cr "${appPath}" 2>/dev/null
|
||||
@@ -1225,6 +1458,9 @@ premiumExpEx=(
|
||||
'enableYourSoundCapsuleModal&showing a modal on desktop to users who have clicked on a Your Sound Capsule share link",default:\K!1&true&s&xpuiJs&1.2.38.720'
|
||||
)
|
||||
|
||||
trap cleanup_temp_dirs EXIT
|
||||
trap 'exit 130' HUP INT TERM
|
||||
|
||||
run_prepare
|
||||
run_uninstall_check
|
||||
run_interactive_check
|
||||
|
||||
Reference in New Issue
Block a user