lampshade29

Trash Guides Arr Stack

Jun 22nd, 2025
27
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 50.30 KB | Software | 0 0
  1. #!/usr/bin/env bash
  2.  
  3. # shellcheck disable=SC2034 # Unused variables left for readability
  4.  
  5. # MIT License
  6.  
  7. # Permission is hereby granted, free of charge, to any person obtaining a copy
  8. # of this software and associated documentation files (the "Software"), to deal
  9. # in the Software without restriction, including without limitation the rights
  10. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. # copies of the Software, and to permit persons to whom the Software is
  12. # furnished to do so, subject to the following conditions:
  13.  
  14. # The above copyright notice and this permission notice shall be included in all
  15. # copies or substantial portions of the Software.
  16.  
  17. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  23. # SOFTWARE.
  24.  
  25. # Author - Ideas and original and code by @bokkoman.
  26. # Contributors - Big thanks to @userdocs for helping with the script.
  27. # Testers - Thanks @Davo1624 for testing initial script. Xpenology for providing VM images.
  28. # Credits - https://trash-guides.info https://github.com/TRaSH-/Guides-Synology-Templates
  29.  
  30. ## This script is adapted for generic Linux NAS systems like Ugreen 4800, supporting Docker.
  31.  
  32. #################################################################################################################################################
  33. # Color me up Scotty - define some color values to use as variables in the scripts - https://robotmoon.com/256-colors/
  34. #################################################################################################################################################
  35. cr="\e[31m" clr="\e[91m" # [c]olor[r]ed [c]olor[l]ight[r]ed
  36. cg="\e[32m" clg="\e[92m" # [c]olor[g]reen [c]olor[l]ight[g]reen
  37. cy="\e[33m" cly="\e[93m" # [c]olor[y]ellow [c]olor[l]ight[y]ellow
  38. cb="\e[34m" clb="\e[94m" # [c]olor[b]lue [c]olor[l]ight[b]lue
  39. cm="\e[35m" clm="\e[38;5;212m" # [c]olor[m]agenta [c]olor[l]ight[m]agenta
  40. cc="\e[36m" clc="\e[38;5;81m" # [c]olor[c]yan [c]olor[l]ight[c]yan
  41.  
  42. tb="\e[1m" td="\e[2m" tu="\e[4m" tn="\n" tbk="\e[5m" # [t]ext[b]old [t]ext[d]im [t]ext[u]nderlined [t]ext[n]ewline [t]ext[b]lin[k]
  43.  
  44. utick="\e[32m\U2714\e[0m" uplus="\e[36m\U002b\e[0m" ucross="\e[31m\U00D7\e[0m" # [u]nicode][tick] [u]nicode][plus] [u]nicode][cross]
  45.  
  46. urc="\e[31m\U25cf\e[0m" ulrc="\e[38;5;9m\U25cf\e[0m" # [u]nicode[r]ed[c]ircle [u]nicode[l]ight[r]ed[c]ircle
  47. ugc="\e[32m\U25cf\e[0m" ulgc="\e[92m\U25cf\e[0m" # [u]nicode[g]reen[c]ircle [u]nicode[l]ight[g]reen[c]ircle
  48. uyc="\e[33m\U25cf\e[0m" ulyc="\e[93m\U25cf\e[0m" # [u]nicode[y]ellow[c]ircle [u]nicode[l]ight[y]ellow[c]circle
  49. ubc="\e[34m\U25cf\e[0m" ulbc="\e[94m\U25cf\e[0m" # [u]nicode[b]lue[c]circle [u]nicode[l]ight[b]lue[c]circle
  50. umc="\e[35m\U25cf\e[0m" ulmc="\e[38;5;135m\U25cf\e[0m" # [u]nicode[m]agenta[c]ircle [u]nicode[l]ight[m]agenta[c]ircle
  51. ucc="\e[36m\U25cf\e[0m" ulcc="\e[96m\U25cf\e[0m" # [u]nicode[c]yan[c]circle [u]nicode[l]ight[c]yan[c]circle
  52. ugrc="\e[37m\U25cf\e[0m" ulgrcc="\e[97m\U25cf\e[0m" # [u]nicode[gr]ey[c]ircle [u]nicode[l]ight[gr]ey[c]ircle
  53.  
  54. cdef="\e[39m" # [c]olor[def]ault
  55. bkend="\e[0m"
  56. cend="\e[0m" # [c]olor[end]
  57. #################################################################################################################################################
  58. # Banner
  59. #################################################################################################################################################
  60. printf '\n%b\n' '\e[38;2;64;81;181m ⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠿⠿⠿⠿⠿⠿⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
  61. ⣿⣿⠿⠛⠋⠉⢀⣀⣀⣀⣠⣤⣤⣤⣤⣤⣀⣀⣀⠈⠉⠛⠻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
  62. ⣿⠁⣠⣴⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣤⡀⢹⣿⣿⠛⠛⠛⠛⠛⠛⠛⠛⠛⣿⠛⠛⠛⠛⠛⠛⠛⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠛⠛⠛⠛⠻⢿⣿⣿⠛⠛⠛⣿⣿⣿⠛⠛⠛⣿⣿⣿⣿
  63. ⣿⠘⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⢸⣿⣿⣀⣀⣀⠀⠀⠀⣀⣀⣀⣿⠀⠀⠀⣤⣤⡄⠀⠀⢹⣿⣿⡿⠿⠿⠿⢿⣿⣿⡏⠀⠀⢠⣦⡄⠀⠀⢻⣿⠀⠀⠀⣿⣿⣿⠀⠀⠀⣿⣿⣿⣿
  64. ⣿⠀⡀⠀⠉⠙⠛⠛⠿⠿⠿⠿⠿⠿⠿⠿⠿⠛⠛⠛⠉⠁⢀⡀⢸⣿⣿⣿⣿⣿⠀⠀⠀⣿⣿⣿⣿⠀⠀⠀⠛⠛⠁⠀⢀⣼⣿⡁⠀⢀⣤⡀⠀⠘⣿⣷⡀⠀⠀⠀⠉⠙⠻⣿⣿⠀⠀⠀⠉⠉⠉⠀⠀⠀⣿⣿⣿⣿
  65. ⣿⡆⣿⣿⣶⣤⣤⣀⣀⣀⣀⠀⠀⠀⢀⣀⣀⣀⣠⣤⣴⣾⣿⠇⣾⣿⣿⣿⣿⣿⠀⠀⠀⣿⣿⣿⣿⠀⠀⠀⣦⡀⠀⠀⠻⣿⣿⠛⠋⠉⣁⡀⠀⠀⣿⠿⠿⠓⣶⣤⣄⠀⠀⢸⣿⠀⠀⠀⣶⣶⣶⠀⠀⠀⣿⣿⣿⣿
  66. ⣿⡇⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⣿⣿⣿⣿⣿⣿⠀⠀⠀⣿⣿⣿⣿⠀⠀⠀⣿⣿⡄⠀⠀⠹⣇⠀⠀⠘⠛⠁⠀⠀⣿⣧⠀⠀⠈⠛⠁⠀⢀⣼⣿⠀⠀⠀⣿⣿⣿⠀⠀⠀⣿⣿⣿⣿
  67. ⣿⣿⠸⣿⣿⣿⣿⣿⣿⣿⠏⠉⠉⠋⠙⣿⣿⣿⣿⣿⣿⣿⡟⢸⣿⣿⣿⣿⣿⣿⣶⣶⣶⣿⣿⣿⣿⣶⣶⣶⣿⣿⣿⣶⣶⣶⣿⣷⣶⣶⣶⣿⣶⣶⣾⣿⣿⣶⣶⣶⣶⣶⣿⣿⣿⣶⣶⣶⣿⣿⣿⣶⣶⣶⣿⣿⣿⣿
  68. ⣿⣿⠀⣿⣿⣿⣿⣿⣿⣏⠀⢠⣿⡄⠀⢈⣾⣿⣿⣿⣿⣿⡇⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
  69. ⣿⣿⡇⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣶⣿⣿⣿⣿⣿⣿⣿⠃⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
  70. ⣿⣿⣇⢸⣿⣿⣷⠂⠀⢘⢿⣿⣿⣿⣿⣿⡁⠀⢻⣿⣿⣿⢀⣿⣿⣿⣿⠋⠁⠀⠀⠀⠀⠀⠉⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⢸⣿⣿⣿⣿⣿⣿⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
  71. ⣿⣿⣿⠈⣿⣿⣧⠀⣠⣿⣿⣿⣿⣿⠋⠿⠷⠀⢀⣿⣿⡏⢸⣿⣿⣿⠁⠀⠀⣰⣾⣿⣦⣤⣤⣼⣿⡏⠉⠉⢹⣿⡏⠉⠉⣿⣿⠉⠉⢹⣿⡿⠛⠉⠉⠛⠀⠀⠀⣿⣿⠿⠛⠉⠉⠉⠛⢿⣿⣿⠟⠋⠉⠉⠉⠛⢿⣿
  72. ⣿⣿⣿⡄⣿⣿⣿⣆⠀⠀⠀⢸⣿⣯⡀⠀⠀⣠⣾⣿⣿⡇⣼⣿⣿⣿⠀⠀⠀⣿⣿⠉⠉⠉⠉⠉⣿⡇⠀⠀⢸⣿⡇⠀⠀⣿⣿⠀⠀⢸⣿⠁⠀⠀⣤⣤⠀⠀⠀⣿⡏⠀⠀⣴⣶⡆⠀⠀⢹⣇⠀⠀⠚⠷⠤⢴⣾⣿
  73. ⣿⣿⣿⡇⠸⢿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣿⣿⣿⣿⣿⠿⠀⣿⣿⣿⣿⡀⠀⠀⠹⣿⣶⡶⠀⠀⠀⣿⡇⠀⠀⢸⣿⠃⠀⠀⣿⣿⠀⠀⢸⣿⠀⠀⠀⣿⣿⠀⠀⠀⣿⡄⠀⠀⢤⣤⣤⣤⣤⣼⣿⣦⣤⣄⣀⠀⠀⠘⣿
  74. ⣿⣿⣿⣷⡀⠀⠈⠉⠛⠛⠛⠛⠛⠛⠛⠛⠛⠉⠉⠀⠀⣰⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⢀⣤⣿⣧⡀⠀⠀⠀⡀⠀⠀⣿⣿⠀⠀⢸⣿⣧⡀⠀⠀⠀⡀⠀⠀⣿⣷⣀⠀⠈⠉⠁⠀⣀⣾⣧⡀⠀⠈⠉⠁⠀⣰⣿
  75. ⣿⣿⣿⣿⣿⣶⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣠⣤⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣶⣾⣿⣿⣿⣿⣿⣿⣶⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣶⣿⣿⣿⣿⣿⣿⣿⣿⣶⣶⣶⣿⣿⣿⣿⣿⣷⣶⣶⣾⣿⣿⣿
  76. ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\e[0m'
  77. sleep 2
  78.  
  79. #################################################################################################################################################
  80. # Disclaimer
  81. #################################################################################################################################################
  82.  
  83. # Display disclaimer
  84. printf '\n%s\n' "DISCLAIMER: Use this script at your own risk."
  85. printf '\n%s\n' "This script is provided as-is without any warranty or guarantee of any kind. The author behind the script shall not be held liable for any damages or losses arising from its use."
  86. printf '\n%s\n' "Please note that the script may perform certain actions that can modify your system or data. It is recommended to review the script and understand its functionality before executing it."
  87. printf '\n%s\n' "By proceeding with the execution of this script, you acknowledge that you have read this disclaimer and agree to take full responsibility for any consequences that may arise from using the script."
  88. printf '\n%s\n' "If you are unsure about the script or its implications, it is recommended to seek assistance from a qualified professional before proceeding."
  89. printf '\n%s\n' "This script is an adaptation of the Synology guide for a generic Linux NAS. Refer to the original guide for detailed application setup at:"
  90. printf '%s\n' "https://trash-guides.info/Hardlinks/How-to-setup-for/Synology"
  91. printf '\n'
  92.  
  93. # Prompt for confirmation
  94. read -erp "Do you want to continue? [y]es/[n]o: " -n 1 confirm
  95. echo
  96.  
  97. # Check user's confirmation
  98. if [[ "${confirm}" != [Yy] ]]; then
  99. printf '%s\n' "Script execution canceled by the user."
  100. exit 0
  101. fi
  102.  
  103. #################################################################################################################################################
  104. # check for root access and exit if the user does not have the required privileges.
  105. #################################################################################################################################################
  106. if [[ "$(id -un)" != 'root' ]]; then
  107. printf "\n%b\n" " ${ulrc} Please run this script with sudo to proceed"
  108. printf "\n%b\n\n" " ${utick} sudo ./$(basename -- "$0")"
  109. exit 1
  110. fi
  111. #################################################################################################################################################
  112. # Hide thing or show them
  113. #################################################################################################################################################
  114. if [[ "${1}" == 'show' ]]; then
  115. hide_all='' hide_output='' hide_error=''
  116. else
  117. hide_all='&> /dev/null' hide_output='1> /dev/null' hide_error='2> /dev/null'
  118. fi
  119. #################################################################################################################################################
  120. # trap to catch errors
  121. #################################################################################################################################################
  122. err_report() {
  123. printf '\n%b\n\n' " ${ucross} Error on line $1 - script exited to allow for debugging"
  124. exit 1
  125. }
  126.  
  127. trap 'err_report $LINENO' ERR
  128.  
  129. #################################################################################################################################################
  130. # Detect Package Manager and Install Function
  131. #################################################################################################################################################
  132.  
  133. PKG_MANAGER=""
  134.  
  135. if command -v apt-get &> /dev/null; then
  136. PKG_MANAGER="apt"
  137. elif command -v dnf &> /dev/null; then
  138. PKG_MANAGER="dnf"
  139. # Add more package managers (e.g., pacman, zypper) if needed for common NAS distros
  140. fi
  141.  
  142. install_package() {
  143. local package_name="$1"
  144. printf '\n%b\n\n' " ${uplus} Installing ${package_name} using ${PKG_MANAGER}"
  145. case "$PKG_MANAGER" in
  146. "apt")
  147. eval apt-get update "${hide_all}"
  148. eval apt-get install -y "${package_name}" "${hide_all}"
  149. ;;
  150. "dnf")
  151. eval dnf install -y "${package_name}" "${hide_all}"
  152. ;;
  153. *)
  154. printf "\n%b\n\n" " ${ucross} Unsupported package manager: ${PKG_MANAGER}. Please install ${package_name} manually."
  155. exit 1
  156. ;;
  157. esac
  158.  
  159. if [ $? -eq 0 ]; then
  160. printf '\n%b\n' " ${utick} ${package_name} installed successfully."
  161. else
  162. printf '\n%b\n' " ${ucross} Failed to install ${package_name}. Please check your internet connection or package manager configuration."
  163. exit 1
  164. fi
  165. }
  166.  
  167. #################################################################################################################################################
  168. # Multi select function
  169. #################################################################################################################################################
  170. function _multiselect {
  171. printf '\n'
  172.  
  173. # Create an array of all container names https://docs.docker.com/engine/reference/commandline/ps/#formatting
  174. # Ensure Docker is running to get container names
  175. if ! docker ps &> /dev/null; then
  176. printf '\n%b\n' " ${ulrc} Docker daemon is not running or not accessible. Cannot detect installed containers."
  177. printf '\n%b\n' " ${ulrc} Please ensure Docker is installed and running before selecting applications."
  178. # Attempt to start docker if it's not running but installed
  179. if command -v docker &> /dev/null && systemctl is-system-running &> /dev/null; then
  180. printf '\n%b\n' " ${ulmc} Attempting to start Docker service..."
  181. systemctl start docker &> /dev/null
  182. sleep 3 # Give Docker some time to start
  183. if ! docker ps &> /dev/null; then
  184. printf '\n%b\n' " ${ucross} Failed to start Docker. Proceeding without installed container detection."
  185. mapfile -t installed_containers < <(echo "") # Empty array
  186. else
  187. printf '\n%b\n' " ${utick} Docker started. Detecting installed containers."
  188. mapfile -t installed_containers < <(docker ps --format "{{.Names}}")
  189. fi
  190. else
  191. mapfile -t installed_containers < <(echo "") # Empty array if Docker isn't installed/running
  192. fi
  193. else
  194. mapfile -t installed_containers < <(docker ps --format "{{.Names}}")
  195. fi
  196.  
  197.  
  198. # Create our menu my_options associative array
  199. declare -A my_options
  200. # Fetch templates from the TRaSH-Guides GitHub repository
  201. eval "$(curl -sL "https://raw.githubusercontent.com/TRaSH-/Guides-Synology-Templates/main/templates/template-file-list.json" | jq -r '.templates | to_entries[]|@sh"my_options[\(.value)]=false"')"
  202.  
  203. # Create some local arrays we need and make sure they're set to empty when the function is used/looped in the script
  204. local selected_values=()
  205. local selected_apps=()
  206. local lastrow
  207. local startrow
  208. # Create this global array we need fall through the function and make sure it is set to empty when the function is used/looped in the script
  209. selected_options=()
  210.  
  211. # check the installed_containers array to see what is already installed, matched against our available_templates array so as not to add non template containers to the menu
  212. for index in "${installed_containers[@]}"; do
  213. if [[ "${!my_options[*]}" =~ ${index} ]]; then
  214. my_options["${index}"]="true"
  215. fi
  216. done
  217.  
  218. # little helpers for terminal print control and key input
  219. # not really sure what this does or how it works.
  220. cursor_blink_on() { printf "\033[?25h"; }
  221. cursor_blink_off() { printf "\033[?25l"; }
  222. cursor_to() { printf '%b' "\033[${1};${2:-1}H"; }
  223. print_inactive() { printf '%b' "${2} ${1} "; }
  224. print_active() { printf '%b' "${2} \033[7m ${1} \033[27m"; }
  225. get_cursor_row() {
  226. IFS=';' read -rsdR -p $'\E[6n' ROW COL
  227. printf '%b' "${ROW#*[}"
  228. }
  229.  
  230. # This will check the defaults of the my_options and set the menu option to true.
  231. # This eseentially switches back to an indexed array due to how the function toggle_option is working.
  232. for defaults in "${!my_options[@]}"; do
  233. if [[ "${my_options[$defaults]}" == 'true' ]]; then
  234. selected_values+=("true")
  235. else
  236. selected_values+=("false")
  237. fi
  238. printf "\n"
  239. done
  240.  
  241. # determine current screen position for overwriting the my_options
  242. lastrow="$(get_cursor_row)"
  243. startrow="$((lastrow - ${#my_options[@]}))"
  244.  
  245. # ensure cursor and input echoing back on upon a ctrl+c during read -s
  246. trap "cursor_blink_on; stty echo; printf '\n'; exit" 2
  247. cursor_blink_off
  248.  
  249. # not really sure what this does or how it works.
  250. key_input() {
  251. local key
  252. IFS= read -rsn1 key &> /dev/null
  253. case "${key}" in
  254. '')
  255. echo enter
  256. ;;
  257. $'\x20')
  258. echo space
  259. ;;
  260. 'k')
  261. echo up
  262. ;;
  263. 'j')
  264. echo down
  265. ;;
  266. $'\x1b')
  267. read -rsn2 key
  268. case "$key" in
  269. '[A' | 'k')
  270. echo up
  271. ;;&
  272. '[B' | 'j')
  273. echo down
  274. ;;&
  275. esac
  276. ;;
  277. *) ;;
  278. esac
  279. }
  280.  
  281. # Passes the toggled options and a positional parameter number, 0 to 19 for example, and then sets the index matching that number to false or true. selected_values[2]=false, selected_values[16]=false
  282. toggle_option() {
  283. local option="${1}"
  284. if [[ "${selected_values[option]}" == 'true' ]]; then
  285. selected_values["$option"]=false
  286. else
  287. selected_values["$option"]=true
  288. fi
  289. }
  290.  
  291. # not really sure what this does or how it works.
  292. print_options() {
  293. # print options by overwriting the last lines
  294. local idx=0
  295. for option in "${!my_options[@]}"; do
  296. local prefix=" [\e[38;5;9m×\e[0m]"
  297. if [[ ${selected_values[idx]} == true ]]; then
  298. prefix=" [\e[38;5;46m✔\e[0m]"
  299. fi
  300.  
  301. cursor_to $((startrow + idx))
  302. if [[ "${idx}" -eq "${1}" ]]; then
  303. print_active "${option}" "${prefix}"
  304. else
  305. print_inactive "${option}" "${prefix}"
  306. fi
  307. ((idx++))
  308. done
  309. }
  310.  
  311. local active=0
  312. while true; do
  313. print_options "${active}"
  314. # user key control
  315. case $(key_input) in
  316. space)
  317. toggle_option "${active}"
  318. ;;
  319. enter)
  320. print_options -1
  321. break
  322. ;;
  323. up)
  324. ((active--))
  325. if [[ "${active}" -lt 0 ]]; then active=$((${#my_options[@]} - 1)); fi
  326. ;;
  327. down)
  328. ((active++))
  329. if [[ "${active}" -ge "${#my_options[@]}" ]]; then active=0; fi
  330. ;;
  331. esac
  332. done
  333.  
  334. # cursor position back to normal
  335. cursor_to "${lastrow}"
  336. printf "\n"
  337. cursor_blink_on
  338.  
  339. if [[ ! "${selected_values[*]}" =~ 'true' ]]; then
  340. printf '%b\n' " ${ulrc} You must select at least one option to proceed. Try again"
  341. _multiselect
  342. else
  343. for values in "${!my_options[@]}"; do
  344. selected_apps+=("$values")
  345. done
  346.  
  347. for count in "${!selected_apps[@]}"; do
  348. my_options[${selected_apps[$count]}]=${selected_values[$count]}
  349. done
  350.  
  351. for final_selection in "${!my_options[@]}"; do
  352. if [[ "${my_options[$final_selection]}" == 'true' ]]; then
  353. selected_options+=("$final_selection")
  354. fi
  355. done
  356.  
  357. # You can use thee "${selected_options[@]}" array to install apps as it is just the name of the app.
  358. printf '%b\n\n' " ${utick} You have selected:"
  359. printf " ${uyc} %s\n" "${selected_options[@]}"
  360. printf '\n'
  361. fi
  362. }
  363. #################################################################################################################################################
  364. # Volumes, data and docker bootstrap
  365. #################################################################################################################################################
  366. # Create an array of all available volumes on this device
  367. # Using findmnt for a more robust detection of mounted filesystems (ext4, xfs, btrfs, f2fs, zfs)
  368. # Modified to include the root directory '/' as a valid volume option.
  369. mapfile -t volume_list_array < <(findmnt -l -o TARGET -n -t ext4,xfs,btrfs,f2fs,zfs | grep -E "^/mnt/|^/media/|^/data|^/volume|^/$" | sort -V)
  370.  
  371. # If no volumes detected, exit
  372. if [[ "${#volume_list_array[@]}" -eq '0' ]]; then
  373. printf "\n%b\n" " ${ulrc} No suitable data volumes found. Please ensure your storage is mounted."
  374. exit 1
  375. fi
  376.  
  377. [[ "${#volume_list_array[@]}" -eq '1' ]] && docker_install_volume="${volume_list_array[0]}" docker_install_volume_id="$(basename "${volume_list_array[0]}")"
  378.  
  379. # Check if Docker is installed
  380. if command -v docker &> /dev/null; then
  381. printf "\n ${utick} %b\n" "Docker is already installed."
  382. install_docker="no"
  383. # Attempt to determine Docker's data root. Default to /var/lib if not easily found.
  384. docker_info_output=$(docker info --format '{{.DockerRootDir}}' 2>/dev/null)
  385. if [[ -n "$docker_info_output" ]]; then
  386. # Assuming DockerRootDir is like /var/lib/docker, we want /var/lib
  387. docker_install_volume="${docker_info_output%/*}"
  388. else
  389. docker_install_volume="/var/lib" # Default for Docker root on most Linux systems
  390. fi
  391. else
  392. install_docker="yes"
  393. fi
  394.  
  395. # If Docker needs to be installed and multiple volumes exist, ask user for Docker installation volume
  396. if [[ "${install_docker}" == 'yes' && "${#volume_list_array[@]}" -gt '1' ]]; then
  397. PS3=$'\n \e[94m\U25cf\e[m '"Please select where to install Docker's data (images, containers) from the list of volumes: "$'\n\n '
  398. printf "\n%b\n\n" " ${uyc} This is where Docker's persistent data will be stored. It's recommended to choose a fast volume."
  399. select option in "${volume_list_array[@]}"; do
  400. if [[ "$REPLY" -gt "${#volume_list_array[@]}" ]]; then
  401. printf '\n%b\n\n' " ${ucross}This is not a valid volume option, try again"
  402. else
  403. docker_install_volume="$(printf '%s' "${option}")"
  404. read -erp $'\n \e[32m\U2714\e[0m '"You selected "$'\e[96m'"${docker_install_volume}"$'\e[m'" is this correct "$'\e[32m'"[y]es"$'\e[m'" or "$'\e[31m'"[n]o"$'\e[m'" : " -i "y" confirm
  405. if [[ "${confirm}" =~ ^[yY](es)?$ ]]; then
  406. docker_install_volume_id="$(basename "${docker_install_volume}")"
  407. break
  408. fi
  409. fi
  410. done
  411. fi
  412.  
  413. # If multiple volumes, ask user for applications data volume
  414. if [[ "${#volume_list_array[@]}" -gt '1' ]]; then
  415. PS3=$'\n \e[94m\U25cf\e[m '"Please select a volume for your application's data (movies, shows, etc.): "$'\n\n '
  416. printf "\n%b\n\n" " ${uyc} This volume is where the media and other application data files will be stored."
  417. select option in "${volume_list_array[@]}"; do
  418. if [[ "$REPLY" -gt "${#volume_list_array[@]}" ]]; then
  419. printf '\n%b\n\n' " ${ucross}This is not a valid volume option, try again"
  420. else
  421. docker_data_volume="$(printf '%s' "${option}")"
  422. read -erp $'\n \e[32m\U2714\e[0m '"You selected "$'\e[96m'"${docker_data_volume}"$'\e[m'" is this correct "$'\e[32m'"[y]es"$'\e[m'" or "$'\e[31m'"[n]o"$'\e[m'" : " -i "y" confirm
  423. if [[ "${confirm}" =~ ^[yY](es)?$ ]]; then
  424. docker_data_dir_id="$(basename "${docker_data_volume}")"
  425. break
  426. fi
  427. fi
  428. done
  429. else
  430. # If only one volume, use it for both Docker and app data
  431. docker_data_volume="${volume_list_array[0]}"
  432. docker_data_dir_id="$(basename "${volume_list_array[0]}")"
  433. fi
  434.  
  435. #################################################################################################################################################
  436. # Default values
  437. #################################################################################################################################################
  438. group="docker" # Standard group for Docker users on Linux.
  439. docker_conf_dir="${docker_install_volume}/docker_config" # Dedicated directory for docker-compose files and appdata
  440. docker_data_dir="${docker_data_volume:-${docker_install_volume}}/data" # /data share within the selected volume
  441. docker_data_dir_id="${docker_data_dir_id:-${docker_install_volume_id}}" # Just the name/id of the data volume.
  442. ip="$(ip route get 1 | awk '{print $NF;exit}')" # get local ip
  443. gateway="$(ip route | grep "$(ip route get 1 | awk '{print $7}')" | awk 'FNR==2{print $1}')" # get gateway ip
  444. TZ="$(realpath --relative-to /usr/share/zoneinfo /etc/localtime)" # get timezone
  445. qsv="/dev/dri/"
  446.  
  447. #################################################################################################################################################
  448. # Install core dependencies (curl, jq) and then Docker
  449. #################################################################################################################################################
  450. # Install curl first, as it's used for downloading templates and .env files
  451. if ! command -v curl &> /dev/null; then
  452. printf '\n%b\n' " ${ulmc} curl is required for downloading files. Installing curl..."
  453. install_package "curl"
  454. else
  455. printf '\n%b\n' " ${utick} curl is already installed."
  456. fi
  457.  
  458. # Install jq, as it's used for docker daemon.json modification
  459. if ! command -v jq &> /dev/null; then
  460. printf '\n%b\n' " ${ulmc} jq is required for Docker configuration. Installing jq..."
  461. install_package "jq"
  462. else
  463. printf '\n%b\n' " ${utick} jq is already installed."
  464. fi
  465.  
  466. if [[ "${install_docker}" == 'yes' ]]; then
  467. install_package "docker.io" # For Debian/Ubuntu based systems
  468. # For Fedora/RHEL, it might be docker-ce, containerd.io, docker-compose-plugin
  469. # If docker-compose is not installed with docker.io, install it separately
  470. if ! command -v docker-compose &> /dev/null && ! command -v docker compose &> /dev/null; then
  471. install_package "docker-compose" # Separate package for docker-compose v1
  472. # Some newer distros use `docker compose` plugin, which is typically installed with docker-ce
  473. # If the above fails, users might need to install docker-compose-plugin manually
  474. fi
  475.  
  476. # Configure Docker data root if it's not the default /var/lib/docker
  477. # This requires moving existing Docker data if it's already running on /var/lib/docker
  478. if [[ "${docker_install_volume}/docker" != "/var/lib/docker" ]]; then
  479. printf '%b\n' " ${ulmc} Configuring Docker data root to ${clc}${docker_install_volume}/docker${cend}"
  480. mkdir -p "${docker_install_volume}/docker"
  481.  
  482. # Stop Docker service before modifying daemon.json
  483. if systemctl is-system-running &> /dev/null; then
  484. printf '\n%b\n' " ${ulmc} Stopping Docker service to reconfigure data root..."
  485. systemctl stop docker &> /dev/null
  486. fi
  487.  
  488. # Ensure /etc/docker directory exists
  489. mkdir -p /etc/docker
  490.  
  491. # Create or update daemon.json
  492. if [ ! -f /etc/docker/daemon.json ]; then
  493. echo '{}' > /etc/docker/daemon.json
  494. fi
  495. # Use jq to safely add or update the "data-root" entry
  496. if ! grep -q '"data-root":' /etc/docker/daemon.json; then
  497. jq --arg dr "${docker_install_volume}/docker" '. + {"data-root": $dr}' /etc/docker/daemon.json > /etc/docker/daemon.json.tmp && mv /etc/docker/daemon.json.tmp /etc/docker/daemon.json
  498. else
  499. jq --arg dr "${docker_install_volume}/docker" '.["data-root"] = $dr' /etc/docker/daemon.json > /etc/docker/daemon.json.tmp && mv /etc/docker/daemon.json.tmp /etc/docker/daemon.json
  500. fi
  501.  
  502. if systemctl is-system-running &> /dev/null; then
  503. printf '\n%b\n' " ${ulmc} Starting Docker service after reconfiguring data root..."
  504. systemctl start docker &> /dev/null
  505. systemctl enable docker &> /dev/null
  506. fi
  507. fi
  508.  
  509. else
  510. printf "\n ${utick} %b\n" "Docker is already installed"
  511. fi
  512.  
  513. #################################################################################################################################################
  514. # Test Docker
  515. #################################################################################################################################################
  516. # Use systemctl to check Docker service status if systemd is available
  517. if systemctl is-system-running &> /dev/null; then
  518. if systemctl is-active --quiet docker; then
  519. printf '\n%b\n' " ${utick} Docker is running!"
  520. else
  521. printf '\n%b\n\n' " ${ucross} Docker service is not active. Please check Docker installation manually."
  522. exit 1
  523. fi
  524. else
  525. # Fallback for systems without systemd (less common for NAS, but possible)
  526. if docker info &> /dev/null; then
  527. printf '\n%b\n' " ${utick} Docker is running!"
  528. else
  529. printf '\n%b\n\n' " ${ucross} Docker is not running or not accessible. Please check Docker installation manually."
  530. exit 1
  531. fi
  532. fi
  533.  
  534. #################################################################################################################################################
  535. # Check for user
  536. #################################################################################################################################################
  537. printf '\n%b\n' " ${ulbc} Checking if user ${clm}dockeruser${cend} exists"
  538. # Use a more generic user name for compatibility
  539. DEFAULT_USER="dockeruser"
  540. if id "${DEFAULT_USER}" &> /dev/null; then
  541. printf '\n%b\n' " ${utick} User ${clm}${DEFAULT_USER}${cend} exists!"
  542. user="${DEFAULT_USER}"
  543. else
  544. printf '\n%b\n' " ${ucross} The user ${clm}${DEFAULT_USER}${cend} doesn't exist, creating a user for security purposes."
  545. read -p "Enter a username for Docker containers (press Enter for default: ${DEFAULT_USER}): " custom_user
  546. user=${custom_user:-$DEFAULT_USER}
  547.  
  548. if [ -z "$user" ]; then
  549. printf '\n%b\n' " ${ucross} Username cannot be empty. Aborting..."
  550. exit 1
  551. fi
  552.  
  553. if id "$user" &> /dev/null; then
  554. printf '\n%b\n' " ${ucross} The user ${clm}$user${cend} already exists. Skipping user creation..."
  555. else
  556. read -s -p "Enter a password for $user: " password
  557. printf '\n'
  558.  
  559. if [ -z "$password" ]; then
  560. printf '\n%b\n' " ${ucross} Password cannot be empty. Aborting..."
  561. exit 1
  562. fi
  563.  
  564. # Use useradd for user creation on generic Linux
  565. if useradd -m -s /bin/bash "$user"; then # -m creates home directory, -s sets shell
  566. echo "$user:$password" | chpasswd
  567. printf '\n%b\n' " ${utick} User ${clm}$user${cend} created!"
  568. # Add user to the 'docker' group
  569. usermod -aG docker "$user"
  570. printf '\n%b\n' " ${utick} User ${clm}$user${cend} added to ${clm}docker${cend} group."
  571. else
  572. printf '\n%b\n' " ${ucross} Failed to create user ${clm}$user${cend}. Aborting..."
  573. exit 1
  574. fi
  575. fi
  576. fi
  577. #################################################################################################################################################
  578. # Create base Docker configuration and data directories and set permissions
  579. #################################################################################################################################################
  580. printf '\n%b\n' " ${ulbc} Creating base Docker configuration directory: ${clc}${docker_conf_dir}${cend}"
  581. mkdir -p "${docker_conf_dir}"
  582. # Set permissions:
  583. # u=rwX: User (owner) has read, write, and execute/traverse permissions.
  584. # g=rwX: Group (docker group) has read, write, and execute/traverse permissions.
  585. # o=rX: Others have read and execute/traverse permissions (important for web servers accessing media).
  586. # The 'X' (capital X) permission means execute for directories, and execute for files if any of the execute bits are set.
  587. # This ensures directories are traversable but executable flag is only set on files where appropriate.
  588. chown "${user}":"${group}" "${docker_conf_dir}"
  589. chmod u=rwX,g=rwX,o=rX "${docker_conf_dir}"
  590. printf '\n%b\n' " ${utick} Created and set permissions for ${clc}${docker_conf_dir}${cend}."
  591.  
  592. printf '\n%b\n' " ${ulbc} Creating base Docker data directory: ${clc}${docker_data_dir}${cend}"
  593. mkdir -p "${docker_data_dir}"
  594. chown "${user}":"${group}" "${docker_data_dir}"
  595. chmod u=rwX,g=rwX,o=rX "${docker_data_dir}"
  596. printf '\n%b\n' " ${utick} Created and set permissions for ${clc}${docker_data_dir}${cend}."
  597.  
  598. printf '\n%b\n' " ${ulmc} Setting final recursive permissions for main directories."
  599. # These recursive commands will ensure all new files/folders within are also correctly permissioned
  600. chown -R "${user}":"${group}" "${docker_data_dir}" "${docker_conf_dir}"
  601. chmod -R u=rwX,g=rwX,o=rX "${docker_data_dir}"
  602. chmod -R u=rwX,g=rwX,o=rX "${docker_conf_dir}"
  603. printf '\n%b\n' " ${utick} Initial permissions recursively applied."
  604.  
  605. #################################################################################################################################################
  606. # VPN stuff
  607. #################################################################################################################################################
  608. install_tun() {
  609. # Check for /dev/net/tun device node
  610. if [[ -c /dev/net/tun ]]; then
  611. printf '\n%b\n' " ${utick} /dev/net/tun device node already exists."
  612. else
  613. printf '\n%b\n' " ${ulmc} Creating /dev/net/tun device node for VPN"
  614. mkdir -p /dev/net
  615. mknod /dev/net/tun c 10 200 # Create character device node
  616. chmod 600 /dev/net/tun # Set permissions
  617. fi
  618.  
  619. # Ensure tun kernel module is loaded
  620. if ! lsmod | grep -q "^tun"; then
  621. printf '\n%b\n' " ${ulmc} Loading tun kernel module."
  622. modprobe tun
  623. if [ $? -ne 0 ]; then
  624. printf "\n%b\n" " ${ulrc} Failed to load 'tun' kernel module. VPN might not work."
  625. else
  626. printf '\n%b\n' " ${utick} 'tun' kernel module loaded."
  627. fi
  628. fi
  629.  
  630. # Install/Enable systemd service if systemd is detected
  631. if systemctl is-system-running &> /dev/null; then # Check if systemd is running
  632. printf '\n%b\n' " ${ulmc} Systemd detected. Configuring tun.service."
  633. if curl -sL "https://raw.githubusercontent.com/TRaSH-/Guides-Synology-Templates/main/script/tun.service" -o "/etc/systemd/system/tun.service"; then
  634. printf '\n%b\n' " ${utick} Service file to start Tun downloaded."
  635. else
  636. printf '\n%b\n' " ${ucross} Failed to download tun.service. VPN might require manual setup."
  637. fi
  638.  
  639. # Reload systemd daemon to pick up new service file
  640. systemctl daemon-reload &> /dev/null
  641. if systemctl enable tun.service &> /dev/null; then
  642. printf '\n%b\n' " ${utick} Service tun.service enabled to start on boot."
  643. else
  644. printf '\n%b\n' " ${ucross} Failed to enable tun.service."
  645. fi
  646.  
  647. if systemctl start tun.service &> /dev/null; then
  648. printf '\n%b\n' " ${utick} Service tun.service started."
  649. else
  650. printf '\n%b\n' " ${ucross} Failed to start tun.service. Check logs for errors."
  651. fi
  652.  
  653. if ! systemctl is-active --quiet tun.service; then
  654. printf "\n%b\n" " ${ulrc} Service tun.service couldn't start properly or is not active. VPN might not work."
  655. printf "\n%b\n" " ${tb} You may need to troubleshoot TUN device setup manually."
  656. # Not exiting here, as user might still want to proceed without VPN
  657. else
  658. printf '\n%b\n' " ${utick} Service tun.service is active."
  659. fi
  660. else
  661. printf "\n%b\n" " ${ulrc} Systemd not detected. Manual TUN setup or alternative init system configuration may be required if VPN is desired."
  662. fi
  663. }
  664. #################################################################################################################################################
  665. # Create docker-compose.yml and download .env
  666. #################################################################################################################################################
  667. #check if docker-compose already exist before overwriting.
  668. file="${docker_conf_dir}/appdata/docker-compose.yml"
  669. if [ -f "$file" ]; then
  670. while true; do
  671. read -erp $'\e[32m\U2714\e[0m docker-compose.yml already exists, overwrite and create new? \e[38;5;10m[y]es\e[m or \e[38;5;9m[n]o\e[m: ' -i "n" yesno
  672. case "${yesno}" in
  673. [Yy])
  674. printf '\n%b\n' " ${ulmc} Overwriting file..."
  675. printf '\n%b\n' " ${ulmc} Bootstrapping docker-compose.yml"
  676. mkdir -p "${docker_conf_dir}/appdata"
  677. cat > "${docker_conf_dir}/appdata/docker-compose.yml" <<EOF
  678. version: "3.2"
  679. services:
  680. EOF
  681. break
  682. ;;
  683. [Nn])
  684. printf '\n%b\n' " ${ulmc} Keeping current docker-compose.yml"
  685. break
  686. ;;
  687. *) printf '\n%b\n\n' " ${ulrc} Please answer ${clg}[y]es${cend} or ${clr}[n]o${cend}" ;;
  688. esac
  689. done
  690. else
  691. printf '\n%b\n' " ${ulmc} Bootstrapping docker-compose.yml"
  692. mkdir -p "${docker_conf_dir}/appdata"
  693. cat > "${docker_conf_dir}/appdata/docker-compose.yml" <<EOF
  694. version: "3.2"
  695. services:
  696. EOF
  697. printf '\n%b\n' " ${utick} docker-compose.yml bootstrapped"
  698. fi
  699.  
  700. printf '\n%b\n' " ${ulmc} Downloading docker .env"
  701. if wget -qO "${docker_conf_dir}/appdata/.env" "https://raw.githubusercontent.com/TRaSH-/Guides-Synology-Templates/main/docker-compose/.env"; then
  702. printf '\n%b\n' " ${utick} Docker .env downloaded."
  703. else
  704. printf '\n%b\n' " ${ucross} There was a problem downloading the .env, try again"
  705. exit 1
  706. fi
  707.  
  708. printf '\n%b\n' " ${ulmc} Setting correct User ID in .env"
  709. sed -i "s|PUID=1035|PUID=$(id "${user}" -u)|g" "${docker_conf_dir}/appdata/.env"
  710. printf '\n%b\n' " ${utick} User ID set.."
  711.  
  712. printf '\n%b\n' " ${ulmc} Setting local IP in .env"
  713. sed -i "s|192.168.x.x:32400|${ip}:32400|g" "${docker_conf_dir}/appdata/.env"
  714. printf '\n%b\n' " ${utick} Local IP set."
  715.  
  716. printf '\n%b\n' " ${ulmc} Setting local Gateway in .env"
  717. sed -i "s|LAN_NETWORK=192.168.x.0/24|LAN_NETWORK=$gateway|g" "${docker_conf_dir}/appdata/.env"
  718. printf '\n%b\n' " ${utick} local Gateway set."
  719.  
  720. printf '\n%b\n' " ${ulmc} Setting timezone in .env"
  721. sed -i "s|Europe/Amsterdam|${TZ}|g" "${docker_conf_dir}/appdata/.env"
  722. printf '\n%b\n' " ${utick} Timezone set."
  723.  
  724. printf '\n%b\n' " ${ulmc} Setting correct docker config dir in then .env"
  725. sed -i "s|DOCKERCONFDIR=/volume1/docker|DOCKERCONFDIR=${docker_conf_dir}|g" "${docker_conf_dir}/appdata/.env"
  726. printf '\n%b\n' " ${utick} ${clc}${docker_conf_dir}${cend} set."
  727.  
  728. printf '\n%b\n' " ${ulmc} Setting correct docker storage dir in the .env"
  729. sed -i "s|DOCKERSTORAGEDIR=/volume1/data|DOCKERSTORAGEDIR=${docker_data_dir}|g" "${docker_conf_dir}/appdata/.env"
  730. printf '\n%b\n' " ${utick} ${clc}${docker_data_dir}${cend} set."
  731. #################################################################################################################################################
  732. # compose template downloader
  733. #################################################################################################################################################
  734. get_app_compose() {
  735. if wget -qO "${docker_conf_dir}/appdata/${1}.yml" "https://raw.githubusercontent.com/TRaSH-/Guides-Synology-Templates/main/templates/${1,,}.yml"; then
  736. [[ "${options}" = 'sabnzbd' ]] && sed -r 's|- 8080:8080$|- 7080:8080|g' -i "${docker_conf_dir}/appdata/${1}.yml"
  737. [[ "${options}" == 'dozzle' ]] && sed -r 's|- 8080:8080$|- 7081:8080|g' -i "${docker_conf_dir}/appdata/${1}.yml"
  738.  
  739. if grep -q "^\s*${1,,}:" "${docker_conf_dir}/appdata/docker-compose.yml"; then
  740. printf '\n%b\n' " ${ucross} Skipped adding ${1,,} to compose, already exists."
  741. rm -f "${docker_conf_dir}/appdata/${1}.yml"
  742. else
  743. printf '\n' >> "${docker_conf_dir}/appdata/docker-compose.yml"
  744. sed -n 'p' "${docker_conf_dir}/appdata/${1}.yml" >> "${docker_conf_dir}/appdata/docker-compose.yml"
  745. rm -f "${docker_conf_dir}/appdata/${1}.yml"
  746. printf '\n%b\n' " ${utick} ${1,,} template added to compose."
  747. fi
  748. else
  749. printf '\n%b\n' " ${ucross} There was a problem downloading the template for ${1,,}. Please try again."
  750. exit 1
  751. fi
  752. }
  753. #################################################################################################################################################
  754. # Run _multiselect function
  755. #################################################################################################################################################
  756. printf '\n%b' " arrow down => down"
  757. printf '\n%b' " arrow up => up"
  758. printf '\n%b' " Space bar => toggle selection"
  759. printf '\n%b\n' " Enter key => confirm selection"
  760. _multiselect
  761. #################################################################################################################################################
  762. # Process selections
  763. #################################################################################################################################################
  764. while true; do
  765. read -erp $' \e[32m\U2714\e[0m '"Is this correct selection? "$'\e[38;5;10m'"[y]es"$'\e[m'" or "$'\e[38;5;9m'"[n]o"$'\e[m'" : " -i "y" yesno
  766. case "${yesno}" in
  767. [Yy]*)
  768. printf '\n%b\n' " ${ulmc} Creating docker-compose"
  769. for options in "${selected_options[@]}"; do
  770. mkdir -p "${docker_conf_dir}/appdata/${options}"
  771. get_app_compose "${options}"
  772. [[ "${options}" == 'plex' ]] && plex_installed="yes"
  773. [[ "${options}" == 'jellyfin' ]] && jellyfin_installed="yes"
  774. [[ "${options}" == 'qbittorrent' ]] && qbit_installed="yes" && mkdir -p "${docker_data_dir}"/torrents/{tv,movies}
  775. [[ "${options}" == 'radarr' ]] && mkdir -p "${docker_data_dir}/media/movies"
  776. [[ "${options}" == 'sonarr' ]] && mkdir -p "${docker_data_dir}/media/tv"
  777. [[ "${options}" =~ ^(sabnzbd|nzbget)$ ]] && mkdir -p "${docker_data_dir}"/usenet/complete/{tv,movies}
  778. done
  779.  
  780. if [[ "${plex_installed}" == "yes" || "${jellyfin_installed}" == "yes" ]]; then
  781. #check for quick sync
  782. if [[ -d "$qsv" ]]; then
  783. ### Do nothing if $qsv exists.
  784. printf '\n%b\n' " ${utick} Intel Quick Sync found for Plex/Jellyfin Hardware Transcoding."
  785. else
  786. ### Take action if $qsv does not exist, by commenting out device mappings
  787. sed -r "s|^(.*)devices:(.*)# optional: if you have a Syno with an Intel CPU(.*)|#\1devices:\2# optional: if you have a Syno with an Intel CPU\3|g" -i "${docker_conf_dir}/appdata/docker-compose.yml"
  788. sed -r "s|^(.*)- /dev/dri:/dev/dri(.*)# optional: if you have a Syno with an Intel CPU(.*)|#\1- /dev/dri:/dev/dri\2# optional: if you have a Syno with an Intel CPU\3|g" -i "${docker_conf_dir}/appdata/docker-compose.yml"
  789. printf '\n%b\n' " ${ucross} No Intel Quick Sync found for Plex/Jellyfin Hardware Transcoding. Device mapping commented out."
  790. fi
  791. fi
  792.  
  793. if [[ "${qbit_installed}" == "yes" ]]; then
  794. while true; do
  795. read -erp $'\n \e[32m\U2714\e[0m '"Do you want Qbittorrent installed with VPN? "$'\e[38;5;10m'"[y]es"$'\e[m'" or "$'\e[38;5;9m'"[n]o"$'\e[m'" : " -i "" yesno
  796. case "${yesno}" in
  797. [Yy]*)
  798. printf '\n%b\n\n' " ${utick} With VPN"
  799. mkdir -p "${docker_conf_dir}/appdata/qbittorrent/wireguard"
  800. install_tun # Call the generic install_tun function
  801. while true; do
  802. read -erp $' \e[93m\U25cf\e[0m '"Place your "$'\e[38;5;81m'"wg0.conf"$'\e[m'" in:"$'\n\n \e[38;5;81m'"${docker_conf_dir}/appdata/qbittorrent/wireguard"$'\e[m\n\n \e[93m\U25cf\e[0m '"When that is done please confirm "$'\e[38;5;10m'"[y]es"$'\e[m'" : " -i "" yes
  803. case "${yes}" in
  804. [Yy]*)
  805. sed -r 's|VPN_ENABLED=false|VPN_ENABLED=true|g' -i "${docker_conf_dir}/appdata/.env"
  806. sed -r 's|# devices:| devices:|g' -i "${docker_conf_dir}/appdata/docker-compose.yml"
  807. sed -r 's|# - /dev/net/tun:/dev/net/tun| - /dev/net/tun:/dev/net/tun|g' -i "${docker_conf_dir}/appdata/docker-compose.yml"
  808. if [[ -f "${docker_conf_dir}/appdata/qbittorrent/wireguard/wg0.conf" ]]; then
  809. if mv "${docker_conf_dir}/appdata/qbittorrent/wireguard/wg0.conf" "${docker_conf_dir}/appdata/qbittorrent/wireguard/wg0-fix.conf" 2> /dev/null; then
  810. printf '\n%b\n' " ${utick} wg0.conf found and fixed (renamed to wg0-fix.conf)."
  811. fi
  812. else
  813. printf '\n%b\n\n ' " ${ucross} wg0.conf not found. Place file with filename ${clc}wg0.conf${cend} and answer yes when ready."
  814. # Allow user to retry putting the file in place
  815. continue
  816. fi
  817. break 2
  818. ;;
  819. [Nn]*)
  820. printf '\n%b\n\n ' " ${ucross} Cancelled."
  821. exit 1
  822. ;;
  823. *) printf '\n%b\n\n' " ${ulrc} Please answer ${clg}[y]es${cend} to continue or ${clr}[n]o${cend} to cancel." ;;
  824. esac
  825. done
  826. ;;
  827. [Nn]*)
  828. printf '\n%b\n' " ${ucross} Without VPN."
  829. sed -r 's|VPN_ENABLED=true|VPN_ENABLED=false|g' -i "${docker_conf_dir}/appdata/.env"
  830. sed -r 's| devices:|# devices:|g' -i "${docker_conf_dir}/appdata/docker-compose.yml"
  831. sed -r 's| - /dev/net/tun:/dev/net/tun|# - /dev/net/tun:/dev/net/tun|g' -i "${docker_conf_dir}/appdata/docker-compose.yml"
  832. break
  833. ;;
  834. esac
  835. done
  836. fi
  837. printf '\n%b\n' " ${ulmc} Doing final permissions stuff"
  838. # Ensure proper recursive ownership and permissions for the selected directories
  839. chown -R "${user}":"${group}" "${docker_data_dir}" "${docker_conf_dir}/appdata"
  840. chmod -R u=rwX,g=rwX,o=rX "${docker_data_dir}" # User and group can read, write, execute. Others can read and execute.
  841. chmod -R u=rwX,g=rwX,o=rX "${docker_conf_dir}/appdata" # User and group can read, write, execute. Others can read and execute.
  842. printf '\n%b\n' " ${utick} Permissions set."
  843.  
  844. printf '\n%b\n' " ${uplus} Installing Pullio for auto updates"
  845. if [[ -x "/usr/local/bin/pullio" ]]; then
  846. printf '\n%b\n' " ${ucross} Pullio is already installed."
  847. else
  848. if wget -qO /usr/local/bin/pullio "https://raw.githubusercontent.com/hotio/pullio/master/pullio.sh"; then
  849. chmod +x /usr/local/bin/pullio
  850. mkdir -p "${docker_conf_dir}/appdata/pullio"
  851. printf '\n%b\n' " ${utick} Pullio installed, read final message when the script is done."
  852. else
  853. printf '\n%b\n' " ${ucross} There was a problem downloading Pullio. Please install manually. Read https://trash-guides.info/Hardlinks/How-to-setup-for/Synology/#pullio-auto-update-docker-compose-the-correct-way"
  854. fi
  855. fi
  856.  
  857. printf '\n%b\n\n' " ${uplus} Installing the selected containers"
  858. # Use 'docker compose' if available, otherwise fallback to 'docker-compose'
  859. if command -v docker &> /dev/null && docker compose version &> /dev/null; then
  860. DOCKER_COMPOSE_CMD="docker compose"
  861. elif command -v docker-compose &> /dev/null; then
  862. DOCKER_COMPOSE_CMD="docker-compose"
  863. else
  864. printf '\n%b\n' " ${ulmc} Docker Compose not found. Attempting to install docker-compose..."
  865. if [[ "${PKG_MANAGER}" == "apt" ]]; then
  866. apt-get update && apt-get install -y docker-compose
  867. elif [[ "${PKG_MANAGER}" == "dnf" ]]; then
  868. dnf install -y docker-compose
  869. fi
  870.  
  871. if command -v docker-compose &> /dev/null; then
  872. DOCKER_COMPOSE_CMD="docker-compose"
  873. printf '\n%b\n' " ${utick} docker-compose installed and ready."
  874. else
  875. printf '\n%b\n\n' " ${ucross} Could not install docker-compose automatically. Please install it manually."
  876. exit 1
  877. fi
  878. fi
  879.  
  880. cd "${docker_conf_dir}/appdata/" || return
  881. eval "${DOCKER_COMPOSE_CMD} up -d --remove-orphans"
  882. printf '\n%b\n\n' " ${utick} All set, everything should be running. If you have errors, follow the complete guide. And join our discord server."
  883. printf '\n%b\n\n' " ${utick} If you want to enable automatic updates, you need to create a Scheduled Task.\n Read instructions here: https://trash-guides.info/Hardlinks/How-to-setup-for/Synology/#pullio-auto-update-docker-compose-the-correct-way"
  884. break
  885. ;;
  886. [Nn]*)
  887. _multiselect
  888. ;;
  889. *) printf '\n%b\n\n' " ${ulrc} Please answer ${clg}[y]es${cend} or ${clr}[n]o${cend}" ;;
  890. esac
  891. done
  892.  
  893. exit
  894.  
  895.  
Add Comment
Please, Sign In to add comment