Skvělý program, doplněk k Resolvu, umožňující vytvářet proxy soubory na pozadí, bez zapojení hlavního programu. Jeho poměrně velkým omezením je však striktní pravidlo pro vytváření proxy adresářů. Ty jsou vždy umístěny v adresáři obsahujícím video soubory. Vždy tedy máme takovou strukturu:
/rootDir
-> /videoDir
-> /Proxy
Což je vlastně docela logické, ale život má své temné stránky. Za prvé, možná nechceme mít proxy na tomto disku, za druhé, prostě na něm není místo. Tak tomu bylo i v mém případě. Surové materiály, tedy zdrojové materiály, byly na externím SSD disku, poměrně velkém, protože měl 2 TB, ale byl plný. Zbývalo asi 50 MB. Přibližně 1400 souborů pro přírodopisný film. V několika různých formátech a rozlišeních. Nic mimořádného, pocházely z různých kamer, fotoaparátů, některé z telefonů. A protože jsem neměl možnost je zkopírovat na jiný, větší disk, aby se tam vešly i proxy, hledal jsem řešení. Kopírovat jen proto, abych vytvořil proxy, mi navíc nedávalo smysl.
Ale zpět k Blackmagic Proxy Generator, druhým trochu nepříjemným, alespoň pro mě, je výběr kodeků, ve kterých lze vytvářet cílové proxy. Bohužel je tam na výběr jen několik, možná logických, ale ne každému může tento výběr vyhovovat. To je ve zkratce geneze nápadu na vlastní řešení.
Dalším argumentem byla skutečnost, že generování proxy souborů samotným programem DaVinci Resolve sice umožňuje určit cílové umístění, ale zároveň blokuje program. A třešničkou na dortu byla skutečnost, že se mi DaVinci během generování několikrát jednoduše zhroutil.
V první fázi jsem prostě přišel na běžné logické řešení: vytvoření symlinků. Jednoduše řečeno, symbolické odkazy z jednoho adresáře do jiného adresáře (pro ty, kteří tento pojem neznají, něco jako „zkratky“ k souborům). Nejprve jsem napsal skript, který zachytával požadavky generátoru na vytvoření proxy adresáře, zachytával tento požadavek a vytvářel „za běhu“ symbolický odkaz. Zdánlivě to fungovalo, zdánlivě ne. Někdy byl generátor rychlejší než skript a první proxy soubor uložil podle svého algoritmu, jindy se podařilo požadavek zachytit a správně propojit. Takže to nebyla správná cesta. A vůbec, jaký má smysl zachytávat požadavky na vytvoření adresáře, když vím, jaké budou potřeba?
Dalším krokem tedy bylo vytvoření předem stanovených symlinků a cílových adresářů v souladu se strukturou, kterou očekává DaVinci Resolve. Abych to zkrátil, nakonec vznikl skript, který rekurzivně prochází katalog/disk zdrojových souborů, vytváří v cílovém umístění katalogy s názvy aktuálního katalogu a ve zdrojovém katalogu vytváří symlink s názvem „proxy“, který odkazuje právě na tento katalog. Je to trochu zamotané, ale nedokázal jsem to vysvětlit jednodušeji. Možná postupně, jak to funguje:
a tak dále, dokud nejsou vyčerpány všechny adresáře. V cílovém místě tedy máme ve skutečnosti zrcadlové kopie adresářů ze zdrojového disku, ale DaVinci Resolve nebo Blackmagic Proxy Generator je vidí jako správnou strukturu adresářů proxy. Samozřejmě by bylo možné vytvořit na cílovém disku/adresáři ještě správnější adresáře s původními názvy a teprve v nich adresáře proxy. Ale k čemu? To by bylo umění jen pro umění.
Je třeba upozornit na jednu nevýhodu: skript hledá všude, takže v případě struktury XDCAM, kde jsou soubory v podadresáři /XDROOT/Clip, vytvoří odkaz na adresář Clip. Totéž platí pro ještě idiotičtější strukturu BPAV, kde každý video soubor má svůj vlastní adresář. Nejlepší je jednoduše přesunout video soubory do správného/nadřazeného/vlastního adresáře. Není třeba zde vysvětlovat, kdo se setkal s těmito formáty záznamu, ten ví, o co jde.
Toto je obsah skriptu k vytvoření proxy struktury pro Resolve, který jsem nakonec po několika pokusech vymyslel (málem jsem smazal zdrojové soubory, ale mám zálohu. Což doporučuji každému).
Dokonce jsem přidal něco jako nápovědu, což normálně nedělám, a po několika měsících přemýšlím, jak se to sakra používalo :) Stačí zkopírovat textový obsah do souboru, například proxyDirCreator.sh, uložit ho někde na disk, přidělit mu práva k provedení a jednoduše používat. Samozřejmě to není program s úchvatně krásným grafickým rozhraním, ale skript pro spuštění v konzoli nebo terminálu. Možná není dokonalý, jako nic na této planetě, ale u mě funguje velmi dobře. A pokud vám něco nebude fungovat, vždy to můžete opravit.
Malá poznámka: pokud chcete používat Bash, musíte mít alespoň verzi 4.1, jinak se bude při zápisu logů zhroutit. Bohužel Apple kvůli typu licence Bash používá i v nejnovějších strojích verzi 3.2.57. Prý proto, že bash od verze 4.0 je pod licencí GPL v3, kterou Apple nějak nemá rád. Proč? Kdo ví. Když se neví, o co jde, jde asi o peníze :-) Je to trochu škoda, ale na druhou stranu: Zsh je velmi kompatibilní s Bash, prakticky vše funguje bez problémů. A určitě tak jednoduché skripty, jako ten pro vytváření proxy adresářů.
#!/bin/zsh
# --- help ---
usage() {
cat << EOF
Use: $0 [OPTION] SRC DST
Creates a directory structure on the DST, and proxy symlinks in the source directories
that point to these directories. Used to bypass BlackmagicProxyGenerator restrictions, where it is
not possible to specify the destination directory, which can be problematic in the case of a full source drive.
Thanks to this approach, you do not take up space on your source drive,
and all proxy directories point to your destination on another drive. And DaVinci Resolve works flawlessly.
--
The script is designed for Mac OSX or Linux. It may be possible to adapt it for Windows, but I am not familiar with that.
The script may contain errors. Use it at your own risk. If you notice any irregularities,
please let me know and I will correct them in my spare time. Enjoy using it, martvin.
admin @ projektika.group
OPION:
--clean Removes existing directories or proxy symlinks before creating new ones.
--exec Makes actual changes (default: dry-run mode)
--log [FILE] Saves the activity log. If FILE is not specified, a file with the date is created.
--help This page
EXAMPLE:
$0 /Volumes/SRC /Volumes/DST
$0 --exec /Volumes/SRC /Volumes/DST
$0 --clean --exec --log SRC DST
$0 --exec --log /path/to/my_proxy.log SRC DST
ATTENTION:
- Dry-run mode is the default for safety.
- Symlinks are only created if they do not exist (unless --clean is used).
- Source directories are not modified except for the proxy directory.
EOF
exit 1
}
# --- default settings ---
CLEAN=0
EXEC=0
DRY_RUN=1
LOG_FILE=""
# --- args parse ---
while [[ $# -gt 0 ]]; do
case $1 in
--clean)
CLEAN=1
shift
;;
--exec)
EXEC=1
DRY_RUN=0
shift
;;
--log)
if [[ -n "$2" && ! "$2" =~ ^--.* ]]; then
potential_path="$2"
shift 2
else
potential_path="proxy_log_$(date %Y%m%d-%H%M%S).log"
shift
fi
potential_path="${potential_path%/}"
if [[ -d "$potential_path" ]]; then
# if directory, create log file inside it
LOG_FILE="$potential_path/proxy_log_$(date %Y%m%d-%H%M%S).log"
elif [[ -n "$potential_path" ]]; then
# if file path, check if directory exists
log_dir="$(dirname "$potential_path")"
if [[ -d "$log_dir" ]]; then
LOG_FILE="$potential_path"
else
echo -e "\033[1;31mError: dir not exist: $log_dir\033[0m"
exit 1
fi
else
echo -e "\033[1;31mError: wrong path after --log\033[0m"
exit 1
fi
;;
--help)
usage
;;
*)
if [[ -z "$SRC" ]]; then
SRC="$1"
elif [[ -z "$DST" ]]; then
DST="$1"
else
echo "Error: Too many arguments."
usage
fi
shift
;;
esac
done
# --- Checking required arguments ---
if [[ -z "$SRC" || -z "$DST" ]]; then
echo "\033[1;31mError: The required SRC or DST arguments are missing, or the --log option was used without a path to the log directory.\033[0m"
usage
fi
# --- path validate ---
if [[ ! -d "$SRC" ]]; then
echo "Error: Source directory does not exist: $SRC"
exit 1
fi
# --- logs ---
LOG_FD=1 # default stdout
if [[ -n "$LOG_FILE" ]]; then
# dir exists
log_dir=$(dirname "$LOG_FILE")
if [[ ! -d "$log_dir" ]]; then
echo "Error: Log directory not exist: $log_dir"
exit 1
fi
# log file
exec {LOG_FD}>>"$LOG_FILE"
echo "=== Log start: $(date) ===" >&$LOG_FD
echo "Mode: $( [[ $DRY_RUN -eq 1 ]] && echo dry-run || echo exec )"
echo "SRC: $SRC"
echo "DST: $DST"
echo "Args: $( [[ $CLEAN -eq 1 ]] && echo '--clean ' || echo '')$( [[ $EXEC -eq 1 ]] && echo '--exec ' || echo '')$( [[ -n "$LOG_FILE" ]] && echo "--log $LOG_FILE" || echo '')"
echo "---" >&$LOG_FD
fi
# --- function ---
log_action() {
local msg="$1"
# terminal
if [[ $DRY_RUN -eq 1 ]]; then
echo "[DRY RUN] $msg"
else
echo "$msg"
fi
# file
if [[ -n "$LOG_FILE" ]]; then
local prefix=$( [[ $DRY_RUN -eq 1 ]] && echo "[DRY RUN]" || echo "[EXEC]" )
echo "$prefix $msg" >&$LOG_FD
fi
}
run() {
if [[ $DRY_RUN -eq 1 ]]; then
echo "[DRY RUN] $*" | tee -a /dev/fd/$LOG_FD 2>/dev/null
else
echo "[EXEC] $*" >&$LOG_FD
"$@"
fi
}
# --- main exec ---
if [[ $EXEC -eq 1 && ! -d "$DST" ]]; then
echo "[EXEC] Creating a target directory: $DST"
mkdir -p "$DST" || { echo "Error: Cannot create $DST"; exit 1; }
elif [[ $DRY_RUN -eq 1 && ! -d "$DST" ]]; then
log_action "Target directory required: $DST (will be created with --exec)"
fi
while IFS= read -r -d '' dir; do
# skip proxy exist dir
if [[ $(basename "$dir") == "proxy" ]]; then
continue
fi
# check for video file in dir
if find "$dir" -maxdepth 1 -type f \( \
-iname "*.mov" -o \
-iname "*.mp4" -o \
-iname "*.mxf" \
\) | grep -q .; then
rel_path="${dir#$SRC/}"
[[ "$rel_path" == "$dir" ]] && rel_path="" # ending /
target_dir="$DST/$rel_path"
proxy_path="$dir/proxy"
# 1. Create the target directory
run mkdir -p "$target_dir"
log_action "Make proxy dir: $target_dir"
# 2. Delete existing symlink/directory ONLY if --clean
if [[ $CLEAN -eq 1 ]]; then
if [[ -d "$proxy_path" || -L "$proxy_path" ]]; then
run rm -rf "$proxy_path"
log_action "The existing symlink/directory has been removed: $proxy_path"
fi
fi
# 3. Create a symlink only if not exist
if [[ -L "$proxy_path" ]]; then
current_target=$(readlink "$proxy_path" 2>/dev/null || echo "???")
if [[ "$current_target" == "$target_dir" ]]; then
log_action "Skip, symlink exitst: $proxy_path → $current_target"
else
log_action "Skip, symlink exists, but it points to: $proxy_path → $current_target"
fi
else
run ln -s "$target_dir" "$proxy_path"
log_action "Symlink sreate: $proxy_path → $target_dir"
fi
fi
done < <(find "$SRC" -type d -print0)
# --- close log ---
if [[ -n "$LOG_FILE" ]]; then
echo "=== End: $(date) ===" >&$LOG_FD
exec {LOG_FD}<&- # close file
echo "Log saved to: $LOG_FILE"
fi
echo ""
if [[ $DRY_RUN -eq 1 ]]; then
echo "Dry-run mode completed. No changes were made."
echo "Use --exec to make changes."
else
if [[ $CLEAN -eq 1 ]]; then
echo "Done. Proxies have been refreshed. (--clean)."
else
echo "Done. New proxy links have been added (incremental mode)."
fi
fi
A tak máme hotové adresáře, ve kterých budou generovány proxy soubory, a to kdekoli. To v případě použití Blackmagic Proxy Generator. Jak již bylo řečeno, samotný Resolve DaVinci má možnost určit, kde mají být. Navíc, když potřebujeme přesunout proxy soubory, například na notebook, abychom během dovolené neztráceli čas na pláži :-), stačí zkopírovat katalog a máme kopii originálu. Není třeba provádět žádné extrahování proxy nebo podobné operace.
Doporučuji používat možnost --log. Můžete zadat cestu s vlastním názvem souboru nebo pouze určit cílový adresář pro protokol. Pokud tedy použijeme syntaxi proxyCreator.sh --log DestinationDir SourceDir DestinationDir , dostaneme logy v cílovém adresáři, pojmenované „proxy_log_Aktuální-Datum-Hodina-Minuta.log”. Po několika týdnech nebo měsících se může ukázat, že tyto informace jsou docela užitečné. A při spuštění bez volby --exec získáme simulaci budoucích akcí, takže můžeme sledovat, co se bude dít. To se může hodit zejména při velkém počtu proxy adresářů, protože se může ukázat, že část adresářů má vygenerované své symlinky
Ale protože jak se řeklo A, tak by se mělo říct i B, dalším krokem bylo vytvoření vlastního generátoru správných proxy video souborů s libovolným kodekem, velikostí nebo bitrate. Tedy technologický závod s firmou Blackmagic :-)
Takže pokračujeme dál. Máme již krásnou strukturu proxy adresářů, je čas do nich nahrát nějaké soubory. Nejjednodušším řešením pro zpracování všech druhů video souborů z konzole je FFMPEG. Je to nenápadný program, ale umí v podstatě všechno. Možná kromě vaření a úklidu v domácnosti :-) Existuje mnoho jeho variant a modifikací, ale já osobně používám klasickou instalaci pomocí "brew install ffmpeg". Možná mají jiné verze někdy větší možnosti, ale já potřebuji jednoduchá řešení. Ano, například FFMBC je přímo stvořený pro profesionální zpracování videa, ale já vytvářím pouze proxy :-)
Vraťme se k generování souborů. Tentokrát jsem se vzdal „Zsh“ ve prospěch obyčejného „Bash“. Prostě jsem měl jednoduchou verzi skriptu z minulých let a nechtěl jsem přepisovat formát, který byl sice kompatibilní, ale ne úplně. Detaily nejsou důležité, ale předávání argumentů jako tabulek nebo řetězců má svůj význam. Nebo tomu prostě nerozumím. Nechme to být, stejně nemám v úmyslu vést „flame war“ o tom, co je lepší. Někdy je prostě něco užitečné nebo hotové a jindy zase něco jiného. Na základě starého, několikřádkového skriptu tak vzniklo něco jako „kombajn” pro proxy soubory. Původně byl nápad spojit proxyDirCreator a proxyFileGenerator (tak jsem ty soubory hrdě pojmenoval :-), ale to by bylo naprosto nesmyslné. Ne každý potřebuje symlinkovat proxy adresáře na jiný disk. Skript tedy rekurzivně prochází zdrojový adresář, vyhledává soubory .mov, .mp4, .mxf (s takovými se nejčastěji setkávám v práci), pokud neexistuje, vytvoří v dané úrovni proxy adresář (pokud existuje, použije existující, ať už je to adresář nebo symlink), provede kompresi všech souborů z daného adresáře, přidá výsledek do souboru „.csv“ jako jakýsi log a pokračuje ve své cestě po adresářích. A tak pokračuje, dokud nezpracuje všechny soubory z dané lokality. Má několik možností, vše je uvedeno v kódu skriptu, takže nemá smysl to zde rozebírat. V každém případě, stejně jako v proxyDirCreator, ve výchozím nastavení nic nevytváří, pouze ukazuje, co by chtěl udělat. Vždy je lepší předem vidět, jaké změny má nějaký program v úmyslu provést, než pak plakat, že se něco pokazilo.
Pokud chcete generovat několik souborů najednou, je třeba nainstalovat GNU Parallel. Upřímně řečeno nevím, kolik jader FFMpeg při práci využívá, mám dojem, že pouze jedno. Znásobení počtu procesů tedy může výrazně zkrátit dobu generování a navíc můžete na ostatních jádrech klidně pracovat. Ale to je téma na samostatný článek. V každém případě mi to funguje a dokonce docela rychle. Používám však procesor Apple M4, který je sám o sobě poměrně rychlý. Neměl jsem možnost provést testy na jiných strojích.
#!/bin/bash
# --- help ---
usage() {
cat << EOF
Usage: $0 [quality] [--jobs N] [--overwrite] [--exec]
Example: $0 /Users/user/Projekt medium # simulation mode, default
$0 /Users/user/Projekt medium --exec # single mode
$0 /Users/user/Projekt medium --jobs 4 --overwrite --exec # parallel mode, overwrite old proxy
Generates proxy files with the selected quality setting. Works recursively, skips "proxy" directories.
---
The script is designed for Mac OSX or Linux. It may be possible to adapt it for Windows, but I am not familiar with that.
The script may contain errors. Use it at your own risk. If you notice any irregularities,
please let me know and I will correct them in my spare time. Enjoy using it :-)
martvin
admin @ projektika.group
---
ARGUMENTS:
start_path Directory to start searching from
quality Quality profile (default: medium, see options below)
light - low bitrate, fast, half resolution (H.264)
medium - speed/quality balance, half resolution (H.264)
high - full resolution, high quality (H.264)
h265-half - half resolution, excellent compression (H.265/HEVC)
h265-full - full resolution, excellent compression (H.265/HEVC)
prores - ProRes 422 (10-bit), half resolution
prores-lt - ProRes LT (lower bitrate), half resolution
prores-proxy - ProRes Proxy (dedicated proxy codec), half resolution
prores-proxy-full - ProRes Proxy, full resolution
OPTIONS:
--jobs N Number of parallel processes (default: 1), use with GNU parallel
--overwrite Overwrite existing proxy files
--exec Execute (without this flag runs in dry-run mode)
--dry-run Shows what it would do without generating (default)
EOF
exit 1
}
if [ $# -eq 0 ]; then
echo "Please specify the start path"
usage
fi
# --- dependency check ---
check_dependencies() {
if ! command -v ffmpeg &> /dev/null; then
echo "\033[1;31mERROR: ffmpeg is not installed or not in PATH.\033[0m"
echo "\033[1;31minstall: brew install ffmpeg\033[0m"
exit 1
fi
if [[ $jobs -gt 1 ]] && ! command -v parallel &> /dev/null; then
echo "\033[1;31mWARNING: GNU parallel is not installed - working sequentially.\033[0m"
jobs=1
fi
}
start_path="$1"
quality="${2:-medium}"
jobs=1
overwrite=0
DRY_RUN=1
# Parsing additional arguments
shift 2 # skip after start_path & quality
while [[ $# -gt 0 ]]; do
case "$1" in
--jobs)
if [[ -n "$2" && "$2" =~ ^[0-9] $ ]]; then
jobs="$2"
shift 2
else
echo "\033[1;31mERROR: --jobs needs a number.\033[0m"
usage
fi
;;
--overwrite)
overwrite=1
shift
;;
--exec)
DRY_RUN=0
shift
;;
--dry-run)
DRY_RUN=1
shift
;;
--help)
usage
;;
*)
echo "Unknown option: $1, maybe a typo? Use --help for help."
shift
;;
esac
done
# Validate start path
if [ ! -d "$start_path" ]; then
echo "\033[1;31mERROR: Directory '$start_path' does not exist\033[0m"
exit 1
fi
case "$quality" in
light)
scale="scale=iw/2:ih/2"
codec="-c:v libx264 -preset ultrafast -crf 28 -pix_fmt yuv420p -c:a aac -b:a 128k"
ext="mp4"
;;
medium)
scale="scale=iw/2:ih/2"
codec="-c:v libx264 -preset fast -crf 23 -pix_fmt yuv420p -c:a aac -b:a 192k"
ext="mp4"
;;
high)
scale="scale=iw:ih"
codec="-c:v libx264 -preset slow -crf 18 -pix_fmt yuv420p -c:a aac -b:a 256k"
ext="mp4"
;;
h265-half)
#compatible setting for macosx quicktime player - -profile:v main -level 4.0 -tag:v hvc1
scale="scale=iw/2:ih/2"
codec="-c:v libx265 -preset medium -crf 26 -pix_fmt yuv420p -profile:v main -level 4.0 -tag:v hvc1 -c:a aac -b:a 192k"
ext="mp4"
;;
h265-full)
#compatible setting for macosx quicktime player
scale="scale=iw:ih"
codec="-c:v libx265 -preset medium -crf 23 -pix_fmt yuv420p -profile:v main -level 4.0 -tag:v hvc1 -c:a aac -b:a 256k"
ext="mp4"
;;
prores)
#prores halfres normal codec
scale="scale=iw/2:ih/2"
codec="-c:v prores_ks -profile:v 2 -pix_fmt yuv422p10le -vendor apl0 -bits_per_mb 2000 -colorspace bt709 -color_primaries bt709 -color_trc bt709 -c:a pcm_s16le"
ext="mov"
;;
prores-lt)
scale="scale=iw/2:ih/2"
codec="-c:v prores_ks -profile:v 1 -pix_fmt yuv422p10le -vendor apl0 -bits_per_mb 3500 -colorspace bt709 -color_primaries bt709 -color_trc bt709 -c:a pcm_s16le"
ext="mov"
;;
prores-proxy)
scale="scale=iw/2:ih/2"
codec="-c:v prores_ks -profile:v 0 -pix_fmt yuv422p10le -vendor apl0 -bits_per_mb 2000 -colorspace bt709 -color_primaries bt709 -color_trc bt709 -c:a pcm_s16le"
ext="mov"
;;
prores-proxy-full)
scale="scale=iw:ih"
codec="-c:v prores_ks -profile:v 0 -pix_fmt yuv422p10le -vendor apl0 -bits_per_mb 2000 -colorspace bt709 -color_primaries bt709 -color_trc bt709 -c:a pcm_s16le"
ext="mov"
;;
*)
echo "Error in quality settings: $quality"
echo "Available options: light|medium|high|h265-half|h265-full|prores|prores-lt|prores-proxy|prores-proxy-full"
exit 1
;;
esac
# Generate CSV filename with timestamp and quality
timestamp=$(date "%Y%m%d_%H%M%S")
csv_file="$start_path/proxy_log_${timestamp}_${quality}.csv"
# old maps, when DJI file, generate bad TC. Simply put, these "idiots" do not adhere to any standards.
# maps="-map 0:v:0 -map 0:a? -map_metadata 0 -copytb 1"
#new maps, i hope is ok :-)
maps="-map 0:v:0 -map 0:a? -map_metadata 0"
check_dependencies
# Create CSV header
echo -e "origin_file\tproxy_file\tcreation_time\tduration" > "$csv_file"
create_proxy() {
local file="$1"
local dir_name=$(dirname "$file")
local basename=$(basename "$file")
local filename="${basename%.*}"
local output_dir="$dir_name/Proxy"
local output_file="$output_dir/$filename.$ext"
# Check if ffmpeg is available
local ffPath=$(command -v ffmpeg)
if [ -z "$ffPath" ]; then
echo "ERROR: ffmpeg not found in PATH"
return 1
fi
mkdir -p "$output_dir"
# Check if a file with the same name but different extension already exists
local mp4_file="$output_dir/$filename.mp4"
local mov_file="$output_dir/$filename.mov"
# Check file existence if not overwrite
if [ "$overwrite" -eq 0 ]; then
if [ -f "$mp4_file" ] || [ -f "$mov_file" ]; then
echo "File exists (as .mp4 or .mov), skipping: $output_file"
return 0
fi
fi
# Set ffmpeg overwrite flag
local ffmpeg_flag=""
if [ "$overwrite" -eq 1 ]; then
ffmpeg_flag="-y"
else
ffmpeg_flag="-n"
fi
# check if attached pic
attached_id=$(ffmpeg -hide_banner -i "$file" 2>&1 | grep "Video.*attached pic" | sed -E 's/.*Stream #0:([0-9] )\[.*:([0-9] )\].*/\2/' | head -n1)
# new maps
if [ -n "$attached_id" ]; then
echo "Attached picture detected on stream #$attached_id — excluding it..."
map_fix="$maps -map -0:v:$attached_id"
else
map_fix="$maps"
fi
if [ $DRY_RUN -eq 1 ]; then
echo "[DRY RUN] Would run: $ffPath -nostdin $ffmpeg_flag -i \"$file\" $map_fix -vf \"$scale\" $codec \"$output_file\""
return 0
fi
# Execute ffmpeg
echo "Processing: $file → $output_file"
if ! < /dev/null $ffPath -nostdin $ffmpeg_flag -i "$file" $map_fix \
-vf "$scale" \
$codec \
"$output_file" 2> >(tee "/tmp/ffmpeg_error.log" >&2); then
echo "ERROR: Failed to process $file"
echo " See detailed log: /tmp/ffmpeg_error.log"
tail -n 20 /tmp/ffmpeg_error.log
echo ""
return 1
fi
# Extract metadata
local ffmpeg_metadata=$($ffPath -i "$file" 2>&1)
local duration=$(echo "$ffmpeg_metadata" | grep "Duration:" | head -n 1 | awk '{print $2}' | sed 's/,//')
local creation_time=$(echo "$ffmpeg_metadata" | grep -i "creation_time" | head -n 1 | awk '{print $3" "$4}')
# Write to CSV
echo -e "$file\t$output_file\t$creation_time\t$duration" >> "$csv_file"
echo "Finished: $file → $output_file"
return 0
}
# Export function and variables for parallel processing
export -f create_proxy
export scale codec ext csv_file overwrite DRY_RUN maps
# Display settings
echo "=== Proxy Generator Settings ==="
echo "Start path: $start_path"
echo "Quality: $quality"
echo "Jobs: $jobs"
echo "Overwrite: $([ $overwrite -eq 1 ] && echo "yes" || echo "no")"
echo "Mode: $([ $DRY_RUN -eq 1 ] && echo "DRY RUN" || echo "EXECUTION")"
echo "CSV output: $csv_file"
echo "================================"
# Main processing
if [ "$jobs" -gt 1 ]; then
if command -v parallel > /dev/null 2>&1; then
echo "Starting parallel processing with $jobs jobs..."
find "$start_path" -type f \( -iname "*.mxf" -o -iname "*.mp4" -o -iname "*.mov" \) \
-not -path "*/proxy" -not -path "*/proxy/*" -print0 | \
parallel -0 -j"$jobs" create_proxy
else
echo "WARNING: GNU parallel is not installed. Falling back to sequential mode..."
jobs=1
fi
fi
if [ "$jobs" -eq 1 ]; then
echo "Starting sequential processing..."
while IFS= read -r -d '' file; do
create_proxy "$file"
done < <(find "$start_path" -type f \( -iname "*.mxf" -o -iname "*.mp4" -o -iname "*.mov" \) \
-not -path "*/Proxy" -not -path "*/Proxy/*" -print0)
fi
echo "Processing completed. Results saved to: $csv_file" Samozřejmě je možné skript rozšířit. Hodila by se například možnost jednoduše generovat proxy z A do B, bez vytváření odkazů nebo adresářů. Ale já jsem chtěl pouze simulovat fungování původního programu. Možností je nekonečné množství, každý uživatel má jistě jiné potřeby. No, nic není dokonalé :-)
Druhá věc, kterou je třeba vysvětlit, je, že argumenty v jednom skriptu jsou na začátku, v druhém na konci příkazu. No, prostě to tak zůstalo z předchozí verze. Ani mě nenapadlo to opravit, aby to bylo stejné. Ale našli se lidé, kteří si toho všimli :-) Díky za připomínky, ale v zásadě to nijak nebrání používání. A navíc každá změna nám trochu pomáhá prolomit rutinu, takže náš mozek pracuje efektivněji :-)
Po krátkém dobrodružství s ComfyUI (bohužel jsem ho z vlastní nepozornosti spustil v neizolovaném prostředí) mi smazal ffmpeg. No nic, věřím konzoli, spouštím „brew instal ...“, a tady nějaká závislost nemá ten správný SSL certifikát, nebo bůhví, co se pokazilo. Po krátkém přemýšlení jsem došel k závěru, že brew je sice pohodlné, ale v podstatě ho nepotřebuji. Potřebuji pouze FFMpeg a FFProbe. A protože na stránce evermeet.cx jsou k dispozici hotové binární soubory (v mém případě pro mac osx) pro oba tyto programy, nejjednodušší bude prostě použít hotový produkt, místo abych budoval celý strom závislostí. Možná je to trochu zamotané, jako všechny moje popisy, ale prostě nepotřebuji pokročilé kodeky pro běžné vytváření proxy. A ty, které jsou obsaženy v binárních verzích FFMpeg, jsou zcela dostačující.
Nebudeme to protahovat, tady je hotový skript pro instalaci FFMpeg a FFProbe v nejnovější dostupné verzi, bez Homebrew, kompilace a dalších podivných věcí :-)
#!/bin/bash
set -e
# install binary without Homebrew
install_binary() {
local name=$1
local tmpfile="/tmp/${name}.zip"
local url="https://evermeet.cx/ffmpeg/getrelease/${name}/zip"
echo "|-> Get ${name}..."
curl -L "$url" -o "$tmpfile" || {
echo -e "\033[0;31m Failed to download ${name}.\033[0m"
return 1
}
echo "Unpack ${name}..."
unzip -o "$tmpfile" -d /tmp/
echo "|-> Install ${name} to /usr/local/bin..."
sudo mv "/tmp/${name}" "/usr/local/bin/${name}"
sudo chmod x "/usr/local/bin/${name}"
echo "|-> Clear... "
rm -f "$tmpfile"
echo "|-> ${name} updated!"
"/usr/local/bin/${name}" -version | head -n 1
echo
}
echo "==============================="
echo "Install/update FFmpeg & FFprobe"
echo "==============================="
install_binary ffmpeg
install_binary ffprobe
echo -e "\033[0;32m All OK! \033[0m"
Stačí zkopírovat, vložit do souboru, udělit oprávnění k provedení a spustit v konzoli. Pravděpodobně se před instalací FFMpega zeptá na heslo, ale to se může lišit. A jak se to říká – „U mě všechno funguje“ :-)
Bylo přidáno odstranění stezky z „attached pic“, což někdy způsobovalo podivné chování, zejména u souborů DJI. A u stejných souborů byl větší problém s časovými kódy. Osmo DJI má sice TC, ale označeno jako „codec:none“. Drony DJI nemají vůbec žádné TC, nanejvýš časové označení v názvu nebo záhlaví. Teoreticky by možnost „-map 0:d?“ měla ignorovat chybějící „path“, ale nefunguje to úplně.
Právě v případě označení `codec:none`, což je podle mě naprostá chyba. Ale to je jen můj osobní názor, pravděpodobně to má nějaký skrytý smysl.
V každém případě, pokud nutně potřebujete TC a obecně je časový kód prakticky vždy zaznamenán také jako metadata, přidejte „-map 0:d?“ do deklarace maps. A to je vše. Dalo by se to rozšířit, přidat výjimky, nějaké if a else. Ale univerzálnost má své meze. Stejně jako moje trpělivost :-)