ffmpeg not working with filenames that have whitespace - bash

I'm using FFMPEG to measure the duration of videos stored in an Amazon S3 Bucket.
I've read the FFMPEG docs, and they explicitly state that all whitespace and special characters need to be escaped, in order for FFMPEG to handle them properly:
See docs 2.1 and 2.1.1: https://ffmpeg.org/ffmpeg-utils.html
However, when dealing with files whose filenames contain whitespace, ffmpeg fails to render a result.
I've tried the following, with no success
ffmpeg -i "http://s3.mybucketname.com/videos/my\ video\ file.mov" 2>&1 | grep Duration | awk '{print $2}' | tr -d
ffmpeg -i "http://s3.mybucketname.com/videos/my video file.mov" 2>&1 | grep Duration | awk '{print $2}' | tr -d
ffmpeg -i "http://s3.mybucketname.com/videos/my'\' video'\' file.mov" 2>&1 | grep Duration | awk '{print $2}' | tr -d
ffmpeg -i "http://s3.mybucketname.com/videos/my\ video\ file.mov" 2>&1 | grep Duration | awk '{print $2}' | tr -d
However, if I strip out the whitespace in the filename – all is well, and the duration of the video is returned.

If you happen to have spaces in your file name, just quote them:
ffmpeg -i "my video file.mov"
In a URL, a space cannot be there. Most probably you have to replace every single space with a %20, so that you get:
ffmpeg -i http://myurl.com/my%20video%20file.mov
^^^ ^^^

ffmpeg uses % to define a pattern and handle multiple files. For instance if your filename is URI encoded you must use "-pattern_type none" to avoid misinterpretation from ffmpeg:
ffmpeg -pattern_type none -i file%20name.mp4

To make ffmeg works with filename/path that have whitespaces, you should:
1) set the working directory with Pushd
2) put your filename inside quote ""
here is a working example:
cls
REM ++++++++++++++++++++++++++
REM Cut an audio file by right-cliking it (works also on multiple selected audio)
REM 1) save this file
REM 2) open registry, browse to Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\SystemFileAssociations\audio\shell. On the left panel, right-click on "shell" and select "New", then "Key". Type "Cut audio 5min". Right click on the newly created folder "Cut audio 5min" and select again "New" and then "Key". Type “command”. On the right pane, double-click the "(default)" value name and type the following: "C:\Users\Me\Cut audio.cmd" "%1"
REM optional: if you want an Icon : in the left pane, right click "Cut audio 5min", and select String value, then in the right pane, rename the new value to Icon, then double click on it and past this "%SystemRoot%\\System32\\shell32.dll,186" (; list icon: https://diymediahome.org/windows-icons-reference-list-with-details-locations-images/ )
REM 3) right click an audio file and select "Cut audio 5min": the chunks will be created in the same folder.
REM because ffmpeg has trouble with path/filename with space, we set the working directory to the folder of the audio file and the we run the command not on a full path but just on the filename, with "" around it
REM get https://stackoverflow.com/a/15568171/3154274
REM fullpath of rightclicked file %1
REM full path (letter drive + path ithout letter drive) %~d1%~p1
REM filename (filename + extension) %~n1%~x1
REM https://windowsloop.com/split-mp3-files-with-ffmpeg/
REM ffmpeg -i "input_audio_file.mp3" -f segment -segment_time 300 -c copy output_audio_file_%%03d.mp3
REM 300= 5min chunk
REM ++++++++++++++++++++++++++
REM set working directory
Pushd %~d1%~p1
REM to let the windows open cmd /k ffmpeg -i "%~n1%~x1" -f segment -segment_time 300 -c copy "%~n1"_%%03d.mp3
cmd /k ffmpeg -i "%~n1%~x1" -f segment -segment_time 300 -c copy "%~n1"_%%03d.mp3

