@@ -95,7 +95,13 @@ print_output() {
9595 if [[ $message == * :* ]]; then
9696 local prefix=" ${message%%:* } :"
9797 local suffix=" ${message#*: } "
98- printf ' %b\n' " $color $icon $prefix ${color} $suffix $COLOR_RESET "
98+ # Robustly check if a suffix was actually extracted.
99+ # This prevents duplication when the message ends with a colon.
100+ if [[ -n $suffix && $suffix != " $message " ]]; then
101+ printf ' %b\n' " $color $icon $prefix ${color} $suffix $COLOR_RESET "
102+ else
103+ printf ' %b\n' " $color $icon $message "
104+ fi
99105 else
100106 printf ' %b\n' " $color $icon $message "
101107 fi
@@ -110,6 +116,48 @@ print_success() { print_output "SUCCESS" "$1"; }
110116print_failure () { print_output " FAILURE" " $1 " ; }
111117print_generic () { print_output " " " $1 " ; }
112118
119+ # Print build info from JSON
120+ print_build_info () {
121+ local json_data=" $1 "
122+ local libtorrent_ver=" $2 "
123+ print_info " Latest Build Information:"
124+
125+ local key_to_remove
126+ if [[ $libtorrent_ver == " v2" ]]; then
127+ key_to_remove=" libtorrent_1_2"
128+ else
129+ key_to_remove=" libtorrent_2_0"
130+ fi
131+
132+ if command -v jq > /dev/null 2>&1 ; then
133+ # Preferred method: Use jq to create "key value" pairs
134+ local filtered_json
135+ filtered_json=$( printf ' %s' " $json_data " | jq -r " del(.$key_to_remove ) | to_entries[] | \" \(.key) \(.value)\" " )
136+ while read -r key value; do
137+ if [[ -z $key ]]; then
138+ continue
139+ fi
140+ local aligned_text
141+ aligned_text=$( printf " %-18s %s" " ${key} :" " $value " )
142+ printf ' %b\n' " $COLOR_INFO $aligned_text $COLOR_RESET "
143+ done <<< " $filtered_json"
144+ else
145+ # Fallback method: Use bash regex matching. It's slower but more robust.
146+ while IFS= read -r line; do
147+ # Filter out the unwanted libtorrent version and non-key-value lines
148+ if [[ $line == * " $key_to_remove " * || ! $line =~ \" (.+)\" :[[:space:]]* \" (.+)\" ]]; then
149+ continue
150+ fi
151+ local key=" ${BASH_REMATCH[1]} "
152+ local value=" ${BASH_REMATCH[2]} "
153+
154+ local aligned_text
155+ aligned_text=$( printf " %-18s %s" " ${key} :" " $value " )
156+ printf ' %b\n' " $COLOR_INFO $aligned_text $COLOR_RESET "
157+ done <<< " $(printf '%s' " $json_data " )"
158+ fi
159+ }
160+
113161# Detect architecture and map to binary name
114162detect_arch () {
115163 local arch_output=" "
@@ -193,6 +241,30 @@ check_gh_cli() {
193241 fi
194242}
195243
244+ # Fetch URL content using curl or wget
245+ fetch_url () {
246+ local url=" $1 "
247+ local tool
248+ tool=$( check_download_tools)
249+
250+ local response
251+ case " $tool " in
252+ wget)
253+ response=$( wget -qO- " $url " 2> /dev/null)
254+ ;;
255+ curl)
256+ response=$( curl -sL " $url " 2> /dev/null)
257+ ;;
258+ esac
259+
260+ if [[ $? -ne 0 ]]; then
261+ print_warning " Failed to fetch information from URL: $url "
262+ printf " " # Return empty on failure
263+ else
264+ printf " %s" " $response "
265+ fi
266+ }
267+
196268# Create download function based on architecture checks
197269create_download_url () {
198270 local arch=" $1 "
@@ -265,25 +337,9 @@ verify_binary_integrity() {
265337
266338 # Fetch release assets from GitHub API
267339 local api_response
268- case " $tool " in
269- wget)
270- api_response=$( wget -qO- " $api_url " 2> /dev/null) || {
271- print_warning " Failed to fetch release information from GitHub API"
272- print_info " Local SHA256 verification completed"
273- return 1
274- }
275- ;;
276- curl)
277- api_response=$( curl -sL " $api_url " 2> /dev/null) || {
278- print_warning " Failed to fetch release information from GitHub API"
279- print_info " Local SHA256 verification completed"
280- return 1
281- }
282- ;;
283- esac
284-
340+ api_response=$( fetch_url " $api_url " )
285341 if [[ -z $api_response ]]; then
286- print_warning " Empty response from GitHub API"
342+ print_warning " Failed to fetch release information from GitHub API"
287343 print_info " Local SHA256 verification completed"
288344 return 1
289345 fi
@@ -294,8 +350,8 @@ verify_binary_integrity() {
294350 # Use jq if available (preferred method) - extract just the hash value
295351 api_digests=$( printf ' %s' " $api_response " | jq -r ' .assets[].digest // empty' 2> /dev/null | sed ' s/^sha256://' )
296352 else
297- # Fall back to sed if jq is not available - extract just the hash value
298- api_digests=$( printf ' %s' " $api_response " | sed -rn ' s|(.*)sha256:( [^"]*)".*|\2|p ' 2> /dev/null )
353+ # Fall back to grep/cut if jq is not available - more robust than a single sed
354+ api_digests=$( printf ' %s' " $api_response " | grep -o ' "digest": *" [^"]*" ' | cut -d ' " ' -f4 | sed ' s/^sha256:// ' )
299355 fi
300356
301357 if [[ -z $api_digests ]]; then
@@ -331,62 +387,40 @@ verify_binary_integrity() {
331387 return 1
332388 fi
333389}
334- # Get release tag from API
335- get_release_tag () {
336- local api=" https://github.com/userdocs/qbittorrent-nox-static/releases/latest/download/dependency-version.json"
337- local ver=" ${LIBTORRENT_VERSION:- v2} "
338- local tool
339- tool=$( check_download_tools)
340-
341- # Fetch API response
342- local response
343- case " $tool " in
344- wget)
345- response=$( wget -qO- " $api " 2> /dev/null) || {
346- handle_error $? " wget -qO $api " " Failed to fetch release information from API"
347- }
348- ;;
349- curl)
350- response=$( curl -sL " $api " 2> /dev/null) || {
351- handle_error $? " curl -sL $api " " Failed to fetch release information from API"
352- }
353- ;;
354- esac
355-
356- if [[ -z $response ]]; then
357- print_error " Failed to fetch release information - empty response"
358- print_error " API endpoint: $api "
359- exit 1
360- fi
390+ # Parse release info from JSON
391+ parse_release_info () {
392+ local response=" $1 "
393+ local ver=" $2 "
361394
362395 # Parse release tag
363- local qbt_ver libt_ver
364- qbt_ver=$( printf ' %s' " $response " | sed -rn ' s|(.*)"qbittorrent": "(.*)",|\2|p' )
365-
396+ local qbt_ver libt_ver revision libt_key
366397 case " $ver " in
367- v1)
368- libt_ver=$( printf ' %s' " $response " | sed -rn ' s|(.*)"libtorrent_1_2": "(.*)",|\2|p' )
369- ;;
370- v2)
371- libt_ver=$( printf ' %s' " $response " | sed -rn ' s|(.*)"libtorrent_2_0": "(.*)",|\2|p' )
372- ;;
398+ v1) libt_key=" libtorrent_1_2" ;;
399+ v2) libt_key=" libtorrent_2_0" ;;
373400 * )
374401 print_error " Invalid LibTorrent version: $ver "
375402 print_error " Valid options: v1, v2"
376403 exit 1
377404 ;;
378405 esac
379406
407+ if command -v jq > /dev/null 2>&1 ; then
408+ qbt_ver=$( printf ' %s' " $response " | jq -r ' .qbittorrent' )
409+ revision=$( printf ' %s' " $response " | jq -r ' .revision // ""' )
410+ libt_ver=$( printf ' %s' " $response " | jq -r --arg KEY " $libt_key " ' .[$KEY]' )
411+ else
412+ qbt_ver=$( printf ' %s' " $response " | sed -rn ' s/.*"qbittorrent": "([^"]*)".*/\1/p' )
413+ revision=$( printf ' %s' " $response " | sed -rn ' s/.*"revision": "([^"]*)".*/\1/p' )
414+ libt_ver=$( printf ' %s' " $response " | sed -rn " s/.*\" ${libt_key} \" : \" ([^\" ]*)\" .*/\1/p" )
415+ fi
416+
380417 if [[ -z $qbt_ver ]] || [[ -z $libt_ver ]]; then
381418 print_error " Failed to parse version information from API response"
382419 print_error " qBittorrent version: ${qbt_ver:- not found} "
383420 print_error " LibTorrent version: ${libt_ver:- not found} "
384421 exit 1
385422 fi
386423
387- # Parse revision field
388- local revision
389- revision=$( printf ' %s' " $response " | sed -rn ' s|.*"revision": "(.*)".*|\1|p' )
390424 if [[ -z $revision ]]; then
391425 print_warning " Failed to parse revision from API response"
392426 fi
@@ -436,10 +470,22 @@ main() {
436470 local arch=" ${FORCE_ARCH:- $(detect_arch)} "
437471 local libtorrent_ver=" ${LIBTORRENT_VERSION:- v2} "
438472 local install_path=" $HOME /bin/qbittorrent-nox"
473+
474+ # Fetch dependency info
475+ local dep_api_url=" https://github.com/userdocs/qbittorrent-nox-static/releases/latest/download/dependency-version.json"
476+ local dep_json
477+ dep_json=$( fetch_url " $dep_api_url " )
478+ if [[ -z $dep_json ]]; then
479+ handle_error 1 " fetch_url $dep_api_url " " Failed to fetch dependency information"
480+ fi
481+
482+ # Print build info for the user
483+ print_build_info " $dep_json " " $libtorrent_ver "
484+
439485 # Get release and download
440486 local release_tag revision
441- # Capture release tag and revision from get_release_tag
442- read release_tag revision <<< " $(get_release_tag )"
487+ # Capture release tag and revision from parse_release_info
488+ read release_tag revision <<< " $(parse_release_info " $dep_json " " $libtorrent_ver " )"
443489
444490 print_info " Architecture: $arch "
445491 print_info " LibTorrent version: $libtorrent_ver "
@@ -450,9 +496,15 @@ main() {
450496 print_info " Attestation verification: $( check_gh_cli && printf ' %s' " enabled" || printf ' %s' " disabled (gh cli not found)" ) "
451497
452498 # Prepare installation directory
453- print_action " Creating install directory: $HOME /bin"
454- mkdir -p " $HOME /bin" || {
455- handle_error $? " mkdir -p $HOME /bin" " Failed to create installation directory"
499+ local install_dir=" $HOME /bin"
500+ if [[ -f $install_dir ]]; then
501+ print_error " Installation path $install_dir exists and is a file."
502+ print_error " Please remove it or choose a different installation path."
503+ exit 1
504+ fi
505+ print_action " Creating install directory: $install_dir "
506+ mkdir -p " $install_dir " || {
507+ handle_error $? " mkdir -p $install_dir " " Failed to create installation directory"
456508 }
457509
458510 # Get release and download
0 commit comments