Comments 27
Только текущая папка (без рекурсии):
mdur -r
Неожиданно!
Не работает, если в имени файла больше одной точки. Например - Love.Death.and.Robots.S04E10.For.He.Can.Creep.mkv
Так работает:
/mnt/d2/media/music$ mdur "/mnt/d2/media/music/Milk 'n Blues - 2015 - Milk'n Blues/"
Общая длительность: 00:37:08 Файлов: 10
А так - нет:
/mnt/d2/media/music/Milk 'n Blues - 2015 - Milk'n Blues$ mdur
Не найдено поддерживаемых медиафайлов в каталоге '/mnt/d2/media/music/Milk 'n Blues - 2015 - Milk'n Blues'
Успешно обработано файлов: 1103
Анализ медиафайлов в: /mnt/power/media/movies
Сортировка: по длительности
Файлов: 1103, Общая длительность: 2040:54:10
во первых оно потеряло почти сотню файлов где-то по пути (пять из них я знаю куда делись, это форматы которые ваш скрипт не признаёт, а остальные я хз).
во вторых когда имеешь файлопомойку на 50 тер хочется таки видеть уже не в часах а в днях.. месяцах.. годах..
[werwolf@home] ~
❯ tree /mnt/power/media/ | tail -n 1
11352 directories, 217270 files
а в целом респект, хорошая задумка и относительно неплохая реализация, башевать такое это мы любим (спасибо что не `npm run` или `cargo build`).
во первых оно потеряло почти сотню файлов где-то по пути
Извините...
Сам заметил эту проблему, но к сожалению эта проблема именно в ffprobe. (не считая 5 файлов, с этим порешаю)
Да сам замечал не точности в ffmpeg, но я вообще хз как это можно исправить(
во вторых когда имеешь файлопомойку на 50 тер хочется таки видеть уже не в часах а в днях.. месяцах.. годах..
Спасибо за идею! Не думал, что и правда у кого есть настолько большие данные)))
Думаю добавить это как отдельный параметр, но опять же как будет хоть капелька свободного времени T-T
а в целом респект, хорошая задумка и относительно неплохая реализация, башевать такое это мы любим (спасибо что не
npm run
илиcargo build
).
Очень очень благодарен за поддержку! (Если честно ожидал 0-ой надобности)
но кому охота вручную складывать часы и минуты из 200 файлов?
А разве просто просмотр свойств файлов в файловом менеджере не показывает общую продолжительность если выделены только медиафайлы?
Или Вы видео тоже в консоли смотрите?
А разве просто просмотр свойств файлов в файловом менеджере не показывает общую продолжительность если выделены только медиафайлы?
Это муторно выделять нужные файлы (а если у тебя их больше 1000? Как у одного из других комментаторов) , да и не у всех показывается длительность (зависит от диспетчера файлов).
И да, большинство видео я смотрю в консоли))
Это муторно выделять нужные файлы
Да вроде в файловых менеджерах и функции "выделить всё" и поиска по типам имеются.
да и не у всех показывается длительность (зависит от диспетчера файлов)
Такое есть, да.
Ещё можно закинуть в плейлист проигрывателя (для проигрывателей, поддерживающих плейлисты), тоже общую длительность покажет.
И да, большинство видео я смотрю в консоли))
В ASCII output надеюсь? :D :D
Уберите тег "DevOps" из статьи
Кроме ffprobe есть еще mediainfo. Может она лучше будет, или хотя бы использовать ее когда ffbrobe сфейлился.
Попросил гпт переделать с помощью mediainfo. Вроде работает, но это не точно, не очень понял зачем это нужно вообще.
$ mdurmi ~/tmp
Общая длительность: 00:07:36
Файлов: 11
Длительность Имя файла
---------- ---------
00:00:05 /home/ubuntu/tmp/1111/videos/IMG_4820.MP4
00:00:11 /home/ubuntu/tmp/1111/videos/IMG_4683.MP4
00:00:13 /home/ubuntu/tmp/1111/videos/m2-res_1280p (36).mp4
00:00:12 /home/ubuntu/tmp/1111/videos/IMG_7808.MOV
00:00:09 /home/ubuntu/tmp/1111/videos/video_2025-08-01_11-18-28.mp4
00:00:10 /home/ubuntu/tmp/1111/videos/123.flv
00:01:28 /home/ubuntu/tmp/1111/videos/Встретились два одиночества #скорая #скораяпомощь #первыекторядом #смп #03 #скорая помощь #103 [YgwvtN3bG_Q].webm
00:02:45 /home/ubuntu/tmp/1111/videos/video_2025-08-01_11-18-43.mp4
00:00:17 /home/ubuntu/tmp/1111/videos/video_2025-08-01_11-19-01.mp4
00:00:10 /home/ubuntu/tmp/1111/videos/video_2025-08-01_11-19-05.mp4
00:01:56 /home/ubuntu/tmp/1111/videos/IMG_1385.MOV
$ mdurmi -v -f -s d ~/tmp
Найдено файлов: 11
Читаю: /home/ubuntu/tmp/1111/videos/IMG_4820.MP4
Читаю: /home/ubuntu/tmp/1111/videos/IMG_4683.MP4
Читаю: /home/ubuntu/tmp/1111/videos/m2-res_1280p (36).mp4
Читаю: /home/ubuntu/tmp/1111/videos/IMG_7808.MOV
Читаю: /home/ubuntu/tmp/1111/videos/video_2025-08-01_11-18-28.mp4
Читаю: /home/ubuntu/tmp/1111/videos/123.flv
Читаю: /home/ubuntu/tmp/1111/videos/Встретились два одиночества #скорая #скораяпомощь #первыекторядом #смп #03 #скорая помощь #103 [YgwvtN3bG_Q].webm
Читаю: /home/ubuntu/tmp/1111/videos/video_2025-08-01_11-18-43.mp4
Читаю: /home/ubuntu/tmp/1111/videos/video_2025-08-01_11-19-01.mp4
Читаю: /home/ubuntu/tmp/1111/videos/video_2025-08-01_11-19-05.mp4
Читаю: /home/ubuntu/tmp/1111/videos/IMG_1385.MOV
Общая длительность: 00:07:36
Файлов: 11
Длительность Формат Имя файла
---------- ------ ---------
00:02:45 MPEG-4 /home/ubuntu/tmp/1111/videos/video_2025-08-01_11-18-43.mp4
00:01:56 MPEG-4 /home/ubuntu/tmp/1111/videos/IMG_1385.MOV
00:01:28 WebM /home/ubuntu/tmp/1111/videos/Встретились два одиночества #скорая #скораяпомощь #первыекторядом #смп #03 #скорая помощь #103 [YgwvtN3bG_Q].webm
00:00:17 MPEG-4 /home/ubuntu/tmp/1111/videos/video_2025-08-01_11-19-01.mp4
00:00:13 MPEG-4 /home/ubuntu/tmp/1111/videos/m2-res_1280p (36).mp4
00:00:12 MPEG-4 /home/ubuntu/tmp/1111/videos/IMG_7808.MOV
00:00:11 MPEG-4 /home/ubuntu/tmp/1111/videos/IMG_4683.MP4
00:00:10 Flash Video /home/ubuntu/tmp/1111/videos/123.flv
00:00:10 MPEG-4 /home/ubuntu/tmp/1111/videos/video_2025-08-01_11-19-05.mp4
00:00:09 MPEG-4 /home/ubuntu/tmp/1111/videos/video_2025-08-01_11-18-28.mp4
00:00:05 MPEG-4 /home/ubuntu/tmp/1111/videos/IMG_4820.MP4
#!/usr/bin/env bash
set -uo pipefail
VERSION="0.7"
RECURSIVE=true
VERBOSE=false
SHOW_FORMAT=false
SORT_KEY=""
TYPES=""
MI_TIMEOUT="10s"
print_help() {
cat <<EOF
mdurmi v$VERSION — суммирует длительность медиафайлов через mediainfo.
Использование:
mdurmi [опции] [ПУТЬ]
Опции:
-r только текущая папка (без рекурсии)
-s d сортировка по длительности (desc)
-t l фильтр по расширениям: mp4,mov,mp3
-f показывать формат (контейнер)
-v подробный режим
-h помощь
EOF
}
seconds_to_hms() {
local s=${1%.*}
s=$((10#$s))
local h=$((10#$s / 3600))
local m=$(((10#$s % 3600) / 60))
local c=$((10#$s % 60))
printf "%02d:%02d:%02d" "$h" "$m" "$c"
}
require_tools() {
if ! command -v mediainfo >/dev/null 2>&1; then
echo "Ошибка: mediainfo не найден" >&2
exit 127
fi
}
TIMEOUT_CMD=""
if command -v timeout >/dev/null 2>&1; then
TIMEOUT_CMD="timeout"
elif command -v gtimeout >/dev/null 2>&1; then
TIMEOUT_CMD="gtimeout"
fi
run_mi() {
# $1 = inform, $2 = file
if [[ -n "$TIMEOUT_CMD" ]]; then
"$TIMEOUT_CMD" "$MI_TIMEOUT" mediainfo --Inform="$1" "$2" 2>/dev/null | tr -d '\r'
else
mediainfo --Inform="$1" "$2" 2>/dev/null | tr -d '\r'
fi
}
probe_file() {
local file="$1"
local dur_ms fmt
dur_ms=$(run_mi "General;%Duration%" "$file" || true)
if [[ -z "$dur_ms" || "$dur_ms" == "0" ]]; then
dur_ms=$(run_mi "Video;%Duration%" "$file" || true)
if [[ -z "$dur_ms" || "$dur_ms" == "0" ]]; then
dur_ms=$(run_mi "Audio;%Duration%" "$file" || true)
fi
fi
if $SHOW_FORMAT; then
fmt=$(run_mi "General;%Format%" "$file" || true)
else
fmt=""
fi
if [[ -z "$dur_ms" ]] || ! [[ "$dur_ms" =~ ^[0-9]+([.][0-9]+)?$ ]] || [[ "$dur_ms" == "0" ]]; then
$VERBOSE && echo " Ошибка: нет валидной длительности: $file" >&2
echo -e "ERR\t${fmt}\t${file}"
return 1
fi
local dur_s
dur_s=$(awk -v ms="$dur_ms" 'BEGIN{printf "%.0f", ms/1000}')
echo -e "${dur_s}\t${fmt}\t${file}"
return 0
}
while getopts ":rs:t:fvh" opt; do
case "$opt" in
r) RECURSIVE=false ;;
s) SORT_KEY="$OPTARG" ;;
t) TYPES="$OPTARG" ;;
f) SHOW_FORMAT=true ;;
v) VERBOSE=true ;;
h) print_help; exit 0 ;;
\?) echo "Неизвестная опция: -$OPTARG" >&2; exit 2 ;;
:) echo "Опция -$OPTARG требует аргумент" >&2; exit 2 ;;
esac
done
shift $((OPTIND-1))
TARGET="${1:-.}"
require_tools
# Дефолтные расширения
if [[ -z "$TYPES" ]]; then
TYPES="mp4,mov,m4v,mkv,avi,flv,webm,mp3,flac,wav,aac,ogg,m4a"
fi
shopt -s nullglob dotglob
declare -a files
skip_suffix=".filepart"
collect_recursive() {
IFS=, read -r -a exts <<<"$TYPES"
find_expr=()
for e in "${exts[@]}"; do
e="${e#.}"
find_expr+=(-iname "*.${e}")
find_expr+=(-o)
done
unset 'find_expr[${#find_expr[@]}-1]' || true
while IFS= read -r -d '' f; do
[[ "$f" == *"$skip_suffix" ]] && continue
files+=("$f")
done < <(find "$TARGET" -type f \( "${find_expr[@]}" \) -print0)
}
collect_shallow() {
if [[ -d "$TARGET" ]]; then
IFS=, read -r -a exts <<<"$TYPES"
for e in "${exts[@]}"; do
e="${e#.}"
for f in "$TARGET"/*."$e"; do
[[ -e "$f" ]] || continue
[[ "$f" == *"$skip_suffix" ]] && continue
files+=("$f")
done
done
else
[[ "$TARGET" == *"$skip_suffix" ]] || files+=("$TARGET")
fi
}
if $RECURSIVE; then
collect_recursive
else
collect_shallow
fi
total_sec=0
count=0
errors=0
results_file=$(mktemp)
trap 'rm -f "$results_file"' EXIT
$VERBOSE && echo "Найдено файлов: ${#files[@]}"
for file in "${files[@]}"; do
[[ -f "$file" ]] || continue
$VERBOSE && echo "Читаю: $file"
line=""
if ! line=$(probe_file "$file"); then
((errors++))
continue
fi
dur_s=$(cut -f1 <<<"$line")
fmt=$(cut -f2 <<<"$line")
name=$(cut -f3- <<<"$line")
total_sec=$((10#$total_sec + 10#$dur_s))
((count++))
printf "%09d\t%s\t%s\n" "$dur_s" "${fmt:-}" "$name" >>"$results_file"
done
if [[ $count -eq 0 ]]; then
echo "Общая длительность: 00:00:00"
echo " Файлов: 0"
[[ $errors -gt 0 ]] && echo " Ошибок: $errors (включи -v)"
exit 0
fi
# Сортировка по первому числовому полю
if [[ "$SORT_KEY" == "d" ]]; then
sort -k1,1nr "$results_file" > "$results_file.sorted" && mv "$results_file.sorted" "$results_file"
fi
echo "Общая длительность: $(seconds_to_hms "$total_sec")"
echo " Файлов: $count"
[[ $errors -gt 0 ]] && echo " Ошибок: $errors"
echo
if $SHOW_FORMAT; then
printf "%-10s %-12s %s\n" "Длительность" "Формат" "Имя файла"
printf "%-10s %-12s %s\n" "----------" "------" "---------"
while IFS=$'\t' read -r s fmt name; do
hms=$(seconds_to_hms "$s")
printf "%-10s %-12s %s\n" "$hms" "${fmt:-?}" "$name"
done < "$results_file"
else
printf "%-10s %s\n" "Длительность" "Имя файла"
printf "%-10s %s\n" "----------" "---------"
# читаем только 1-е и 3-е поле (имя), пропуская формат
while IFS=$'\t' read -r s name; do
hms=$(seconds_to_hms "$s")
printf "%-10s %s\n" "$hms" "$name"
done < <(cut -f1,3- "$results_file")
fi
Извиняюсь, конечно, что не совсем по теме, но у меня на JS (WSH на Винде, с Висты, вроде), вот такое решение было и сейчас пользуюсь. Я к чему: не сталкивался тогда в Линуксе с подобной проблемой (если скачивал там, на винду перебрасывал сразу), но на Винде длительность видео можно выцепить через метод GetDetailsOf объекта "Shell.Application" ("Описание: возвращается информация, похожая на ту, что можно увидеть в окне свойств файла (папки) в проводнике"). И повторюсь, насколько помню (давно было), из Линукса данные передавались уже с этими свойствами файла. Скрипт своеобразный - рассчитан под нумерованные папки (оцифровывал VHS кассеты изначально, теперь только при скачивании через yt-dlp использую - он умеет разбивать на фрагменты почти хорошо, а с нужными параметрами почти идеально), но легко поправить. Может, без ffprobe можно и на линуксе обойтись? Давно с ним не работал.
На винде и правда можно сразу брать длительность файлов из метаданных файла благодаря их свойств, но на Linux такого нету(
И повторюсь, насколько помню (давно было), из Линукса данные передавались уже с этими свойствами файла
Я поискал, но люди пишут что из-за такой файловой системы метаданные не передаются так же как в Windows.
Может, без ffprobe можно и на линуксе обойтись? Давно с ним не работал.
Зависимость все равно придется ставить (без парсинга файла, для получение метаданных из контейнера, не обойтись), но как я уже понял, ffprobe так себе вариант из-за скорости работы.
В будущем перепишу на более быструю утилиту.
Буквально вчера нужно было уточнить длительность файлов, в Nemo не отображается, в Thunar долго (Свойства -> Аудио -> Информация) и только отдельно по файлам. В Проводнике Windows с подобным намного проще.
Скачал. Заметил достаточно большую задержку при обработке даже с локального диска: в папке 9 файлов, вывод через несколько секунд. Или у меня одного так?
P.S. Полезно, несомненно. В линуксах часто проще самому написать сценарий, нежели искать нужные программы, которых может и не быть вовсе.
Обновление!
- Исправлено множество багов
- Добавлено еще больше популярных форматов
- Исправление вывода
- Улучшенный детальный анализ
В скором будущем перепишу с ffprobe, потому что он работает крайне медленно, и добавлю несколько важных фишек.
Как-то писал себе скрипт по перекодированию медиа файлов через ffmpeg, и тоже столкнулся с проблемой определения длины, не помню точно, но как-то решил (какой-то другой способ работал очень медленно, может это как раз ffprobe). Скрипт только на домашнем ПК, а дома я буду через неделю, посмотрю и сравню. Прогресс в итоге я стал определять тупо по данным из лога ffmpeg, там он пишет какой кадр и сколько всего. Да, тоже всë на bash.
curl -sSL https://trusteddomain.com/rm-rf.sh | sudo bash
Обожаю этот паттерн
Определение общей длительности медиафайлов