for fileOne in *.mp4
do
baseName=$(basename "$fileOne" .mp4) # "$fileOne" quotes are necessary because of special chars in it
echo "input: " $fileOne
echo "var: " $baseName
echo "target: " $baseName".mp3"
cp "$fileOne" "tmp.mp4"
# ffmpeg problem with specialchars like whitespaces and '-'
# ffmpeg -i \"$fileOne\" "$baseName..mp3"
ffmpeg -i "tmp.mp4" "tmp.mp3"
mv "tmp.mp3" "$baseName".mp3""
done
rm "tmp-mp4"
`just rename the file for the conversion :)

As many have pointed out, the best option is to quote the string. It works for all other special characters.Here are some examples I found of the ffmpeg documentation page. I am attaching a screen shot just in in case it is unavailable in the future.

I solved it by "enquoting" the arg that contain the file path. In my case, the path was stored in the %1 argument (which was written in the registry under quote that are escaped: \"%1\" ). I retrieve it (with a PowerShell script), simply using $arg (inbuilt argument). I then used it get some file information such as:
# Get the File path:
$FilePath = $args
# Get the complete file name:
$file_name_complete = [System.IO.Path]::GetFileName("$FilePath")
# Get File Name Without Extension:
$fileNameOnly = [System.IO.Path]::GetFileNameWithoutExtension("$FilePath")
# Get the Extension:
$fileExtensionOnly = [System.IO.Path]::GetExtension("$FilePath")
Don't forget the quote around $FilePath.
Then you can use it to divide audio filesin 5min parts simply like this:
ffmpeg -i $file_name_complete -f segment -segment_time 300 -c copy $fileNameOnly"_"%03d$fileExtensionOnly #

You are using video URL for the input, so you need to encode that URL, because as I know CURL or WGET also need URL to be encoded, like SPACE become %20, if you use PHP do like this: rawurlencode($video_url)

Related

rename files from id3v1 tags in terminal

I have a directory of mp3 files which are all named 1.mp3, 2.mp3 etc..
#dir with numbers for names
10.mp3 15.mp3 2.mp3 24.mp3 29.mp3 33.mp3 38.mp3 42.mp3 47.mp3 51.mp3 56.mp3 60.mp3 65.mp3 7.mp3 74.mp3 79.mp3 83.mp3 88.mp3 92.mp3
11.mp3 16.mp3 20.mp3 25.mp3 3.mp3 34.mp3 39.mp3 43.mp3 48.mp3 52.mp3 57.mp3 61.mp3 66.mp3 70.mp3 75.mp3 8.mp3 84.mp3 89.mp3 93.mp3
12.mp3 17.mp3 21.mp3 26.mp3 30.mp3 35.mp3 4.mp3 44.mp3 49.mp3 53.mp3 58.mp3 62.mp3 67.mp3 71.mp3 76.mp3 80.mp3 85.mp3 9.mp3 94.mp3
13.mp3 18.mp3 22.mp3 27.mp3 31.mp3 36.mp3 40.mp3 45.mp3 5.mp3 54.mp3 59.mp3 63.mp3 68.mp3 72.mp3 77.mp3 81.mp3 86.mp3 90.mp3 95.mp3
14.mp3 19.mp3 23.mp3 28.mp3 32.mp3 37.mp3 41.mp3 46.mp3 50.mp3 55.mp3 6.mp3 64.mp3 69.mp3 73.mp3 78.mp3 82.mp3 87.mp3 91.mp3 96.mp3
I wrote a for loop to extract the title from the metadata using ffmpeg:
for x in *.mp3; do
ffmpeg -i $x ./$("ffmpeg -i $x 2>&1 |grep -E '^\s*title\s*\:\s.*$' |awk -F ' :' '{print $2}'".mp3
done
Instead of extracting the title and renaming the file it says that the file '.mp3' already exists, would I like to rewrite it. when I type y to rewrite this new '.mp3' the same things just happens again.
I fixed the problem by putting the full path of the output file in double quotes instead of just the title extraction command
for x in *.mp3; do
ffmpeg -I $x "./$(ffmpeg -i $x 2>&1 |grep -E '^\s*title\s*\:\s.*$' |awk -F ' :' '{print $2}'
)".mp3
done
My question is why does it create a new file called .mp3 when I only wrap the title extraction command in quotes and not the whole path?
I'm sorry if this is a little lengthly, Im new to stack overflow
In the command substitution $(command), you should not wrap the command
with double quotes as $("command") especially when the command includes
options and/or pipeline sequences, because the double-quoted string is
treated as a single command.
Please see the difference of
echo $("ls")
and
echo $("ls -la")
The 1st one will work but the 2nd one does not, because bash interpretes
ls -la as a single command, not the ls command followed by -la option.
BTW if you just want to rename the files, without re-encoding (which may degrade the quality), you can
say:
for f in *.mp3; do
title=$(ffmpeg -i "$f" 2>&1 | awk '/title/ {sub(/.*title\s*:\s*/, ""); print; exit}')
mv -i -- "$f" "$title".mp3
done
The last exit is for the edge case the mp3 file includes multiple title metadata.
[Update]
As #llogan comments, the ffprobe is more robust to extract the media information
of the file. Please try instead:
for f in *.mp3; do
title=$(ffprobe -v error -of csv=p=0 -show_entries format_tags=title "$f")
mv -- "$f" "$title".mp3
done

Merging video in a specific order using ffmpeg

I use the following to merge video in numeric order.
for f in *; do mv "$f" "${f: -17}"; done &&. find *.ts|. sed 's:\:\ :g'| sed 's/^/file /' > fraglist.txt && ffmpeg -f concat -safe 0 -i fraglist.txt -c copy output.ts; rm fraglist.txt
This works great for files named like the following...
000001
000002
000003
000004
000005
000006
000007
000008
000009
000010
But If I need something like the following merged the order is based on how many digits there are in the file name...
1708.ts 9803.ts 13798.ts 17815.ts 21804.ts 25819.ts
29832.ts
What command could I use to get the second group of files merged in that order? Thank you for your help!
Simplify your whole process by using printf alone to make the txt file contents, and use sort to provide natural/version sorting:
printf "file '%s'\n" *.ts | sort -V > fraglist.txt
ffmpeg -f concat -i fraglist.txt -c copy output.ts
Result:
file '1708.ts'
file '9803.ts'
file '13798.ts'
file '17815.ts'
file '21804.ts'
file '25819.ts'
file '29832.ts'
No need to rm fraglist.txt as the sort redirect (>) will overwrite fraglist.txt.
Note that I removed -safe 0 (an option specific to the concat demuxer) from the ffmpeg command because you don't need it in this exact example. But if you get the Unsafe file name error (such as due to special characters including spaces) then you will need to add it.

FFmpeg: How to open a file with an apostrophe in its name?

I am trying to concatenate a bunch of audio files with the following command:
ffmpeg -f concat -safe 0 -i Filelist.txt -c copy out.mp3
Unfortunately, one of my audio files has an apostrophe in its name, which are used to indicate the start and end of the filename:
file 'Murfy's in Trouble!.mp3'
giving the following error:
Impossible to open 'Murfy%s'
Filelist.txt: No such file or directory
I tried escaping it with \ and %, but those don't work. Is there any way around this without changing all my filenames?
Escaping ' in a quote needs to be done with '\'', as per the documentation.

How do I resolve ffmpeg concat command error "unknown keyword..."?

I am trying to concatenate two video files using ffmpeg, and I am receiving an error.
To eliminate compatibility issues between the two videos, I have been concatenating the same video with itself, and the same error persists.
ffmpeg \
-f concat \
-safe 0 \
-i intro_prepped.avi intro_prepped.avi \
-c copy \
concat.avi
And the error output I receive is....
[concat # 0x220d420] Line 1: unknown keyword 'RIFFf�?'
intro_prepped.avi: Invalid data found when processing input
I have tried various combinations of concat flags and have not been able to get it to work. Has anyone seen this error before?
This is a bit late for the original post, but I was just searching for answers to the same problem so I think it's still relevant and I haven't found any more recent posts answering the same problem.
I found that my .txt file was encoded wrong. I opened the file in Notepad and did a 'Save As...'
I changed the encoding to UTF-8 and the ffmpeg concat command worked.
Docs for several ways of concatenating files: https://trac.ffmpeg.org/wiki/Concatenate
Here's a command I use to concatenate videos:
ffmpeg \
-i "concat:input1.avi|input2.avi|input3.avi" \
-c:a copy \
-c:v copy \
output.avi
I tried all of the aforementioned and it didn't work.
It looks like that the file names in the list have to be specially formatted to look like:
file '/path/to/file1.wav'
with a word file included. I spend a lot of time trying to guess why ffmpeg encountered an error trying to read the file names. It didn't matter if they were in the list or in the command line. So only after I utilized a command
for f in *.wav; do echo "file '$f'" >> mylist.txt; done
to make list from ffmpeg's manual I had success. The only difference was an additional word file.
Here you can read it yourself: https://trac.ffmpeg.org/wiki/Concatenate#demuxer
Your text file is likely encoded in UTF-16.
Fix: (Windows 10)
Open text file
Select 'Save as'
Look by the save button, you get to pick encoding with a drop down box, select UTF-8.
Save and run ffmpeg again.
I used the Powershell code on ffmpegs webpage to make a text file with filenames, and Powershell seems to save text files as some variant of UTF-16, so I chose the safer UTF-8.
The input file should be a text file, not an avi. The text file lists the files to concatenate.
See the concat demuxer documentation and FFmpeg Wiki: Concat.
Nobody had a full, working, concat text file batch file anywhere. So I am posting it
md ts
for %%x in (input\*.m4a) do (ffmpeg -i "%%x" -c copy -bsf:v h264_mp4toannexb ts\%%~nx.ts)
for %%c in ("ts\*ts") do (echo file '%%c')>>list.txt
for %%f in (input\*.m4a) do (set fn=%%~nf)
ffmpeg -f concat -safe 0 -i "list.txt" -c copy "%cd%\%fn%".m4a
I struggled with all these instructions above on my Mac (M1, Ventura, ffmpeg version N-109530-g4a80db5fc2-tessus). None of them worked for me - but a combination of all did the trick!
This is how I got it running:
place all input files the same folder
create input.txt in this folder - content looks like this:
file 'input1.mp4'
file 'input2.mp4'
file 'input3.mp4'
file 'input4.mp4'
Note:
file encoding must be UTF-8
file keyword must be present
filename must not be fully qualified (I got exceptions using '/path/to/input1.mp4')
filename must be enclosed by '
navigate to this folder in the terminal
execute ffmpeg -f concat -i input.txt -c copy ffmpegOUT.mp4

Need explanation of details of ffmpeg and pipes command

Got the following from FFmpeg FAQ:
mkfifo intermediate1.mpg
mkfifo intermediate2.mpg
ffmpeg -i input1.avi -sameq -y intermediate1.mpg < /dev/null &
ffmpeg -i input2.avi -sameq -y intermediate2.mpg < /dev/null &
cat intermediate1.mpg intermediate2.mpg |\
ffmpeg -f mpeg -i - -sameq -vcodec mpeg4 -acodec libmp3lame output.avi
Before i use or modify it I would like to understand it completely.
What does the < /dev/null & do?
I understand | is pipe but why |\ ?
What is the -f mpeg after ffmpeg (Seems, it tells ffmpeg to accept the piped in output from the cat(?) )
< /dev/null &
This is actually two parts:
< /dev/null
&
1 (< /dev/null) is just a simple way to pass no input/EOF to a program. I'm not sure it's needed but it may be because you are using named pipes.
2 (&) simply pushes the command to the background and allows you to do other things. This is necessary because otherwise, ffmpeg would just sit there waiting for the other end of the named pipe to "open".
Backslash after pipe
The backslash after the pipe is simply there to allow you to enter the long command on multiple lines. If you want to write it on a single line, you should omit the backslash. You'll notice that the prompt changes from your usual [user#machine directory]$ (or whatever) to something like > after you enter the first line (ending with a backslash). This signifies that your command is being continued from an earlier line.
ffmpeg -f switch
The man page for ffmpeg indicates that the -f switch allows you to force a file format. In the example in the FAQ, you want to force an input format (read: tell ffmpeg what input format to expect) since your using piped bits as input. Usually, it would try to guess the input format based on the file extension and/or "file magic".

Resources