mirror of
https://github.com/kuhyx/scripts.git
synced 2026-07-04 15:23:11 +02:00
feat: scripts for converting mp4 to webm and inverse
This commit is contained in:
parent
ff5c54895b
commit
316352fd10
194
scripts/utils/to_mp4.sh
Executable file
194
scripts/utils/to_mp4.sh
Executable file
@ -0,0 +1,194 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# to_mp4.sh
|
||||
#
|
||||
# Convert video files (non-mp4) to mp4 format using ffmpeg.
|
||||
# Accepts either a single video file or a directory (will recurse into subdirectories).
|
||||
|
||||
# Video extensions to search for (excluding mp4 since that's our target)
|
||||
VIDEO_EXTENSIONS=("webm" "mkv" "avi" "mov" "wmv" "flv" "m4v" "mpg" "mpeg" "3gp" "ogv" "ts" "mts" "m2ts" "vob" "asf" "rm" "rmvb" "divx" "f4v")
|
||||
|
||||
# Conversion settings
|
||||
CRF=23 # Quality (0-51, lower = better quality, 18-28 is reasonable for H.264)
|
||||
PRESET="medium" # Encoding speed: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow
|
||||
DELETE_ORIGINAL=false
|
||||
|
||||
log() {
|
||||
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage:
|
||||
$(basename "$0") [OPTIONS] PATH
|
||||
|
||||
Convert video files to mp4 format using ffmpeg.
|
||||
PATH can be a single video file or a directory (will recurse into subdirectories).
|
||||
|
||||
Options:
|
||||
-c CRF Quality level 0-51 (default: 23, lower = better quality)
|
||||
-p PRESET Encoding preset (default: medium)
|
||||
Options: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow
|
||||
-d Delete original file after successful conversion
|
||||
-h Show this help
|
||||
|
||||
Examples:
|
||||
$(basename "$0") video.webm
|
||||
$(basename "$0") /path/to/videos/
|
||||
$(basename "$0") -c 20 -d /path/to/videos/
|
||||
$(basename "$0") -p slow -c 18 movie.mkv
|
||||
EOF
|
||||
}
|
||||
|
||||
ensure_ffmpeg() {
|
||||
if ! command -v ffmpeg >/dev/null 2>&1; then
|
||||
echo "Error: 'ffmpeg' is not installed or not in PATH." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
is_video_file() {
|
||||
local file="$1"
|
||||
local ext="${file##*.}"
|
||||
ext="${ext,,}" # lowercase
|
||||
|
||||
for video_ext in "${VIDEO_EXTENSIONS[@]}"; do
|
||||
if [[ "$ext" == "${video_ext,,}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
convert_to_mp4() {
|
||||
local input_file="$1"
|
||||
local output_file="${input_file%.*}.mp4"
|
||||
|
||||
# Skip if output already exists
|
||||
if [[ -f "$output_file" ]]; then
|
||||
log "Skipping '$input_file': output '$output_file' already exists"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "Converting '$input_file' -> '$output_file'"
|
||||
|
||||
# Use H.264 codec for video and AAC for audio (maximum compatibility)
|
||||
if ffmpeg -hide_banner -loglevel warning -i "$input_file" \
|
||||
-c:v libx264 -crf "$CRF" -preset "$PRESET" \
|
||||
-c:a aac -b:a 192k \
|
||||
-movflags +faststart \
|
||||
"$output_file"; then
|
||||
log "Successfully converted '$input_file'"
|
||||
|
||||
if [[ "$DELETE_ORIGINAL" == true ]]; then
|
||||
log "Deleting original: '$input_file'"
|
||||
rm "$input_file"
|
||||
fi
|
||||
else
|
||||
log "Error converting '$input_file'"
|
||||
# Remove partial output file if it exists
|
||||
[[ -f "$output_file" ]] && rm "$output_file"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
process_directory() {
|
||||
local dir="$1"
|
||||
local count=0
|
||||
local failed=0
|
||||
|
||||
log "Searching for video files in '$dir'..."
|
||||
|
||||
# Find all video files (case-insensitive)
|
||||
while IFS= read -r -d '' file; do
|
||||
# Skip mp4 files
|
||||
if [[ "${file,,}" == *.mp4 ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
((count++)) || true
|
||||
if ! convert_to_mp4 "$file"; then
|
||||
((failed++)) || true
|
||||
fi
|
||||
done < <(find "$dir" -type f \( -iname "*.webm" -o -iname "*.mkv" -o -iname "*.avi" -o -iname "*.mov" \
|
||||
-o -iname "*.wmv" -o -iname "*.flv" -o -iname "*.m4v" -o -iname "*.mpg" -o -iname "*.mpeg" \
|
||||
-o -iname "*.3gp" -o -iname "*.ogv" -o -iname "*.ts" -o -iname "*.mts" -o -iname "*.m2ts" \
|
||||
-o -iname "*.vob" -o -iname "*.asf" -o -iname "*.rm" -o -iname "*.rmvb" -o -iname "*.divx" \
|
||||
-o -iname "*.f4v" \) -print0 2>/dev/null)
|
||||
|
||||
log "Processed $count video file(s), $failed failed"
|
||||
|
||||
if [[ $count -eq 0 ]]; then
|
||||
log "No video files found in '$dir'"
|
||||
fi
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
while getopts ":c:p:dh" opt; do
|
||||
case "$opt" in
|
||||
c) CRF="$OPTARG" ;;
|
||||
p) PRESET="$OPTARG" ;;
|
||||
d) DELETE_ORIGINAL=true ;;
|
||||
h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
:)
|
||||
echo "Error: Option -$OPTARG requires an argument." >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
\?)
|
||||
echo "Error: Invalid option -$OPTARG" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND - 1))
|
||||
|
||||
if [[ $# -lt 1 ]]; then
|
||||
echo "Error: No path specified." >&2
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TARGET_PATH="$1"
|
||||
}
|
||||
|
||||
main() {
|
||||
ensure_ffmpeg
|
||||
parse_args "$@"
|
||||
|
||||
if [[ ! -e "$TARGET_PATH" ]]; then
|
||||
echo "Error: Path '$TARGET_PATH' does not exist." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -f "$TARGET_PATH" ]]; then
|
||||
# Single file
|
||||
if [[ "${TARGET_PATH,,}" == *.mp4 ]]; then
|
||||
log "File '$TARGET_PATH' is already in mp4 format, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if is_video_file "$TARGET_PATH"; then
|
||||
convert_to_mp4 "$TARGET_PATH"
|
||||
else
|
||||
echo "Error: '$TARGET_PATH' is not a recognized video file." >&2
|
||||
exit 1
|
||||
fi
|
||||
elif [[ -d "$TARGET_PATH" ]]; then
|
||||
# Directory
|
||||
process_directory "$TARGET_PATH"
|
||||
else
|
||||
echo "Error: '$TARGET_PATH' is neither a file nor a directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Done!"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
206
scripts/utils/to_webm.sh
Executable file
206
scripts/utils/to_webm.sh
Executable file
@ -0,0 +1,206 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# to_webm.sh
|
||||
#
|
||||
# Convert video files (non-webm) to webm format using ffmpeg.
|
||||
# Accepts either a single video file or a directory (will recurse into subdirectories).
|
||||
|
||||
# Video extensions to search for (excluding webm since that's our target)
|
||||
VIDEO_EXTENSIONS=("mp4" "mkv" "avi" "mov" "wmv" "flv" "m4v" "mpg" "mpeg" "3gp" "ogv" "ts" "mts" "m2ts" "vob" "asf" "rm" "rmvb" "divx" "f4v")
|
||||
|
||||
# Conversion settings
|
||||
CRF=30 # Quality (0-63, lower = better quality, 23-30 is reasonable)
|
||||
PRESET="medium" # Encoding speed: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow
|
||||
DELETE_ORIGINAL=false
|
||||
|
||||
log() {
|
||||
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage:
|
||||
$(basename "$0") [OPTIONS] PATH
|
||||
|
||||
Convert video files to webm format using ffmpeg.
|
||||
PATH can be a single video file or a directory (will recurse into subdirectories).
|
||||
|
||||
Options:
|
||||
-c CRF Quality level 0-63 (default: 30, lower = better quality)
|
||||
-p PRESET Encoding preset (default: medium)
|
||||
Options: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow
|
||||
-d Delete original file after successful conversion
|
||||
-h Show this help
|
||||
|
||||
Examples:
|
||||
$(basename "$0") video.mp4
|
||||
$(basename "$0") /path/to/videos/
|
||||
$(basename "$0") -c 25 -d /path/to/videos/
|
||||
$(basename "$0") -p slow -c 20 movie.mkv
|
||||
EOF
|
||||
}
|
||||
|
||||
ensure_ffmpeg() {
|
||||
if ! command -v ffmpeg >/dev/null 2>&1; then
|
||||
echo "Error: 'ffmpeg' is not installed or not in PATH." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
build_extension_pattern() {
|
||||
# Build a find pattern for video extensions
|
||||
local pattern=""
|
||||
for ext in "${VIDEO_EXTENSIONS[@]}"; do
|
||||
if [[ -n "$pattern" ]]; then
|
||||
pattern="$pattern -o"
|
||||
fi
|
||||
pattern="$pattern -iname *.$ext"
|
||||
done
|
||||
echo "$pattern"
|
||||
}
|
||||
|
||||
is_video_file() {
|
||||
local file="$1"
|
||||
local ext="${file##*.}"
|
||||
ext="${ext,,}" # lowercase
|
||||
|
||||
for video_ext in "${VIDEO_EXTENSIONS[@]}"; do
|
||||
if [[ "$ext" == "${video_ext,,}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
convert_to_webm() {
|
||||
local input_file="$1"
|
||||
local output_file="${input_file%.*}.webm"
|
||||
|
||||
# Skip if output already exists
|
||||
if [[ -f "$output_file" ]]; then
|
||||
log "Skipping '$input_file': output '$output_file' already exists"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "Converting '$input_file' -> '$output_file'"
|
||||
|
||||
# Use VP9 codec for video and Opus for audio (good quality, wide compatibility)
|
||||
if ffmpeg -hide_banner -loglevel warning -i "$input_file" \
|
||||
-c:v libvpx-vp9 -crf "$CRF" -b:v 0 \
|
||||
-c:a libopus -b:a 128k \
|
||||
-preset "$PRESET" \
|
||||
"$output_file"; then
|
||||
log "Successfully converted '$input_file'"
|
||||
|
||||
if [[ "$DELETE_ORIGINAL" == true ]]; then
|
||||
log "Deleting original: '$input_file'"
|
||||
rm "$input_file"
|
||||
fi
|
||||
else
|
||||
log "Error converting '$input_file'"
|
||||
# Remove partial output file if it exists
|
||||
[[ -f "$output_file" ]] && rm "$output_file"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
process_directory() {
|
||||
local dir="$1"
|
||||
local count=0
|
||||
local failed=0
|
||||
|
||||
log "Searching for video files in '$dir'..."
|
||||
|
||||
# Find all video files (case-insensitive)
|
||||
while IFS= read -r -d '' file; do
|
||||
# Skip webm files
|
||||
if [[ "${file,,}" == *.webm ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
((count++)) || true
|
||||
if ! convert_to_webm "$file"; then
|
||||
((failed++)) || true
|
||||
fi
|
||||
done < <(find "$dir" -type f \( -iname "*.mp4" -o -iname "*.mkv" -o -iname "*.avi" -o -iname "*.mov" \
|
||||
-o -iname "*.wmv" -o -iname "*.flv" -o -iname "*.m4v" -o -iname "*.mpg" -o -iname "*.mpeg" \
|
||||
-o -iname "*.3gp" -o -iname "*.ogv" -o -iname "*.ts" -o -iname "*.mts" -o -iname "*.m2ts" \
|
||||
-o -iname "*.vob" -o -iname "*.asf" -o -iname "*.rm" -o -iname "*.rmvb" -o -iname "*.divx" \
|
||||
-o -iname "*.f4v" \) -print0 2>/dev/null)
|
||||
|
||||
log "Processed $count video file(s), $failed failed"
|
||||
|
||||
if [[ $count -eq 0 ]]; then
|
||||
log "No video files found in '$dir'"
|
||||
fi
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
while getopts ":c:p:dh" opt; do
|
||||
case "$opt" in
|
||||
c) CRF="$OPTARG" ;;
|
||||
p) PRESET="$OPTARG" ;;
|
||||
d) DELETE_ORIGINAL=true ;;
|
||||
h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
:)
|
||||
echo "Error: Option -$OPTARG requires an argument." >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
\?)
|
||||
echo "Error: Invalid option -$OPTARG" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND - 1))
|
||||
|
||||
if [[ $# -lt 1 ]]; then
|
||||
echo "Error: No path specified." >&2
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TARGET_PATH="$1"
|
||||
}
|
||||
|
||||
main() {
|
||||
ensure_ffmpeg
|
||||
parse_args "$@"
|
||||
|
||||
if [[ ! -e "$TARGET_PATH" ]]; then
|
||||
echo "Error: Path '$TARGET_PATH' does not exist." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -f "$TARGET_PATH" ]]; then
|
||||
# Single file
|
||||
if [[ "${TARGET_PATH,,}" == *.webm ]]; then
|
||||
log "File '$TARGET_PATH' is already in webm format, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if is_video_file "$TARGET_PATH"; then
|
||||
convert_to_webm "$TARGET_PATH"
|
||||
else
|
||||
echo "Error: '$TARGET_PATH' is not a recognized video file." >&2
|
||||
exit 1
|
||||
fi
|
||||
elif [[ -d "$TARGET_PATH" ]]; then
|
||||
# Directory
|
||||
process_directory "$TARGET_PATH"
|
||||
else
|
||||
echo "Error: '$TARGET_PATH' is neither a file nor a directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Done!"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Loading…
Reference in New Issue
Block a user