generated from nhcarrigan/template
298 lines
8.7 KiB
Bash
Executable File
298 lines
8.7 KiB
Bash
Executable File
#!/bin/bash
|
|
# Pull files from Android device via ADB
|
|
# Author: Naomi Carrigan & Hikari 💕
|
|
|
|
set -euo pipefail
|
|
|
|
# Color codes for pretty output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
BLUE='\033[0;34m'
|
|
YELLOW='\033[1;33m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Function to display usage
|
|
usage() {
|
|
echo -e "${BLUE}Usage: $0 [android_source] [local_destination]${NC}"
|
|
echo -e "${YELLOW}If no arguments provided, interactive mode will start${NC}"
|
|
echo ""
|
|
echo "Common Android paths:"
|
|
echo " /sdcard/Download/ - Downloads folder"
|
|
echo " /sdcard/DCIM/ - Camera folder"
|
|
echo " /sdcard/Pictures/ - Pictures folder"
|
|
echo " /sdcard/WhatsApp/ - WhatsApp media"
|
|
echo " /sdcard/Screenshots/ - Screenshots"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " $0 /sdcard/DCIM/Camera/ ~/Pictures/phone-backup/"
|
|
echo " $0 /sdcard/Download/document.pdf ~/Downloads/"
|
|
echo " $0"
|
|
exit 1
|
|
}
|
|
|
|
# Function to check if ADB is installed and device is connected
|
|
check_adb() {
|
|
if ! command -v adb &> /dev/null; then
|
|
echo -e "${RED}❌ Error: ADB is not installed or not in PATH${NC}"
|
|
echo "Please install Android Debug Bridge (ADB) first"
|
|
exit 1
|
|
fi
|
|
|
|
# Check if device is connected
|
|
if ! adb devices | grep -q "device$"; then
|
|
echo -e "${RED}❌ Error: No Android device connected${NC}"
|
|
echo "Please connect your device and enable USB debugging"
|
|
echo ""
|
|
echo "Current devices:"
|
|
adb devices
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Function to browse Android filesystem
|
|
browse_android_fs() {
|
|
local current_path="${1:-/sdcard/}"
|
|
|
|
while true; do
|
|
echo -e "${CYAN}📱 Current path: $current_path${NC}"
|
|
echo ""
|
|
|
|
# List contents
|
|
echo -e "${YELLOW}Contents:${NC}"
|
|
local items=$(adb shell "ls -la '$current_path' 2>/dev/null" | tail -n +2 | awk '{print $NF}' | grep -v "^\.$" | grep -v "^\.\.$")
|
|
|
|
local i=1
|
|
local -a entries=()
|
|
|
|
# Add parent directory option if not at root
|
|
if [[ "$current_path" != "/" ]]; then
|
|
echo "0) .. (Go up)"
|
|
entries[0]=".."
|
|
fi
|
|
|
|
# List items
|
|
while IFS= read -r item; do
|
|
if [[ -n "$item" ]]; then
|
|
# Check if directory
|
|
if adb shell "test -d '$current_path/$item' 2>/dev/null && echo 'dir'" | grep -q "dir"; then
|
|
echo "$i) $item/"
|
|
else
|
|
echo "$i) $item"
|
|
fi
|
|
entries[$i]="$item"
|
|
((i++))
|
|
fi
|
|
done <<< "$items"
|
|
|
|
echo ""
|
|
echo "s) Select this path"
|
|
echo "q) Quit"
|
|
echo ""
|
|
|
|
read -p "Enter choice: " choice
|
|
|
|
case $choice in
|
|
s|S)
|
|
echo "$current_path"
|
|
return 0
|
|
;;
|
|
q|Q)
|
|
return 1
|
|
;;
|
|
0)
|
|
if [[ "$current_path" != "/" ]]; then
|
|
current_path=$(dirname "$current_path")
|
|
fi
|
|
;;
|
|
[0-9]*)
|
|
if [[ -n "${entries[$choice]:-}" ]]; then
|
|
local selected="${entries[$choice]}"
|
|
local new_path="$current_path/$selected"
|
|
# Clean up path
|
|
new_path=$(echo "$new_path" | sed 's|//|/|g')
|
|
|
|
# Check if it's a directory
|
|
if adb shell "test -d '$new_path' 2>/dev/null && echo 'dir'" | grep -q "dir"; then
|
|
current_path="$new_path"
|
|
else
|
|
# It's a file, return it
|
|
echo "$new_path"
|
|
return 0
|
|
fi
|
|
else
|
|
echo -e "${RED}Invalid choice${NC}"
|
|
fi
|
|
;;
|
|
*)
|
|
echo -e "${RED}Invalid choice${NC}"
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
done
|
|
}
|
|
|
|
# Interactive mode
|
|
interactive_mode() {
|
|
echo -e "${BLUE}📲 ADB Pull - Interactive Mode${NC}"
|
|
echo ""
|
|
|
|
# Choose method
|
|
echo -e "${YELLOW}How would you like to select the source?${NC}"
|
|
echo "1) Browse Android filesystem"
|
|
echo "2) Enter path directly"
|
|
echo "3) Quick access to common folders"
|
|
echo ""
|
|
|
|
read -p "Select method (1-3): " method
|
|
|
|
case $method in
|
|
1)
|
|
# Browse mode
|
|
if source_path=$(browse_android_fs); then
|
|
echo -e "${GREEN}Selected: $source_path${NC}"
|
|
else
|
|
echo -e "${RED}❌ Cancelled${NC}"
|
|
exit 1
|
|
fi
|
|
;;
|
|
2)
|
|
# Direct path entry
|
|
read -p "Enter Android source path: " -e source_path
|
|
;;
|
|
3)
|
|
# Quick access
|
|
echo ""
|
|
echo -e "${YELLOW}Common locations:${NC}"
|
|
echo "1) /sdcard/DCIM/Camera/"
|
|
echo "2) /sdcard/Pictures/"
|
|
echo "3) /sdcard/Download/"
|
|
echo "4) /sdcard/WhatsApp/Media/"
|
|
echo "5) /sdcard/Screenshots/"
|
|
echo "6) /sdcard/Documents/"
|
|
echo "7) /sdcard/Music/"
|
|
echo ""
|
|
|
|
read -p "Select location (1-7): " quick_choice
|
|
|
|
case $quick_choice in
|
|
1) source_path="/sdcard/DCIM/Camera/" ;;
|
|
2) source_path="/sdcard/Pictures/" ;;
|
|
3) source_path="/sdcard/Download/" ;;
|
|
4) source_path="/sdcard/WhatsApp/Media/" ;;
|
|
5) source_path="/sdcard/Screenshots/" ;;
|
|
6) source_path="/sdcard/Documents/" ;;
|
|
7) source_path="/sdcard/Music/" ;;
|
|
*)
|
|
echo -e "${RED}❌ Invalid choice${NC}"
|
|
exit 1
|
|
;;
|
|
esac
|
|
;;
|
|
*)
|
|
echo -e "${RED}❌ Invalid choice${NC}"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# Get destination
|
|
echo ""
|
|
read -p "Enter local destination path (default: current directory): " -e dest_path
|
|
dest_path="${dest_path:-.}"
|
|
dest_path="${dest_path/#\~/$HOME}"
|
|
|
|
# Pull the file/directory
|
|
pull_from_android "$source_path" "$dest_path"
|
|
}
|
|
|
|
# Function to pull files from Android
|
|
pull_from_android() {
|
|
local source="$1"
|
|
local dest="$2"
|
|
|
|
# Validate source exists
|
|
if ! adb shell "test -e '$source' 2>/dev/null && echo 'exists'" | grep -q "exists"; then
|
|
echo -e "${RED}❌ Error: Source '$source' does not exist on device${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# Create destination directory if needed
|
|
if [[ ! -d "$dest" ]]; then
|
|
mkdir -p "$dest"
|
|
fi
|
|
|
|
echo -e "${BLUE}📥 Pulling from Android device...${NC}"
|
|
echo "Source: $source"
|
|
echo "Destination: $dest"
|
|
echo ""
|
|
|
|
# Check if source is directory
|
|
if adb shell "test -d '$source' 2>/dev/null && echo 'dir'" | grep -q "dir"; then
|
|
echo -e "${YELLOW}Pulling directory...${NC}"
|
|
|
|
# Count files for progress
|
|
local file_count=$(adb shell "find '$source' -type f 2>/dev/null | wc -l" | tr -d '\r\n')
|
|
echo "Found $file_count files to pull"
|
|
echo ""
|
|
|
|
if adb pull "$source" "$dest"; then
|
|
echo -e "${GREEN}✅ Directory pulled successfully!${NC}"
|
|
|
|
# Show summary
|
|
local pulled_dir="$dest/$(basename "$source")"
|
|
if [[ -d "$pulled_dir" ]]; then
|
|
echo ""
|
|
echo -e "${BLUE}📊 Summary:${NC}"
|
|
echo "Location: $pulled_dir"
|
|
echo "Files: $(find "$pulled_dir" -type f | wc -l)"
|
|
echo "Total size: $(du -sh "$pulled_dir" | cut -f1)"
|
|
fi
|
|
else
|
|
echo -e "${RED}❌ Failed to pull directory${NC}"
|
|
exit 1
|
|
fi
|
|
else
|
|
# Single file
|
|
if adb pull "$source" "$dest"; then
|
|
echo -e "${GREEN}✅ File pulled successfully!${NC}"
|
|
|
|
# Show file info
|
|
local filename=$(basename "$source")
|
|
local full_path="$dest/$filename"
|
|
|
|
echo ""
|
|
echo -e "${BLUE}📄 File info:${NC}"
|
|
ls -lh "$full_path"
|
|
else
|
|
echo -e "${RED}❌ Failed to pull file${NC}"
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Main script
|
|
main() {
|
|
check_adb
|
|
|
|
if [[ $# -eq 0 ]]; then
|
|
# No arguments, run interactive mode
|
|
interactive_mode
|
|
elif [[ $# -eq 1 ]]; then
|
|
if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
|
|
usage
|
|
else
|
|
echo -e "${RED}❌ Error: Missing destination path${NC}"
|
|
usage
|
|
fi
|
|
elif [[ $# -eq 2 ]]; then
|
|
# Arguments provided
|
|
dest_path="${2/#\~/$HOME}"
|
|
pull_from_android "$1" "$dest_path"
|
|
else
|
|
echo -e "${RED}❌ Error: Too many arguments${NC}"
|
|
usage
|
|
fi
|
|
}
|
|
|
|
main "$@" |