#!/bin/bash # Bootanimation creator script by github.com/rhythmcache # Telegram @rhythmcache TMP_DIR="$(pwd)/bootanim" rm -rf "$TMP_DIR" mkdir -p "$TMP_DIR/frames" "$TMP_DIR/result" desc_file="$TMP_DIR/result/desc.txt" output_zip="./bootanimation.zip" WHITE='\033[1;37m' BRIGHT_YELLOW='\033[1;33m' BRIGHT_RED='\033[1;31m' BRIGHT_CYAN='\033[1;36m' GREEN='\033[0;32m' NC='\033[0m' rm -f downloaded_video.* check_termux_environment() { if echo "$PREFIX" | grep -q "com.termux"; then echo -e "${BRIGHT_RED}Termux Detected${NC}" echo -e "${BRIGHT_RED}Checking Internal Storage Access${NC}" if [ -r /storage/emulated/0/ ] && [ -w /storage/emulated/0/ ]; then echo -e "${GREEN}Internal Storage Is Accessible..${NC}" else echo "Internal Storage is not fully accessible. Setting up storage..." termux-setup-storage fi fi } check_termux_environment echo -e "${BRIGHT_CYAN}" echo "░█▀▄░█▀█░█▀█░▀█▀░█▀█░█▀█░▀█▀░█▄█░█▀█░▀█▀░▀█▀░█▀█░█▀█" echo "░█▀▄░█░█░█░█░░█░░█▀█░█░█░░█░░█░█░█▀█░░█░░░█░░█░█░█░█" echo "░▀▀░░▀▀▀░▀▀▀░░▀░░▀░▀░▀░▀░▀▀▀░▀░▀░▀░▀░░▀░░▀▀▀░▀▀▀░▀░▀" echo -e "${NC}" echo -e "${BRIGHT_YELLOW}" echo "░█▀▀░█▀▄░█▀▀░█▀█░▀█▀░█▀█░█▀▄" echo "░█░░░█▀▄░█▀▀░█▀█░░█░░█░█░█▀▄" echo "░▀▀▀░▀░▀░▀▀▀░▀░▀░░▀░░▀▀▀░▀░▀" echo -e "${NC}" sleep 1 echo -e "${BRIGHT_CYAN}========================================${NC}" echo -e "${WHITE} by rhythmcache ${NC}" echo -e "${BRIGHT_CYAN}========================================${NC}" sleep 2 install_package() { local package="$1" if command -v pkg &> /dev/null; then echo "Termux detected" if [ "$package" == "yt-dlp" ]; then if ! command -v yt-dlp &> /dev/null; then echo "yt-dlp not found. Installing via pip..." pkg install python openssl-tool -y pip install yt-dlp fi else pkg update && pkg install -y "$package" fi elif command -v dnf &> /dev/null; then sudo dnf install -y "$package" elif command -v pacman &> /dev/null; then sudo pacman -Sy --noconfirm "$package" elif command -v zypper &> /dev/null; then sudo zypper install -y "$package" elif command -v yum &> /dev/null; then sudo yum install -y "$package" elif command -v apk &> /dev/null; then sudo apk add "$package" elif command -v apt &> /dev/null; then # Other Linux distros sudo apt update && sudo apt install -y "$package" else echo "Error: Unsupported package manager. Please install $package manually." exit 1 fi } if ! command -v ffmpeg &> /dev/null; then echo "ffmpeg not found. Installing..." install_package "ffmpeg" || { echo "Failed to install ffmpeg."; exit 1; } fi if ! command -v bc &> /dev/null; then echo "bc not found. Installing..." install_package "bc" || { echo "Failed to install bc. Please install it manually."; exit 1; } fi if ! command -v zip &> /dev/null; then echo "zip not found. Installing..." install_package "zip" || { echo "Failed to install zip."; exit 1; } fi get_video_properties() { local video_file="$1" width=$(ffprobe -v error -select_streams v:0 -show_entries stream=width -of csv=p=0 "$video_file") height=$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of csv=p=0 "$video_file") frame_rate=$(ffprobe -v error -select_streams v:0 -show_entries stream=r_frame_rate -of csv=p=0 "$video_file") if [[ -z "$width" || -z "$height" || -z "$frame_rate" ]]; then echo "Error: Could not retrieve video properties (width, height, or frame_rate)." >&2 return 1 fi if [[ "$frame_rate" == */* ]]; then numerator=$(echo "$frame_rate" | cut -d'/' -f1) denominator=$(echo "$frame_rate" | cut -d'/' -f2) fps=$(echo "($numerator + $denominator/2) / $denominator" | bc) else fps=$frame_rate fi return 0 } ######################## extract_audio_blocks() { local video="$1" local output_dir="$TMP_DIR/audio" mkdir -p "$output_dir" get_video_properties "$video" if [[ -z "$fps" || "$fps" -eq 0 ]]; then echo "Error: Invalid frame rate (fps) detected: $fps" >&2 return 1 fi local has_audio has_audio=$(ffprobe -v error -show_entries stream=codec_type -select_streams a -of csv=p=0 "$video") if [[ -z "$has_audio" ]]; then echo "Warning: No audio stream found in the video. Skipping audio extraction " >&2 return 0 fi local frame_block_duration frame_block_duration=$(bc <<< "scale=2; 400 / $fps") local duration duration=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$video") if [[ -z "$duration" ]]; then echo "Error: Could not determine the duration of the video." >&2 return 1 fi local start_time=0 local part=0 while (( $(echo "$start_time < $duration" | bc -l) )); do local output_audio="$output_dir/audio${part}.wav" ffmpeg -y -i "$video" -ss "$start_time" -t "$frame_block_duration" -vn -acodec pcm_s16le -ar 44100 -ac 2 "$output_audio" 2>&1 | \ grep -i -e "audio" -e "wav" || { echo -e "Error: Failed to extract audio for block $part. " >&2 } start_time=$(bc <<< "$start_time + $frame_block_duration") part=$((part + 1)) done echo -e "${BRIGHT_CYAN} Audio extraction completed successfully. ${NC}" return 0 } ######################### echo -e "${GREEN}Choose video source:${NC}" echo "1. YouTube Video" echo "2. Local Video" echo -e "${BRIGHT_CYAN}" read -r -p "Enter your choice (1 or 2): " source_choice echo -e "${NC}" if [[ "$source_choice" == "1" ]]; then if ! command -v yt-dlp &> /dev/null; then echo "yt-dlp not found. Installing..." install_package "yt-dlp" || { echo "Failed to install yt-dlp. Plz Install it Manually. "; exit 1; } fi echo -e "${BRIGHT_YELLOW}" read -r -p "Enter YouTube video link: " yt_url echo -e "${NC}" # List available resolutions echo "Fetching available resolutions..." yt_dlp_info=$(yt-dlp -F "$yt_url") echo "Available resolutions (MP4 only):" yt_dlp_resolutions=$(echo "$yt_dlp_info" | grep -E '^[0-9]+ ' | grep -i "mp4" | awk '{print $1, $2, $3, $NF}') if [[ -z "$yt_dlp_resolutions" ]]; then echo "No MP4 formats available for this video." exit 1 fi echo "$yt_dlp_resolutions" echo -e "${BRIGHT_YELLOW}" read -r -p "Enter the format code for the desired resolution: " format_code echo -e "${NC}" # Download video yt_dlp_output="downloaded_video.mp4" yt-dlp -f "$format_code" -o "$yt_dlp_output" "$yt_url" || { echo "Error downloading video from YouTube." exit 1 } video="$yt_dlp_output" elif [[ "$source_choice" == "2" ]]; then # Local video selected echo -e "${BRIGHT_YELLOW} Enter video path (e.g. /path/to/video.mp4) ${NC}" echo -e "${BRIGHT_YELLOW}" read -r -p "=> PATH: " video echo -e "${NC}" if [ ! -f "$video" ]; then echo "Error: Video file does not exist." exit 1 fi else echo "Invalid choice. Exiting." exit 1 fi echo -e "${BRIGHT_YELLOW}Choose configuration type to create bootanimation:${NC}" echo "1. Use Video's Default Resolution and FPS" echo "2. Custom configuration" echo -e "${BRIGHT_CYAN}" read -r -p "Enter your choice (1 or 2): " config_choice echo -e "${NC}" if [[ "$config_choice" == "1" ]]; then get_video_properties "$video" echo "Using video properties:" echo "Resolution: ${width}x${height}" echo "FPS: $fps" # Prompt for audio inclusion echo -e "${BRIGHT_YELLOW}" read -r -p "Do you want to include audio with the bootanimation? (y/n): " include_audio echo -e "${NC}" if [[ "$include_audio" =~ ^[Yy]$ ]]; then echo "Audio will be included in the bootanimation." echo -e "${BRIGHT_RED} => ⚠️ Warning: Not all ROMs/devices support audio in bootanimations. Proceed at your own risk. ${NC}" extract_audio_blocks "$video" else echo "Audio will not be included." fi # Loop prompt for default configuration echo -e "${BRIGHT_YELLOW}Select BootAnimation Behaviour:${NC}" sleep 1 echo " - 1. Bootanimation should stop if the device completes boot successfully. - 2. Bootanimation should play its full length, no matter what. - 3. Keep looping the animation until the device boots. => If your video is too short or if it is a GIF, choose 3. => If you are unsure, choose 1. " echo -e "${BRIGHT_YELLOW}" read -r -p "Select Your Desired Option (1, 2, or 3): " loop_option echo -e "${NC}" if [[ "$loop_option" != "1" && "$loop_option" != "2" && "$loop_option" != "3" ]]; then echo "Error: Invalid option selected. Please select 1, 2, or 3." exit 1 fi else # Custom configuration echo "Custom configuration selected. Audio will be disabled by default." # Resolution Input echo -e "${BRIGHT_YELLOW}" read -r -p "Enter output resolution (e.g., 1080x1920): " resolution echo -e "${NC}" # Validate Resolution if [[ ! "$resolution" =~ ^[0-9]+x[0-9]+$ ]]; then echo -e "{BRIGHT_RED}Error: Invalid resolution format. Please use the format 'widthxheight' (e.g., 1080x1920). {NC}" exit 1 fi width=$(echo "$resolution" | cut -d'x' -f1) height=$(echo "$resolution" | cut -d'x' -f2) # Frame Rate Input echo -e "${BRIGHT_YELLOW}" read -r -p "Enter frame rate you want to put in bootanimation: " fps echo -e "${NC}" # Loop Option Prompt echo -e "${BRIGHT_YELLOW}Select BootAnimation Behaviour:${NC}" sleep 1 echo " - 1. Bootanimation should stop if the device completes boot successfully. - 2. Bootanimation should play its full length, no matter what. - 3. Keep looping the animation until the device boots. => If your video is too short or if it is a GIF, choose '3'. => If you are unsure, choose 1." echo -e "${BRIGHT_YELLOW}" read -r -p "Select Your Desired Option (1, 2, or 3): " loop_option echo -e "${NC}" if [[ "$loop_option" != "1" && "$loop_option" != "2" && "$loop_option" != "3" ]]; then echo "Error: Invalid option selected. Please select 1, 2, or 3." exit 1 fi fi # Prompt the user for the background color code echo -e "${BRIGHT_CYAN}Select Background Color${NC}" echo -e "${BRIGHT_RED} Leave Empty If you are not Sure ${NC}" echo -e "${BRIGHT_YELLOW}" read -r -p "=> Enter Background Color Code (e.g #FFFFFF) :" BC echo -e "${NC}" if [[ -n "$BC" ]]; then if [[ ! "$BC" =~ ^#?([0-9A-Fa-f]{6}|[0-9A-Fa-f]{3})$ ]]; then echo -e "${BRIGHT_RED}Error: Invalid color code format.${NC}" exit 1 fi BC="#${BC#\#}" fi # Prompt for output path echo -e "${BRIGHT_YELLOW} Enter path to save the Magisk module (e.g., /path/to/module/name.zip) ${NC}" echo -e "${BRIGHT_YELLOW}" read -r -p "=> PATH: " output_path echo -e "${NC}" if [[ ! "${output_path}" =~ \.zip$ ]]; then output_path="${output_path%/}/CreatedMagiskModule.zip" fi echo -e "${NC}" sleep 1 echo -e "${BRIGHT_CYAN}========================================${NC}" echo -e "${WHITE} PROCESSING VIDEO ${NC}" echo -e "${BRIGHT_CYAN}========================================${NC}" # Generate frames with ffmpeg ffmpeg -i "$video" -vf "scale=${width}:${height}" "$TMP_DIR/frames/%06d.jpg" 2>&1 | \ grep --line-buffered -o 'frame=.*' | \ while IFS= read -r line; do echo "$line" done echo "Processing completed." # Count frames frame_count=$(find "$TMP_DIR/frames" -mindepth 1 -maxdepth 1 -type f | wc -l) if [ "$frame_count" -eq 0 ]; then echo "Error: No frames generated. Exiting." echo "If you are using Termux, make sure you grant storage permissions by running:" echo " 'termux-setup-storage' " echo "and ensure the video file is correct" exit 1 fi echo "Processed $frame_count frames." echo -e "${GREEN} Arranging Frames ${NC}" echo "$width $height $fps" > "$desc_file" # Maximum frames per part max_frames=400 part_index=0 frame_index=0 # Prepare initial part directory mkdir -p "$TMP_DIR/result/part$part_index" # Pack frames into parts for frame in "$TMP_DIR/frames"/*.jpg; do mv "$frame" "$TMP_DIR/result/part$part_index/" frame_index=$((frame_index + 1)) # If the maximum frames per part is reached, create a new part if [ "$frame_index" -ge "$max_frames" ]; then frame_index=0 part_index=$((part_index + 1)) mkdir -p "$TMP_DIR/result/part$part_index" fi done # audio if [[ "$include_audio" =~ ^[Yy]$ ]]; then echo "Including audio for each part..." audio_index=0 for part_dir in $(find "$TMP_DIR/result" -type d -name "part*" | sort -V); do audio_file="$TMP_DIR/audio/audio${audio_index}.wav" if [ -f "$audio_file" ]; then mv "$audio_file" "$part_dir/audio.wav" echo "Added audio${audio_index}.wav to $part_dir/audio.wav" else echo -e "${BRIGHT_RED} Warning: Expected audio file $audio_file not found. Video has no audio ? ${NC}" fi audio_index=$((audio_index + 1)) done else echo "Audio not selected. Skipping audio processing." fi # Create desc.txt and handle looping if [[ "$loop_option" == "1" ]]; then for i in $(seq 0 "$part_index"); do echo "p 1 0 part$i${BC:+ $BC}" >> "$desc_file" done elif [[ "$loop_option" == "2" ]]; then for i in $(seq 0 "$part_index"); do echo "c 1 0 part$i${BC:+ $BC}" >> "$desc_file" done else for i in $(seq 0 "$part_index"); do echo "c 0 0 part$i${BC:+ $BC}" >> "$desc_file" done fi sleep 1 echo -e "${BRIGHT_CYAN}=========================================${NC}" # Zip the bootanimation echo " => Creating bootanimation.zip..." if cd "$TMP_DIR/result" && zip -q -r -0 "$output_zip" ./*; then : else echo "Error creating zip file." exit 1 fi echo -e "${GREEN} => Animation Written Successfully ✅${NC}" #Writing Module echo -e "${BRIGHT_CYAN} => Writing Module${NC}" mkdir -p "./magisk_module/animation" mod="./magisk_module" mkdir -p "$mod/META-INF/com/google/android/" mkdir -p "$mod/webroot" # Write Customize.sh cat <<'EOF' > "$mod/customize.sh" # This Installer is a part of Bootanimation-Creator-Script # https://github.com/rhythmcache # rhythmcache.t.me ui_print "- This Module Was Created Using BootAnimation-Creator-Script" # Function to install bootanimation files install_bootanimation() { local target_dir="$1" mkdir -p "$MODPATH/$target_dir" if [ -f "/$target_dir/bootanimation.zip" ] && [ -f "/$target_dir/bootanimation-dark.zip" ]; then cp -f "$MODPATH/animation/bootanimation.zip" "$MODPATH/$target_dir/bootanimation.zip" cp -f "$MODPATH/animation/bootanimation.zip" "$MODPATH/$target_dir/bootanimation-dark.zip" echo "description=Bootanimations installed at $target_dir" >> "$MODPATH/module.prop" elif [ -f "/$target_dir/bootanimation.zip" ]; then cp -f "$MODPATH/animation/bootanimation.zip" "$MODPATH/$target_dir/bootanimation.zip" echo "description=Bootanimation installed at $target_dir" >> "$MODPATH/module.prop" else return 1 fi } if [ -f "/system/product/media/bootanimation.zip" ] || [ -f "/system/product/media/bootanimation-dark.zip" ]; then install_bootanimation "system/product/media" elif [ -f "/system/media/bootanimation.zip" ] || [ -f "/system/media/bootanimation-dark.zip" ]; then install_bootanimation "system/media" else ui_print "- Failed to install. Your device is not currently supported." abort fi ui_print "" ui_print "" set_perm_recursive "$MODPATH" 0 0 0755 0644 rm -rf "$MODPATH/animation" ui_print "- Installation Complete!" EOF # Create index.html ########### cat <<'EOF' > "$mod/webroot/index.html"
KernelSU API access is required. Grant access?