Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions lib/batch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,32 @@ batch_process_folders() {
# BATCH WRAPPERS (for interactive menu)
################################################################################

# Batch rename multiple folders (interactive)
batch_rename_interactive() {
log_info "Batch Rename Multiple Folders"
echo ""
echo "Enter directories to rename (one per line, empty line to finish):"
echo ""

local -a folders
while true; do
read -p "Directory: " dir
[[ -z "$dir" ]] && break

dir=$(validate_directory "$dir")
if [[ $? -eq 0 ]]; then
folders+=("$dir")
fi
done

if [[ ${#folders[@]} -eq 0 ]]; then
log_warning "No directories specified"
return 1
fi

batch_process_folders "rename" "${folders[@]}"
}

# Batch flatten multiple folders (interactive)
batch_flatten_interactive() {
log_info "Batch Flatten Multiple Folders"
Expand Down
292 changes: 250 additions & 42 deletions lib/catalog.sh
Original file line number Diff line number Diff line change
Expand Up @@ -115,78 +115,286 @@ init_catalog_db() {
fi
}

# Register a drive in the drives database
# Register a drive in the drives database without scanning files.
# Usage: register_drive <mount_point>
# Prints the drive_id on success.
register_drive() {
local mount_point="$1"

if [[ ! -d "$mount_point" ]]; then
log_error "Mount point does not exist: $mount_point"
return 1
fi

init_catalog_db

local drive_id=$(get_drive_id "$mount_point")
local drive_label=$(get_drive_label "$mount_point")
local drive_type=$(detect_drive_type "$mount_point")
local timestamp=$(date -Iseconds)
local drive_id drive_label drive_type timestamp
drive_id=$(get_drive_id "$mount_point")
drive_label=$(get_drive_label "$mount_point")
drive_type=$(detect_drive_type "$mount_point")
timestamp=$(date -Iseconds)

log_info "Registering drive: $drive_label ($drive_id)"
log_verbose " Mount point: $mount_point"
log_verbose " Type: $drive_type"

local entry
entry=$(jq -n \
--arg id "$drive_id" \
--arg label "$drive_label" \
--arg type "$drive_type" \
--arg mount "$mount_point" \
--arg ts "$timestamp" \
'{"id":$id,"label":$label,"type":$type,"mount_point":$mount,
"file_count":0,"last_scanned":null,"registered_at":$ts}')

jq --arg id "$drive_id" \
--argjson entry "$entry" \
--arg ts "$timestamp" \
'.drives = ([.drives[] | select(.id != $id)] + [$entry]) |
.last_updated = $ts' \
"$CATALOG_DRIVES_DB" > "${CATALOG_DRIVES_DB}.tmp" \
&& mv "${CATALOG_DRIVES_DB}.tmp" "$CATALOG_DRIVES_DB"

echo "$drive_id"
}

################################################################################
# CATALOG OPERATIONS (STUB IMPLEMENTATIONS)
# CATALOG OPERATIONS
################################################################################

# Catalog a drive and index all media files
# Note: This is a stub - full implementation not yet available
# Scan a directory/drive and index all media files into the catalog.
# Usage: catalog_drive <mount_point> [recursive=true]
catalog_drive() {
local mount_point="$1"
local recursive="${2:-true}"

log_warning "Drive cataloging feature is not yet implemented"
echo ""
echo -e "${COLOR_YELLOW}This feature would:${COLOR_RESET}"
echo -e " ${COLOR_CYAN}${SYMBOL_BULLET}${COLOR_RESET} Scan drive: $mount_point"
echo -e " ${COLOR_CYAN}${SYMBOL_BULLET}${COLOR_RESET} Index all media files"
echo -e " ${COLOR_CYAN}${SYMBOL_BULLET}${COLOR_RESET} Extract metadata (duration, resolution, etc.)"
echo -e " ${COLOR_CYAN}${SYMBOL_BULLET}${COLOR_RESET} Calculate file hashes for duplicate detection"
echo -e " ${COLOR_CYAN}${SYMBOL_BULLET}${COLOR_RESET} Store in catalog database"
echo ""
return 1
if [[ ! -d "$mount_point" ]]; then
log_error "Path does not exist: $mount_point"
return 1
fi

if ! command -v jq >/dev/null 2>&1; then
log_error "jq is required. Install with: sudo apt-get install jq"
return 1
fi

init_catalog_db

local drive_id drive_label drive_type timestamp
drive_id=$(get_drive_id "$mount_point")
drive_label=$(get_drive_label "$mount_point")
drive_type=$(detect_drive_type "$mount_point")
timestamp=$(date -Iseconds)

log_info "Cataloging: $drive_label ($mount_point)"

# Build find args
local -a find_args=("$mount_point")
[[ "$recursive" != "true" ]] && find_args+=("-maxdepth" "1")
find_args+=("-type" "f" \( \
-iname "*.mp4" -o -iname "*.mkv" -o -iname "*.avi" -o -iname "*.mov" -o \
-iname "*.wmv" -o -iname "*.flv" -o -iname "*.webm" -o -iname "*.m4v" -o \
-iname "*.mpg" -o -iname "*.mpeg" -o -iname "*.3gp" -o \
-iname "*.mp3" -o -iname "*.flac" -o -iname "*.wav" -o -iname "*.aac" -o \
-iname "*.m4a" -o -iname "*.ogg" -o \
-iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o \
-iname "*.gif" -o -iname "*.webp" \
\))

local -a media_files
mapfile -t media_files < <(find "${find_args[@]}" 2>/dev/null | sort)
local total=${#media_files[@]}

log_info "Found $total media file(s) — indexing..."

# Build JSON array of file entries
local file_entries="[]"
local processed=0

for file in "${media_files[@]}"; do
((processed++))
printf "\r${COLOR_CYAN}[%d/%d]${COLOR_RESET} %s" \
"$processed" "$total" "$(basename "$file")" >&2

local filename size mtime media_type file_hash
filename=$(basename "$file")
size=$(stat -c %s "$file" 2>/dev/null || stat -f %z "$file" 2>/dev/null || echo 0)
mtime=$(stat -c %Y "$file" 2>/dev/null || stat -f %m "$file" 2>/dev/null || echo 0)
media_type=$(get_media_type "$file")
file_hash=""
[[ "${CATALOG_INCLUDE_HASH:-false}" == true ]] && file_hash=$(calculate_file_hash "$file")

local rel_path="${file#${mount_point}/}"
local video_id
video_id=$(echo -n "$file" | md5sum | awk '{print $1}')

file_entries=$(echo "$file_entries" | jq \
--arg path "$file" \
--arg rel "$rel_path" \
--arg name "$filename" \
--arg type "$media_type" \
--argjson sz "$size" \
--argjson mt "$mtime" \
--arg hash "$file_hash" \
--arg drv "$drive_id" \
--arg vid "$video_id" \
--arg ts "$timestamp" \
'. + [{"video_id":$vid,"path":$path,"relative_path":$rel,
"filename":$name,"media_type":$type,
"file_size":$sz,"file_mtime":$mt,
"hash":$hash,"drive_id":$drv,"last_scanned":$ts}]')
done
echo "" >&2

# Replace this drive's entries in catalog DB
jq --arg drv "$drive_id" \
--argjson entries "$file_entries" \
--arg ts "$timestamp" \
'.videos = ([.videos[] | select(.drive_id != $drv)] + $entries) |
.last_updated = $ts' \
"$CATALOG_DB" > "${CATALOG_DB}.tmp" && mv "${CATALOG_DB}.tmp" "$CATALOG_DB"

# Upsert drive record
local drive_entry
drive_entry=$(jq -n \
--arg id "$drive_id" \
--arg label "$drive_label" \
--arg type "$drive_type" \
--arg mount "$mount_point" \
--argjson n "$total" \
--arg ts "$timestamp" \
'{"id":$id,"label":$label,"type":$type,"mount_point":$mount,
"file_count":$n,"last_scanned":$ts,"registered_at":$ts}')

jq --arg drv "$drive_id" \
--argjson entry "$drive_entry" \
--arg ts "$timestamp" \
'.drives = ([.drives[] | select(.id != $drv)] + [$entry]) |
.last_updated = $ts' \
"$CATALOG_DRIVES_DB" > "${CATALOG_DRIVES_DB}.tmp" \
&& mv "${CATALOG_DRIVES_DB}.tmp" "$CATALOG_DRIVES_DB"

log_success "Cataloged $total file(s) from: $drive_label"
STATS[files_processed]=$(( ${STATS[files_processed]:-0} + total ))
return 0
}

# List all cataloged drives
# Note: This is a stub - full implementation not yet available
# List all drives registered in the catalog.
list_cataloged_drives() {
log_warning "Drive listing feature is not yet implemented"
init_catalog_db

if ! command -v jq >/dev/null 2>&1; then
log_error "jq is required. Install with: sudo apt-get install jq"
return 1
fi

local drive_count
drive_count=$(jq '.drives | length' "$CATALOG_DRIVES_DB" 2>/dev/null || echo 0)

echo ""
echo -e "${COLOR_YELLOW}This feature would show:${COLOR_RESET}"
echo -e " ${COLOR_CYAN}${SYMBOL_BULLET}${COLOR_RESET} All registered drives"
echo -e " ${COLOR_CYAN}${SYMBOL_BULLET}${COLOR_RESET} Drive status (online/offline)"
echo -e " ${COLOR_CYAN}${SYMBOL_BULLET}${COLOR_RESET} Number of files cataloged"
echo -e " ${COLOR_CYAN}${SYMBOL_BULLET}${COLOR_RESET} Last scan date"
echo -e "${COLOR_BOLD}${COLOR_WHITE}Cataloged Drives${COLOR_RESET} ${COLOR_WHITE}(${drive_count} registered)${COLOR_RESET}"
echo -e "${COLOR_CYAN}────────────────────────────────────────────────────────────────${COLOR_RESET}"

if [[ "$drive_count" -eq 0 ]]; then
echo -e " ${COLOR_YELLOW}${SYMBOL_INFO} No drives cataloged yet.${COLOR_RESET}"
echo -e " Use ${COLOR_WHITE}[1] Scan & Catalog Drive${COLOR_RESET} to add one."
echo ""
return 0
fi

local total_files=0

while IFS= read -r drive_json; do
local label type mount file_count last_scanned registered status_str status_color
label=$(echo "$drive_json" | jq -r '.label')
type=$(echo "$drive_json" | jq -r '.type')
mount=$(echo "$drive_json" | jq -r '.mount_point')
file_count=$(echo "$drive_json" | jq -r '.file_count')
last_scanned=$(echo "$drive_json"| jq -r '.last_scanned // "Never"')
registered=$(echo "$drive_json" | jq -r '.registered_at' | cut -c1-10)

[[ "$last_scanned" != "Never" ]] && last_scanned="${last_scanned:0:10}"

if [[ -d "$mount" ]] && df "$mount" >/dev/null 2>&1; then
status_str="Online "; status_color="${COLOR_BRIGHT_GREEN}"
else
status_str="Offline"; status_color="${COLOR_RED}"
fi

total_files=$(( total_files + file_count ))

printf " ${status_color}${SYMBOL_BULLET}${COLOR_RESET} ${COLOR_BOLD}${COLOR_WHITE}%-20s${COLOR_RESET}" "$label"
printf " ${status_color}[%s]${COLOR_RESET}" "$status_str"
printf " %-8s" "$type"
printf " ${COLOR_CYAN}%5s files${COLOR_RESET}" "$file_count"
printf " Scanned: ${COLOR_WHITE}%s${COLOR_RESET}" "$last_scanned"
printf " Added: ${COLOR_WHITE}%s${COLOR_RESET}\n" "$registered"
echo -e " ${COLOR_WHITE}${mount}${COLOR_RESET}"
done < <(jq -c '.drives[]' "$CATALOG_DRIVES_DB" 2>/dev/null)

echo -e "${COLOR_CYAN}────────────────────────────────────────────────────────────────${COLOR_RESET}"
echo -e " ${COLOR_WHITE}Total files cataloged: ${COLOR_BRIGHT_CYAN}${total_files}${COLOR_RESET}"
echo ""
return 1
}

# Search catalog for media files
# Note: This is a stub - full implementation not yet available
# Search catalog for media files by filename.
# Usage: search_catalog <term> [all|video|image|audio]
search_catalog() {
local search_term="$1"
local media_filter="${2:-all}"

log_warning "Catalog search feature is not yet implemented"
echo ""
echo -e "${COLOR_YELLOW}This feature would allow:${COLOR_RESET}"
echo -e " ${COLOR_CYAN}${SYMBOL_BULLET}${COLOR_RESET} Searching across all cataloged drives"
echo -e " ${COLOR_CYAN}${SYMBOL_BULLET}${COLOR_RESET} Finding files by name, metadata, or hash"
echo -e " ${COLOR_CYAN}${SYMBOL_BULLET}${COLOR_RESET} Filtering by media type (video, image, audio)"
echo -e " ${COLOR_CYAN}${SYMBOL_BULLET}${COLOR_RESET} Showing file location even if drive is offline"
echo ""
echo -e "${COLOR_WHITE}Search term: ${COLOR_RESET}$search_term"
echo -e "${COLOR_WHITE}Filter: ${COLOR_RESET}$media_filter"
if ! command -v jq >/dev/null 2>&1; then
log_error "jq is required. Install with: sudo apt-get install jq"
return 1
fi

if [[ -z "$search_term" ]]; then
log_error "Usage: search_catalog <term> [all|video|image|audio]"
return 1
fi

init_catalog_db

local results count
results=$(jq -c \
--arg term "${search_term,,}" \
--arg filter "$media_filter" \
'[.videos[] |
select((.filename | ascii_downcase) | contains($term)) |
select($filter == "all" or .media_type == $filter)]' \
"$CATALOG_DB" 2>/dev/null)

if ! echo "$results" | jq empty 2>/dev/null; then
log_error "Catalog may be corrupt — run a scan to rebuild it"
return 1
fi

count=$(echo "$results" | jq 'length')

echo ""
return 1
echo -e "${COLOR_BOLD}${COLOR_WHITE}Results for \"${search_term}\"${COLOR_RESET} ${COLOR_CYAN}(${count} found)${COLOR_RESET}"
echo -e "${COLOR_CYAN}────────────────────────────────────────────────────────────────${COLOR_RESET}"

if [[ "$count" -eq 0 ]]; then
echo -e " ${COLOR_YELLOW}No matches found.${COLOR_RESET}"
echo ""
return 0
fi

echo "$results" | jq -r '.[] | [.filename, .path, .media_type, (.file_size | tostring)] | @tsv' | \
while IFS=$'\t' read -r filename path media_type size; do
local size_mb=$(( size / 1048576 ))
local online_tag
if [[ -f "$path" ]]; then
online_tag="${COLOR_BRIGHT_GREEN}[Online] ${COLOR_RESET}"
else
online_tag="${COLOR_RED}[Offline]${COLOR_RESET}"
fi
echo -e " ${online_tag} ${COLOR_BOLD}${COLOR_WHITE}${filename}${COLOR_RESET} ${COLOR_CYAN}${media_type}${COLOR_RESET} ${size_mb}MB"
echo -e " ${COLOR_WHITE}${path}${COLOR_RESET}"
echo ""
done

return 0
}

################################################################################
Expand Down
Loading