I am using FFPROBE to get some file durations in PowerShell.
For my video it gets the frame count properly but for my audio files it returns a different frame count.
This is my code:
$audioId = "$id.m4a"
$videoId = "$id.mp4"
$duration1 = ffprobe -i $videoId -show_frames 2>&1 | grep -c media_type=video
$duration2 = ffprobe -i $audioId -show_frames 2>&1 | grep -c media_type=audio
echo $duration1
echo $duration2
For video it returns a proper output, but for audio it returns a wrong result.
Any suggestions?
Thank you
Related
I have a set of filenames in a directory, some of which are likely to have identical substrings but not known in advance. This is a sorting exercise. I want to move the files with the maximum substring ordered letter match together in a subdirectory named with that number of letters and progress to the minimum match until no matches of 2 or more letters remain. Ignore extensions. Case insensitive. Ignore special characters.
Example.
AfricanElephant.jpg
elephant.jpg
grant.png
ant.png
el_gordo.tif
snowbell.png
Starting from maximum length matches to minimum length matches will result in:
./8/AfricanElephant.jpg and ./8/elephant.jpg
./3/grant.png and ./3/ant.png
./2/snowbell.png and ./2/el_gordo.tif
Completely lost on an efficient bash or python way to do what seems a complex sort.
I found some awk code which is almost there:
{
count=0
while ( match($0,/elephant/) ) {
count++
$0=substr($0,RSTART+1)
}
print count
}
where temp.txt contains a list of the files and is invoked as eg
awk -f test_match.awk temp.txt
Drawback is that a) this is hardwired to look for "elephant" as a string (I don't know how to make it take an input string (rather than file) and an input test string to count against, and
b) I really just want to call a bash function to do the sort as specified
If I had this I could wrap some bash script around this core awk to make it work.
function longest_common_substrings () {
shopt -s nocasematch
for file1 in * ; do for file in * ; do \
if [[ -f "$file1" ]]; then
if [[ -f "$file" ]]; then
base1=$(basename "$file" | cut -d. -f1)
base2=$(basename "$file1" | cut -d. -f1)
if [[ "$file" == "$file1" ]]; then
echo -n ""
else
echo -n "$file $file1 " ; $HOME/Scripts/longest_common_substring.sh "$base1" "$base2" | tr -d '\n' | wc -c | awk '{$1=$1;print}' ;
fi
fi
fi
done ;
done | sort -r -k3 | awk '{ print $1, $3 }' > /tmp/filesort_substring.txt
while IFS= read -r line; do \
file_to_move=$(echo "$line" | awk '{ print $1 }') ;
directory_to_move_to=$(echo "$line" | awk '{ print $2 }') ;
if [[ -f "$file_to_move" ]]; then
mkdir -p "$directory_to_move_to"
\gmv -b "$file_to_move" "$directory_to_move_to"
fi
done < /tmp/filesort_substring.txt
shopt -u nocasematch
where $HOME/Scripts/longest_common_substring.sh is
#!/bin/bash
shopt -s nocasematch
if ((${#1}>${#2})); then
long=$1 short=$2
else
long=$2 short=$1
fi
lshort=${#short}
score=0
for ((i=0;i<lshort-score;++i)); do
for ((l=score+1;l<=lshort-i;++l)); do
sub=${short:i:l}
[[ $long != *$sub* ]] && break
subfound=$sub score=$l
done
done
if ((score)); then
echo "$subfound"
fi
shopt -u nocasematch
Kudos to the original solution for computing the match in the script which I found elsewhere in this site
I'm trying to take a pile of screenshot PNGs, and the timestamp of each screenshot, and create a video with ffmpeg that recreates the timing of the screenshots.
According to the ffmpeg help, -vsync 0 seems to be what I need,
-vsync parameter
0, passthrough Each frame is passed with its timestamp from the
demuxer to the muxer.
Does anyone know how to pass this timestamp information into ffmpeg? Presently each screenshot has the recording time in milliseconds as the filename.
The below example doesn't accept any timing information that I can tell, so I'm looking for the proper format to pipe to the command.
ffmpeg -vsync 0 -pattern_type glob -i '*.png' -c:v libx264 output.mp4
Script: get time from filename, minus previous filetime = duration of temp video, then concat videos
#!/bin/bash
LST=($(ls -1tr Screenshot*.png))
TOT=${#LST[*]}
f="${LST[0]}"
#Screenshot_20201115_135335.png
FNM="${f%.*}"
SEC="${FNM:24:2}"
MIN="${FNM:22:2}"
HOU="${FNM:20:2}"
echo $f $HOU $MIN $SEC
BEG=$(echo "$HOU * 3600 + $MIN * 60 + $SEC" | bc -l)
echo $f $BEG
INP=("-i" "$f")
OUT="${f%.*}.mkv"
TXT=list.txt
echo "#png to mkv" > $TXT
for (( i=1; i<=$(( $TOT -1 )); i++ )); do
f="${LST[$i]}"
FNM="${f%.*}"
SEC="${FNM:24:2}"
MIN="${FNM:22:2}"
HOU="${FNM:20:2}"
TIM=$(echo "$HOU * 3600 + $MIN * 60 + $SEC" | bc -l)
DUR=$(echo "$TIM - $BEG" | bc -l)
echo $f $TIM $DUR
ffmpeg -y -hide_banner -loop 1 "${INP[#]}" -t $DUR "/tmp/${OUT}"
echo "file '/tmp/${OUT}'" >> $TXT
BEG=$TIM
INP=("-i" "$f")
OUT="${f%.*}.mkv"
done
ffmpeg -y -hide_banner -loop 1 "${INP[#]}" -t 5 "/tmp/${OUT}"
echo "file '/tmp/${OUT}'" >> $TXT
cat "$TXT"
ffmpeg -hide_banner -f concat -safe 0 -i "$TXT" -c:v h264_nvenc -cq 20 -y /tmp/output.mkv
ffplay /tmp/output.mkv
I have this:
for file in **/*.ogg;
if ffprobe "$file" 2>&1 | sed -E -n 's/^ *GENRE *: (.*)/\1/p' | grep -q "$argv";
echo "$file"
else
end
end
but I would like to turn it into a function which will take a list of filenames as standard-input:
$ find . -maxdepth 1 -not -type d -exec du -h {} + | cut -f2 | filterByGenre Classical
You could do
function filterByGenre
while read line
do stuff with $line
end
end
or
function filterByGenre
set listOfLines (cat)
for line in $listOfLines
do stuff with $line
end
end
I have got a list of the top 20 files/folders that are taking the most amount of room on my hard drive. I would like to separate them into size path/to/file. Below is what I have done so far.
I am using: var=$(du -a -g /folder/ | sort -n -r | head -n 20). It returns the following:
120 /path/to/file
115 /path/to/another/file
110 /file/path/
etc.
I have tried the following code to split it up into single lines.
for i in $(echo $var | sed "s/\n/ /g")
do
echo "$i"
done
The result I would like is as follows:
120 /path/to/file,
115 /path/to/another/file,
110 /file/path/,
etc.
This however is the result I am getting:
120,
/path/to/file,
115,
/path/to/another/file,
110,
/file/path/,
etc.
I think awk will be easier, can be combined with a pipe to the original command:
du -a -g /folder/ | sort -n -r | head -n 20 | awk '{ print $1, $2 "," }'
If you can not create a single pipe, and have to use $var
echo "$var" | awk '{ print $1, $2 "," }'
echo '2003'| wc -c
I thought it would give me 4, but it turned to be 5, what is that additional byte?
Because echo will get a new line.
echo "2014" | wc -c
it will get 5
printf "2014" | wc -c
it will get 4 where printf will not add a new line.
echo contains a built-in switch, -n, to remove newline. So running:
echo -n "2021" | wc -c
Will output the expected 4.
echo adds new line which is causing the issue.
As mentioned by "KyChen", you can use printf or:
a="2014 ;
echo $a |awk '{print length}'