Created
January 14, 2026 15:14
-
-
Save regg00/34654115d5499395a2da609edf6f4b0f to your computer and use it in GitHub Desktop.
Shelfmark script to upload new books to Booklore API.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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