Skip to content

Instantly share code, notes, and snippets.

@regg00
Created January 14, 2026 15:14
Show Gist options
  • Select an option

  • Save regg00/34654115d5499395a2da609edf6f4b0f to your computer and use it in GitHub Desktop.

Select an option

Save regg00/34654115d5499395a2da609edf6f4b0f to your computer and use it in GitHub Desktop.
Shelfmark script to upload new books to Booklore API.
#!/bin/bash
# Booklore File Uploader
# Uploads files to Booklore API
#
# Required environment variables:
# BOOKLORE_HOST - Booklore server URL (e.g., http://192.168.7.3:6060)
# BOOKLORE_USERNAME - Booklore username
# BOOKLORE_PASSWORD - Booklore password
# BOOKLORE_LIBRARY_ID - Library ID to upload to
# BOOKLORE_PATH_ID - Path ID within the library
#
# Usage: ./upload_to_booklore.sh /path/to/file.epub
set -euo pipefail
# Debug mode - set to 1 for verbose output
DEBUG=${DEBUG:-0}
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
log_info() {
echo -e "${GREEN}[INFO]${NC} $1" >&2
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1" >&2
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1" >&2
}
log_debug() {
if [[ "$DEBUG" == "1" ]]; then
echo -e "${CYAN}[DEBUG]${NC} $1" >&2
fi
}
# Validate required environment variables
validate_env() {
local missing=0
if [[ -z "${BOOKLORE_HOST:-}" ]]; then
log_error "BOOKLORE_HOST environment variable is not set"
missing=1
fi
if [[ -z "${BOOKLORE_USERNAME:-}" ]]; then
log_error "BOOKLORE_USERNAME environment variable is not set"
missing=1
fi
if [[ -z "${BOOKLORE_PASSWORD:-}" ]]; then
log_error "BOOKLORE_PASSWORD environment variable is not set"
missing=1
fi
if [[ -z "${BOOKLORE_LIBRARY_ID:-}" ]]; then
log_error "BOOKLORE_LIBRARY_ID environment variable is not set"
missing=1
fi
if [[ -z "${BOOKLORE_PATH_ID:-}" ]]; then
log_error "BOOKLORE_PATH_ID environment variable is not set"
missing=1
fi
if [[ $missing -eq 1 ]]; then
exit 1
fi
}
# Authenticate with Booklore and get access token
authenticate() {
local login_url="${BOOKLORE_HOST}/api/v1/auth/login"
log_info "Authenticating with Booklore at ${BOOKLORE_HOST}..."
local response
local http_code
response=$(curl -s -w "\n%{http_code}" \
-X POST \
-H "Content-Type: application/json" \
-d "{\"username\": \"${BOOKLORE_USERNAME}\", \"password\": \"${BOOKLORE_PASSWORD}\"}" \
"${login_url}")
http_code=$(echo "$response" | tail -n1)
local body
body=$(echo "$response" | sed '$d')
if [[ "$http_code" != "200" ]]; then
log_error "Authentication failed with HTTP status ${http_code}"
log_error "Response: ${body}"
exit 1
fi
# Extract access token from JSON response
local access_token
access_token=$(echo "$body" | grep -o '"accessToken"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/"accessToken"[[:space:]]*:[[:space:]]*"\([^"]*\)"/\1/')
if [[ -z "$access_token" ]]; then
# Try alternative key format
access_token=$(echo "$body" | grep -o '"access_token"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/"access_token"[[:space:]]*:[[:space:]]*"\([^"]*\)"/\1/')
fi
if [[ -z "$access_token" ]]; then
log_error "Failed to extract access token from response"
log_error "Response: ${body}"
exit 1
fi
log_info "Authentication successful"
echo "$access_token"
}
# Format bytes to human-readable string
format_bytes() {
local bytes=$1
if [[ $bytes -lt 1024 ]]; then
echo "${bytes} B"
elif [[ $bytes -lt 1048576 ]]; then
echo "$(( bytes / 1024 )) KB"
elif [[ $bytes -lt 1073741824 ]]; then
echo "$(( bytes / 1048576 )) MB"
else
echo "$(( bytes / 1073741824 )) GB"
fi
}
# Upload file to Booklore
upload_file() {
local file_path="$1"
local access_token="$2"
local filename
filename=$(basename "$file_path")
local file_size
file_size=$(stat -c%s "$file_path" 2>/dev/null || stat -f%z "$file_path" 2>/dev/null)
local human_size
human_size=$(format_bytes "$file_size")
local upload_url="${BOOKLORE_HOST}/api/v1/files/upload?libraryId=${BOOKLORE_LIBRARY_ID}&pathId=${BOOKLORE_PATH_ID}"
log_info "Uploading ${filename} (${human_size}) to Booklore..."
log_debug "Upload URL: ${upload_url}"
log_debug "File path: ${file_path}"
log_debug "Token (first 20 chars): ${access_token}"
local response
local http_code
# Build curl command with proper headers
local curl_cmd="curl -s -w \"\n%{http_code}\""
curl_cmd+=" -X POST"
curl_cmd+=" -H \"Authorization: Bearer ${access_token}\""
curl_cmd+=" -F \"file=@${file_path};filename=${filename};type=application/octet-stream\""
curl_cmd+=" \"${upload_url}\""
log_debug "Curl command: ${curl_cmd}"
response=$(curl -s -w "\n%{http_code}" \
-X POST \
-H "Authorization: Bearer ${access_token}" \
-F "file=@${file_path};filename=${filename};type=application/octet-stream" \
"${upload_url}")
http_code=$(echo "$response" | tail -n1)
local body
body=$(echo "$response" | sed '$d')
log_debug "HTTP Status: ${http_code}"
log_debug "Response body: ${body}"
if [[ "$http_code" == "200" ]] || [[ "$http_code" == "201" ]] || [[ "$http_code" == "204" ]]; then
log_info "Successfully uploaded ${filename}"
return 0
else
log_error "Upload failed with HTTP status ${http_code}"
log_error "Response: ${body}"
log_error ""
log_error "Debug info:"
log_error " URL: ${upload_url}"
log_error " File: ${file_path}"
log_error " Size: ${human_size}"
log_error ""
log_error "Try running with DEBUG=1 for more details"
return 1
fi
}
# Main function
main() {
if [[ $# -lt 1 ]]; then
log_error "Usage: $0 <file_path>"
log_error "Example: $0 /path/to/book.epub"
exit 1
fi
local file_path="$1"
# Validate file exists
if [[ ! -f "$file_path" ]]; then
log_error "File not found: ${file_path}"
exit 1
fi
# Validate environment variables
validate_env
# Authenticate and get access token
local access_token
access_token=$(authenticate)
# Upload the file
upload_file "$file_path" "$access_token"
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment