Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: JDownloader #745

Draft
wants to merge 9 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.dev.lock
.idea/**
4 changes: 2 additions & 2 deletions scripts/box
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ function _adduser() {
echo "D /var/run/${user} 0750 ${user} ${user} -" >> /etc/tmpfiles.d/"${user}".conf
systemd-tmpfiles /etc/tmpfiles.d/"${user}".conf --create >> $log 2>&1

for app in rtorrent rutorrent deluge autodl flood nzbget lounge transmission wireguard organizr mango qbittorrent; do
for app in rtorrent rutorrent deluge autodl flood nzbget lounge transmission wireguard organizr mango qbittorrent jdownloader; do
if [[ $app = "rutorrent" ]]; then
if [[ -d /srv/rutorrent ]]; then
if [[ ! -f /install/.rutorrent.lock ]]; then touch /install/.rutorrent.lock; fi
Expand Down Expand Up @@ -369,7 +369,7 @@ function _deluser() {
echo_warn "This will delete the user '${user}' and all associated configs and subdirectories.\nTHIS ACTION CANNOT BE UNDONE.\nRemoval will start as soon as you confirm this message."
ask "Are you sure you want to continue?" N || exit 1

for app in rtorrent rutorrent deluge autodl flood nzbget lounge transmission mango wireguard organizr qbittorrent; do
for app in rtorrent rutorrent deluge autodl flood nzbget lounge transmission mango wireguard organizr qbittorrent jdownloader; do
if [[ $app = "rutorrent" ]]; then
if [[ -d /srv/rutorrent ]]; then
if [[ ! -f /install/.rutorrent.lock ]]; then touch /install/.rutorrent.lock; fi
Expand Down
223 changes: 223 additions & 0 deletions scripts/install/jdownloader.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
#!/bin/bash
# JDownloader Installer for swizzin
# Author: Aethaeran

##########################################################################
# References
##########################################################################

# Liara already made a doc for installing JDownloader manually:
# https://docs.swizzin.net/guides/jdownloader/
# swizzin docs
# https://swizzin.ltd/dev/structure/
# Swizzin environment variables
# https://swizzin.ltd/guides/advanced-setup/
# JDownloader docs
# https://support.jdownloader.org/Knowledgebase/Article/View/install-jdownloader-on-nas-and-embedded-devices
# https://support.jdownloader.org/Knowledgebase/Article/View/headless-systemd-autostart-script
# https://board.jdownloader.org/showthread.php?t=81420
# Some of the logic used here
# https://www.digitalocean.com/community/tutorials/how-to-install-java-with-apt-on-debian-10
# https://linuxize.com/post/how-to-check-if-string-contains-substring-in-bash/
# https://linuxize.com/post/bash-check-if-file-exists/
# https://superuser.com/questions/402979/kill-program-after-it-outputs-a-given-line-from-a-shell-script
# https://board.jdownloader.org/showthread.php?t=81433

##########################################################################
# Import Sources
##########################################################################

. /etc/swizzin/sources/functions/java
. /etc/swizzin/sources/functions/jdownloader
. /etc/swizzin/sources/functions/utils

##########################################################################
# Functions
##########################################################################

function _install() {

echo_info "Setting up JDownloader for $user"
JD_HOME="/home/$user/jd2"
mkdir -p "$JD_HOME"

# TODO: Test environment variable 'MYJD_BYPASS' to bypass the following block. For unattended installs.
if [[ ! -z $MYJD_BYPASS ]]; then
if ask "Do you want to inject MyJDownloader details for $user?" N; then
inject="true"
echo_info "Injecting MyJDownloader details for $user"
inject_myjdownloader # Get account info for this user. and insert it into this installation
else
inject="false"
fi
else
echo_info "Bypassing MyJDownloader detail entering because of unattended variable."
fi

echo_progress_start "Downloading JDownloader.jar..."
while [[ ! -e "/tmp/JDownloader.jar" ]]; do
if wget -q http://installer.jdownloader.org/JDownloader.jar -O "/tmp/JDownloader.jar"; then
echo_info "Jar downloaded..."
if [ ! jar tvf "/tmp/JDownloader.jar" ] 2>/dev/null; then # Java will detect if a .jar is corrupt
echo_info ".jar is corrupt. Removing, and trying again."
rm "/tmp/JDownloader.jar"
else
echo_info ".jar is valid."
fi
else
echo_error "Failed to download. Removing, and trying again."
if [[ -e "/tmp/JDownloader.jar" ]]; then
rm "/tmp/JDownloader.jar"
fi
fi
done

echo_progress_done "JDownloader.jar downloaded."

if [[ ! -e "$JD_HOME/JDownloader.jar" ]]; then
cp "/tmp/JDownloader.jar" "$JD_HOME/JDownloader.jar"
fi

# TODO: Currently, we need something here to disable all currently running JDownloader installations, or the MyJD verification logic will cause a loop. Would rather we didn't.
for each_user in "${users[@]}"; do # disable all instances
systemctl disable --now "jdownloader@$each_user" --quiet
done

# TODO: Figure out if this would be a WHOLE lot simpler if I didn't run the `-norestart` flag *facepalm*
command="java -jar $JD_HOME/JDownloader.jar -norestart"

echo_progress_start "Attempting JDownloader2 initialisation"
end_initialisation_loop="false"
while [[ $end_initialisation_loop == "false" ]]; do # Run command until a certain file is created.
echo_info "Running temporary JDownloader process..." # TODO: This should be echo_log_only at PR end.
if [[ -e "$tmp_log" ]]; then # Remove the tmp log if exists
rm "$tmp_log"
fi
if [[ -e "$JD_HOME"/logs ]]; then
tmp_log="$(get_most_recent_dir "$JD_HOME"/logs)/Log.L.log.0"
$command >"$tmp_log" 2>&1 &
else
$command >"/dev/null" 2>&1 &
fi
kill_process="false"
pid=$!
#shellcheck disable=SC2064
trap "kill $pid 2> /dev/null" EXIT # Set trap to kill background process if this script ends.
process_died="false"
while [[ $process_died == "false" ]]; do # While background command is still running...

echo_info "Background command is still running..." # TODO: This should be removed at PR end.
sleep 1 # Pace this out a bit, no need to check what JDownloader is doing more frequently than this.
# If any of specified strings are found in the log, kill the last called background command.
if [[ -e "$tmp_log" ]]; then

if grep -q "Create ExitThread" -F "$tmp_log"; then # JDownloader exited gracefully on it's own. Usually this will only happen first run.
echo_info "JDownloader exited gracefully." # TODO: This should be echo_log_only at PR end.
fi

if grep -q "Initialisation finished" -F "$tmp_log"; then #
echo_info "JDownloader started successfully." # TODO: This should be echo_log_only at PR end.

# Pretty sure this will remove the long pause.
keep_sleeping="true"
while [[ ! $keep_sleeping == "false" ]]; do
if grep -q "No Console Available" -F "$tmp_log" || grep -q "Start HTTP Server" -F "$tmp_log"; then
keep_sleeping="false"
else
sleep 1 # Wait until JDownloader has attempted launching the HTTP server.
fi
done

if grep -q "No Console Available" -F "$tmp_log"; then
echo_warn "MyJDownloader account details were incorrect. They won't be able to use the web UI."
if [[ $inject == "true" ]]; then
echo_info "Please enter the MyJDownloader details again."
inject_myjdownloader # Get account info for this user. and insert it into this installation
else
end_initialisation_loop="true"
fi
kill_process="true"
fi

# This only works for verification if it is the first JDownloader instance to attempt connecting to MyJDownloader. I assume other instances use the same HTTP server.

if grep -q "Start HTTP Server" -F "$tmp_log"; then
echo_info "MyJDownloader account details verified."
kill_process="true"
end_initialisation_loop="true"
fi

fi
fi

if kill -0 $pid 2>/dev/null; then
if [[ $kill_process == "true" ]]; then
echo_info "Kill JDownloader." # TODO: This should be echo_log_only at PR end.
kill $pid # Kill the background command
sleep 1 # Give it a second to actually die.
process_died="true"
fi
else
echo_info "Background command died without being killed." # TODO: This should be echo_log_only at PR end.
process_died="true"
fi

done
trap - EXIT # Disable the trap on a normal exit.
done
echo_progress_done "Initialisation concluded"

chown -R "$user": "$JD_HOME" # Set owner on JDownloader folder.
chmod 700 -R "$JD_HOME" # Set permissions on JDownloader folder.

}

_systemd() {
# JD_HOME should be setting the working directory for this instance of JDownloader.
# JDownloader will automatically create a pid file when running. That way, systemd can use it to ensure it is disabling the correct process.
cat >/etc/systemd/system/[email protected] <<EOF
[Unit]
Description=JDownloader Service
After=network.target

[Service]
User=%i
Group=%i
Environment=JD_HOME=/home/%i/jd2
ExecStart=/usr/bin/java -Djava.awt.headless=true -jar /home/%i/jd2/JDownloader.jar
PIDFile=/home/%i/jdownloader/JDownloader.pid

[Install]
WantedBy=multi-user.target
EOF
}

##########################################################################
# Script Main
##########################################################################

if [[ -n "$1" ]]; then # Install JDownloader for JUST the user that was passed to script as argument (i.e. box adduser $user)
user="$1"
_install
exit 0
fi

install_java8 # Install Java as it is a dependency.

readarray -t users < <(_get_user_list) # Install a separate JDownloader instance for each user, as it cannot be multi-seated.
for user in "${users[@]}"; do
_install
done

_systemd # Insert service file to /etc/systemd/system

# TODO: Remove this block once verification method is changed.
# Don't start services until after each user is installed. Due to current verification process.
for user in "${users[@]}"; do # Enable a separate service for each swizzin user
echo_progress_start "Enabling service jdownloader@$user"
systemctl enable --quiet --now jdownloader@"$user"
echo_progress_done
done

touch /install/.jdownloader.lock # Create lock file so that swizzin knows JDownloader is installed.
echo_success "JDownloader installed" # Winner winner. Chicken dinner.
51 changes: 51 additions & 0 deletions scripts/remove/jdownloader.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/bash
# JDownloader remover for swizzin
# Author: Aethaeran

# Import functions
. /etc/swizzin/sources/functions/utils

# this will check for all swizzin users, and iterate and remove all JDownloader installations.
users=("$(_get_user_list)")

# TODO: Add bypass purging altogether with variable
# TODO: Move purging functionality to a function outside this script

# Do a check to see if they want to purge JDownloader configurations.
if ask "Do you want to purge ANY JDownloader configurations?" N; then
purge_some="true" # If yes
if ask "Do you want to purge ALL JDownloader configurations?" N; then
purge_all="true" # If yes
else
purge_all="false" # If no
fi
else
purge_some="false" # If no
echo_info "Each user's JDownloader configuration can be found in their home folder under the sub-directory 'jd2'"
fi

# The following line cannot follow SC2068 because it will cause the list to become a string.
# shellcheck disable=SC2068
for user in ${users[@]}; do
echo_progress_start "Removing JDownloader for $user..."
systemctl disable -q --now jdownloader@"$user"
JD_HOME="/home/$user/jd2"
if [[ $purge_all == "true" ]]; then
rm_if_exists $JD_HOME
else
if [[ $purge_some == "true" ]]; then
if ask "Do you want to purge $user's JDownloader configuration?" N; then
rm_if_exists $JD_HOME # If yes
else
echo_info "Not purging JDownloader configuration for $user\n their JDownloader configuration can be found at $JD_HOME"
fi
fi
fi
echo_progress_done
done
echo_progress_start "Removing shared JDownloader files..."
rm_if_exists -r /etc/systemd/system/[email protected]
rm_if_exists /install/.jdownloader.lock
echo_progress_done
echo_success "JDownloader removed"
echo_info "If JDownloader was the only thing using Java, and Java was installed with JDownloader. Then you can uninstall default-jre with 'apt remove default-jre -y'. It's not done by default because you may have other services using it."
20 changes: 20 additions & 0 deletions scripts/tests/jdownloader.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash
# JDownloader Installer for swizzin
# Author: Aethaeran

#shellcheck source=sources/functions/tests
. /etc/swizzin/sources/functions/tests

app_name="jdownloader"
pretty_name="JDownloader"

# TODO: Check if services are running. on each instance

readarray -t users < <(_get_user_list) # Install a separate JDownloader instance for each user, as it cannot be multi-seated.
for user in "${users[@]}"; do
check_service "$app_name@$user"
done

# TODO: Check if http server is up
# TODO: Check if myjdownloader.org is up
# TODO: Check if MyJDownloader details are verified on each instance
44 changes: 44 additions & 0 deletions sources/functions/jdownloader
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/bin/bash
# JDownloader Functions for swizzin
# Author: Aethaeran

function inject_myjdownloader() {

# TODO: Make this only pass env file details to master user

echo_info "An account from https://my.jdownloader.org/ is required in order to access the web UI.\nUse a randomly generated password at registration as the password is stored in plain text"
if [[ -z "${MYJD_EMAIL}" ]]; then
echo_query "Please enter the e-mail used to access this account once created:"
read -r 'MYJD_EMAIL'
else
echo_info "Using email = $MYJD_EMAIL"
fi

if [[ -z "${MYJD_PASSWORD}" ]]; then
echo_query "Please enter the password for the account"
read -r 'MYJD_PASSWORD'
else
echo_info "Using password = $MYJD_PASSWORD"
fi

if [[ -z "${MYJD_DEVICENAME}" ]]; then
echo_query "Please enter the desired device name"
read -r 'MYJD_DEVICENAME'
else
echo_info "Using device name = $MYJD_DEVICENAME"
fi

mkdir -p "$JD_HOME/cfg"

if [[ -e "$JD_HOME/cfg/org.jdownloader.api.myjdownloader.MyJDownloaderSettings.json" ]]; then
rm "$JD_HOME/cfg/org.jdownloader.api.myjdownloader.MyJDownloaderSettings.json"
fi

cat > "$JD_HOME/cfg/org.jdownloader.api.myjdownloader.MyJDownloaderSettings.json" << EOF
{
"email" : "$MYJD_EMAIL",
"password" : "$MYJD_PASSWORD",
"devicename" : "$MYJD_DEVICENAME"
}
EOF
}
8 changes: 8 additions & 0 deletions sources/functions/utils
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,11 @@ function check_swap_off() {
_get_installed_apps() {
find /install -type f -name .\*.lock | awk -F. '{print $2}' | sort
}

# Returns the most recently modified directory from the directory indicated in the first argument
# Found at:
# https://unix.stackexchange.com/questions/136976/get-the-latest-directory-not-the-latest-file
get_most_recent_dir() {
latest_directory_inode=$(find "$1" -mindepth 1 -maxdepth 1 -type d -printf '%Ts %i\n' | sort -nr | sed -n '1 s/.* //p')
find "$1" -maxdepth 1 -inum "$latest_directory_inode"
}