How to modify this Windows script to do a match? - windows

ffmpeg can resize a video/image file .. first is input_file .. parameters .. output_file ..
ffmpeg -i input.avi -vf scale=320:240 output.avi
or
ffmpeg -i 20140724_071746.mp4 -vf scale=640:-1 20140724_071746_LOW_640.mp4
more info here: https://trac.ffmpeg.org/wiki/Scaling%20%28resizing%29%20with%20ffmpeg
I want it to downscale all the videos on my microSD card to create space (I have originals backed-up)
So I want it to go throw all the files in all the sub-directories overnight and resize all the files. Chances are that the script might stop or crash and I would need to run it again, or if I add new files I would want to run it again.
So I would want it to skip processing all the files that have been processed AND their resized versions.
In my case if a FILE_NAME.mp4 also has FILE_NAME_LOW_640.mp4 SKIP it
AND
if a FILE_NAME_LOW_640.mp4 has *640 SKIP it
Here is my Windows batch script so far
REM #echo off
REM just save as "DOS"
REM cd /d C:\s
setlocal enabledelayedexpansion
for %%j in (*.mp4) do (
set filename=%%~nj
echo %%j
ffmpeg -i %%j -vf scale=640:-1 %%j_LOW_640.mp4
REM but now I want to add the two checks to skip files that have been resized .. or if they are the resized version
REM if not "!filename!"=="%%j_LOW_640.mp4" AND IF FILE !COINTAIN *640* THEN ffmpeg -i %%j -vf scale=640:-1 %%j_LOW_640.mp4
)
pause
REM AND I would also want it to process all the sub-directories
In other words my questions for help are:
How can I do a check for a string if it contains a string match?
How can I have my script also process all the subdirectories?

I hope the method be simple enough so it don't needs explanations. You may read further details on FOR parameter modifiers at HELP FOR
#echo off
setlocal EnableDelayedExpansion
for /F "delims=" %%a in ('dir /S /B *.mp4') do (
set "name=%%~Na"
if "!name:~-3!" neq "640" if not exist "%%~DPNa_LOW_640.mp4" (
ffmpeg -i "%%a" -vf scale=640:-1 "%%~DPNa_LOW_640.mp4"
)
)

for /f "delims=" %A in ('dir /b /s c:\somewhere\*.mp4') do (findstr /c:"_640" %A || Echo ffeg etc %A)
|| means run command if findstr returns an errorlevel of not 0, which it does if the string isn't found. /s in dir does subfolders.
Use %%A in batch and %A when typing.
See Novice Batch Issue- Creating Files for a list of command punctuation.
And a vbs script walking the tree. Run it with cscript in a command prompt(cscript //nologo "c:\some path\script.vbs". Change starting folder for your machine (replace the c:\users\david candy\documents).
On Error Resume Next
Set fso = CreateObject("Scripting.FileSystemObject")
ProcessFolder "c:\users\david candy\documents"
Sub ProcessFolder(FolderPath)
Set fldr = fso.GetFolder(FolderPath)
Set Fls = fldr.files
For Each thing in Fls
wscript.echo thing.name
Next
Set fldrs = fldr.subfolders
For Each thing in fldrs
wscript.echo thing.name
ProcessFolder thing.path
Next
End Sub

Related

Need to run GPSBabel command on txt files

I'm using windows 10. My current project requires me to merge several TXT files into a single one. Problem is that the TXT files has all the same name and what differentiate them is their folder name.
For example:
Folder1
Folder with a 13 digits random name
gpsdata.txt
Folder with a 13 digits random name
gpsdata.txt
- Folder with a 13 digits random name
gpsdata.txt
I tried using the following BATCH command but it overwrites the file and I end with only the track from the last gpsdata.txt file.
FOR /R C:\FOLDER1 %%i IN (metadata.txt) DO X:\GPSBabel\gpsbabel -t
-i xcsv,style=C:\mystyle.style -f %%i -o gpx -F C:\FOLDER2\gpsdata.gpx
Is there a way to 'copy and merge' these txt files and transform then into gpx via .BAT using GPSBabel?
thanks
The FOR loop does not seem to be producing what you want. Try constructing the FOR loop alone until %%i comes out as you expect.
FOR /R C:\FOLDER1 %%i IN (metadata.txt) DO (ECHO %%~i)
You might want the FOR loop to produce a path to every metadata.txt file. Does this produce what you are seeking?
FOR /F "delims=" %%i IN ('DIR /S /B C:\FOLDER1\metadata.txt') DO (echo %%i)
That might make the code look like:
FOR /F "delims=" %%i IN ('DIR /S /B "C:\FOLDER1\metadata.txt"') DO (
X:\GPSBabel\gpsbabel -t -i xcsv,style=C:\mystyle.style -f "%%~i" -o gpx -F C:\FOLDER2\gpsdata.gpx
)
Do not use for /R, neither do use for /F with dir /S, since you have got a flat directory structure. What you need is for /D, or for /F together with dir in order to control sorting.
Approach using for /D (sub-directories are iterated in the order as returned by the file system):
rem // Prepare an empty file to begin with:
copy nul "C:\FOLDER1\metadata.txt" > nul
rem // Iterate over the immediate sub-directories:
for /D %%I in ("C:\FOLDER1\?????????????") do (
rem // Check whether the current sub-directory contains the text file:
if exist "%%~I\gpsdata.txt" (
rem // Append the text file to the result file:
copy /B "C:\FOLDER1\metadata.txt" + "%%~I\gpsdata.txt" "C:\FOLDER1\metadata.txt" > nul
)
)
Approach using for /F and dir (sub-directories are sorted in alphabetic manner due to /O:N):
rem // Change into target directory:
pushd "C:\FOLDER1" && (
rem // Prepare an empty file to begin with:
copy nul "metadata.txt" > nul
rem // Iterate over the immediate sub-directories:
for /F "delims=" %%I in ('dir /B /A:D-L-H-S /O:N "?????????????"') do (
rem // Check whether the current sub-directory contains the text file:
if exist "%%I\gpsdata.txt" (
rem // Append the text file to the result file:
copy /B "metadata.txt" + "%%I\gpsdata.txt" "metadata.txt" > nul
)
)
rem // Return from target directory:
popd
)
For those that may need to convert using GPSBabel on several txt files with the same name inside different subfolders to get a GPX out of them, I was able to perform this with the following batch file:
setlocal enabledelayedexpansion
FOR /F "delims=" %%i IN ('DIR /S /B C:\FOLDER1\metadata.txt') DO "X:\GPSBabel\gpsbabel.exe" -t -i xcsv,style=C:\mystyle.style -f "%%i" -x discard,hdop=10,vdop=20 -x duplicate,location -x simplify,crosstrack,error=0.001k -o gpx -F "%%i".gpx
set f=
for /F "delims=" %%f IN ('DIR /S /B C:\FOLDER1\*.gpx') DO set f=!f! -f "%%f"
"X:\GPSBabel\gpsbabel.exe" -t -i gpx %f% -x discard,hdop=10,vdop=20 -x duplicate,location -x simplify,crosstrack,error=0.001k -x nuketypes,waypoints -o gpx -F C:\FOLDER2\%DATE:~6,4%_%DATE:~3,2%_%DATE:~0,2%_LOG.gpx
The first part just create a GPX file for every TXT file inside their same folder.
The second part is taking all those GPX file and merging them into a single one on a different folder setting the name of the file as "current_date"_log.gpx

Windows batch program NOT EXIST not functioning

I have written a simple batch file to convert movies in a folder. The problem is, it never stops running. It keeps processing the next movie (which it just created). Here is the code:
#echo off & setlocal
FOR /r %%# in (*.mkv) DO IF NOT EXIST "%%~dpn#_265.mkv" (Title %~nx0 Processing: %%~f#
ffmpeg -i "%%~f#" -c:v libx265 -vtag hvc1 -map_metadata 0 -max_muxing_queue_size 1000 "%%~dpn#_265.mkv")
PAUSE
The file to convert is c:\tmp\c.mkv.
The code creates the first file c:\tmp\c_265.mkv, then creates a second file from that one called c:\tmp\c_265_265.mkv, and so on.
Your main issue with your code is likely to be that you're checking for the existence of every mkv file in the same directory, but with _265 appended to the basename. So in your example %%# would resolve to c:\tmp\c_265.mkv and you'd be checking for the existence of c:\tmp\c_265_265.mkv, which probably isn't the only check you needed to make. You probably wanted to check to ensure that the last four characters of %%# were not already _265 too.
Without changing your for /r methodology, (I'd go with a for /f with findstr idea similar to Gerhard's comment), you could do it like this:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
For /R "." %%G In (
"*.mkv"
) Do If /I "%%~xG" == ".mkv" If Not Exist "%%~dpnG_265%%~xG" (
Set "FileBaseName=%%~nG"
SetLocal EnableDelayedExpansion
If Not "!FileBaseName:~-4!" == "_265" (
"ffmpeg.exe" -i "%%G" -c:v libx265 -vtag hvc1 -map_metadata 0 -max_muxing_queue_size 1000 "%%~dpnG_265%%~xG"
)
EndLocal
)
Pause

DOS batch file For loop runs twice

I have a batch file that recursively encodes videos to a sub folder "encode" then deletes the original and moves the new file to the original directory. The problem I'm having is that after each video is encoded, the for loop picks up the newly encoded video and runs again. After that, it moves on to the next video. I'm not sure why it runs twice instead of once or infinitely. What am I misunderstanding? I am a complete novice, so my apologies if it's a simple mistake.
#echo off
set /A count = 0
pushd %~dp0
for /R %%f in (*.mp2, *.mpg, *.vob, *.avi, *.wmv, *.mov, *.mp4, *.m4v, *.mpeg) do (
mkdir %%~dpf\encode
C:\HandBrakeCLI -i "%%f" -o "%%~dpf\encode%%~nf.mp4"
del "%%f"
move "%%~dpf\encode\%%~nf.mp4" %%~dpf
rmdir %%~dpf\encode
set /A count+=1
)
popd
echo Count is: %count%
pause
The reason it processes twice is because the file conversion creates another video clip that matches the criteria in the FOR loop.
To avoid dealing with folder structures and syncing, a simple way to get around this is to split the process into two loops, performing the *.mp4 files first:
#echo off
set /A count = 0
pushd %~dp0
for /R %%f in (*.mp4) do (
mkdir %%~dpf\encode
C:\HandBrakeCLI -i "%%f" -o "%%~dpf\encode%%~nf.mp4"
del "%%f"
move "%%~dpf\encode\%%~nf.mp4" %%~dpf
rmdir %%~dpf\encode
set /A count+=1
)
for /R %%f in (*.mp2, *.mpg, *.vob, *.avi, *.wmv, *.mov, *.m4v, *.mpeg) do (
mkdir %%~dpf\encode
C:\HandBrakeCLI -i "%%f" -o "%%~dpf\encode%%~nf.mp4"
del "%%f"
move "%%~dpf\encode\%%~nf.mp4" %%~dpf
rmdir %%~dpf\encode
set /A count+=1
)
popd
echo Count is: %count%
pause
Don't forget to remove the *.mp4 from the second loop's file list.
Edit: Here's a solution that's more elegant.
#echo off
set /A count = 0
pushd %~dp0
for %%e in (mp4 mp2 mpg vob avi wmv mov m4v mpeg) do (
for /R %%f in (*.%%e) do (
mkdir %%~dpf\encode
C:\HandBrakeCLI -i "%%f" -o "%%~dpf\encode%%~nf.mp4"
del "%%f"
move "%%~dpf\encode\%%~nf.mp4" %%~dpf
rmdir %%~dpf\encode
set /A count+=1
)
)
popd
echo Count is: %count%
pause
Make sure the mp4 is listed first so it's not reconverted once the other file types are processed.
I've tested it to see why it behaves so strangely like this, well, and the only answer I could come up with is that it simply acts strangely. my guess is that it depends on the order of operations.
this is my take on it:
in some implementations of the code, the for /r avoids an infinite loop. what it does, is run on the files that are not .mp4 first. then, probably because it was renamed, it doesn't recognize it as the same file.
probably, after it runs on the .mp4, the second time, it's file inode (except for the last time opened and edited) shouldn't be different at least to the point where the system won't recognize it as the same file anymore.
I tested a bit different version of the code that did produce an infinite loop:
set /A count = 0
pushd %~dp0
for /R %%f in (*.mp2, *.mpg, *.vob, *.avi, *.wmv, *.mov, *.mp4, *.m4v, *.mpeg) do (
mkdir %%~dpf\encode
move %%f %%~dpf\encode
rename %%~dpf\encode\%%f %%~dpf\encode\%%~nf.mp4
move %%~dpf\encode\%%~nf.mp4 %%~dpf
rmdir %%~dpf\encode
set /A count+=1
)
popd
echo Count is: %count%
pause
and strangely, not only it ran an infinite loop, but it also created an infinite amount of subfolders named '\encode\encode\encode...', but the reason for that doesn't matter.
what's different in this version of the code, is that I always rename the files.
that is probably why it produces an infinite loop.
the for /r command reiterates files that have been renamed, and not necessarily on those edited.
the solution seems to be, indeed, to run the code on all .mp4 files first, and the other ones second.

Batch: Looping through folders and subfolders and get them into a script

I want to convert .flac files from X:\Music\flac\flacfolder\name.flac to X:\Music\mp3\flacfolder\name.mp3 using ffmpeg, but I couldn't find how to loop through while passing the directory to a different command and manipulating it.
Usually, to process files recursively, I would suggest to use the for /R loop. However, in this situation, since I guess you want to copy the directory hierarchy from the source to the target folder, I do not use it, because it resolves to absolute paths only. Instead I use xcopy /L, which does not copy anything (due to /L), but lists all applicable items as paths relative to the source folder; then I wrap around a for /F loop to read the list of relative paths and to resolve them related to the target folder; in the loop body finally, the ffmpeg needs to be placed (define the options to your needs and remove the preceding upper-case ECHO after having tested; the ffmpeg tool does not receive any relative paths but absolute ones only for both input and output files):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_SOURCE=X:\Music\flac\flacfolder" & rem // (absolute source path)
set "_TARGET=X:\Music\mp3\flacfolder" & rem // (absolute target path)
set "_PATTERN=*.flac" & rem // (pure file pattern for input files)
set "_FILEEXT=.mp3" & rem // (pure file extension of output files)
pushd "%_TARGET%" || exit /B 1
for /F "delims=" %%F in ('
cd /D "%_SOURCE%" ^&^& ^(rem/ list but do not copy: ^
^& xcopy /L /S /Y /I ".\%_PATTERN%" "%_TARGET%" ^
^| find ".\" ^& rem/ remove summary line;
^)
') do (
2> nul mkdir "%%~dpF."
rem // Set up the correct `ffmpeg` command line here:
ECHO ffmpeg -i "%_SOURCE%\%%~F" "%%~dpnF%_FILEEXT%"
)
popd
endlocal
exit /B
If you want the destination files in a flat folder structure instead, a for /R loop workes fine:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_SOURCE=X:\Music\flac\flacfolder" & rem // (absolute source path)
set "_TARGET=X:\Music\mp3\flacfolder" & rem // (absolute target path)
set "_PATTERN=*.flac" & rem // (pure file pattern for input files)
set "_FILEEXT=.mp3" & rem // (pure file extension of output files)
for /R "%_SOURCE%" %%F in ("%_PATTERN%") do (
rem // Set up the correct `ffmpeg` command line here:
ECHO ffmpeg -i "%_SOURCE%\%%~F" "%_TARGET%\%%~nF%_FILEEXT%"
)
endlocal
exit /B
Try something like:
#echo off
setlocal
set FLAC_FOLDER=c:\temp\flacfolder
set MP3_ROOT_FOLDER=c:\temp\mp3folder
echo Processing folder [%FLAC_FOLDER%]...
for /f "tokens=*" %%F in ('dir "%FLAC_FOLDER%\*.flac" /a-d /b') do call :PROCESS_FLAC_FILE "%FLAC_FOLDER%" "%%F"
goto END
:PROCESS_FLAC_FILE
set PFF_FOLDER=%1
set PFF_FILE=%2
set PFF_FOLDER=%PFF_FOLDER:"=%
set PFF_FILE=%PFF_FILE:"=%
for /f %%I in ("%PFF_FILE%") do set PFF_MP3_FILE=%%~nI.mp3
echo Processing FLAC file [%PFF_FILE%] in folder [%PFF_FOLDER%]; output file is [%PFF_MP3_FILE%]...
REM Now call ffmpeg using the approriate variables. Enclose the variables in double-quotes, e.g.:
REM (note, I don't know the syntax for ffmpeg, so I'm making this up as an example)
ffmpeg.exe -source "%PFF_FOLDER%\%PFF_FILE%" -target "%MP3_ROOT_FOLDER%\%PFF_MP3_FILE%"
goto END
:END
I have an example I used in the past. I tried adding your structure to it.
#echo off
cd X:\Music\flac\flacfolder
for /F "tokens=1 delims=" %%i IN ('dir /s /b ^| findstr .flac') do (
call :process_code "%%i"
)
goto end
:process_code
echo Running conversion for %1
:: Run your process here
goto :eof
:end
echo done!
I hope this helps

Delete files less than a specific size

I would like to delete all files that are less than a specific size in a directory. Does anyone know if there is a Windows command that will do this? something like del *.* where size<3kb
I am currently doing this:
for /F %%A in ("*.pdf") do If %%~zA LSS 20103409 del %%~fA
and the ouput I get is:
C:\Documents and Settings\agordon\Desktop\test>If 6440450 LSS 20103409 del C:\Do
cuments and Settings\agordon\Desktop\test\US Tox 01-06-11.pdf
The system cannot find the path specified.
...even though that PDF file is small enough to be deleted.
What am I doing wrong?
This is actually working:
FOR %%F IN (*.pdf) DO (
IF %%~zF LSS 20103409 DEL %%F
)
However it is not recognizing the file names because they have spaces! How do I convert the Windows name to a "DOS" name in that script? For example, the Windows name is file name.pdf I would probably need to convert to "DOS" and it would look like this file_name.pdf or something like that.
Try this from a batch script:
#echo off
setlocal
for /f "usebackq delims=;" %%A in (`dir /b *.pdf`) do If %%~zA LSS 3145728 del "%%A"
BTW: Using PowerShell (on Windows 10) was the easier way for me:
I only did this on the directory where I wish to delete files smaller than 20MB:
ls | where {$_.Length -lt 20mb} | Remove-Item
Ok so I used AtomicParsley in a cmd script to add artwork to all my MP4 movies in over 400 sub folders with the --overWrite option. It sometimes barfs and the orignal file is left at less than 2k. I needed a way to find all these messed up MP4 files so I can redownload them.
This is what I used from the command line using Mrchief's example and it works like a charm in windows 7.
#for /f "usebackq delims=;" %A in (`dir /s /b *.mp4`) do #If %~zA LSS 2048 echo "%A"
and this is the output after processing 32 of the files
D:\Movie>#for /f "usebackq delims=;" %A in (`dir /s /b *.mp4`) do #If %~zA LSS 2048 echo "%A"
"D:\Movie\Action\Deadland (2009)\Deadland (2009).mp4"
"D:\Movie\Science Fiction\Alien3 (1992)\Alien3 (1992).mp4"
D:\Movie>
you could replace the echo with del and change the 2048 to search for a different size.
Here is a different approach using robocopy and its filter capabilities. Here is an excerpt of the File Selection Options shown when robocopy /? is typed into the command prompt window:
/MAX:n :: MAXimum file size - exclude files bigger than n bytes.
/MIN:n :: MINimum file size - exclude files smaller than n bytes.
/MAXAGE:n :: MAXimum file AGE - exclude files older than n days/date.
/MINAGE:n :: MINimum file AGE - exclude files newer than n days/date.
/MAXLAD:n :: MAXimum Last Access Date - exclude files unused since n.
/MINLAD:n :: MINimum Last Access Date - exclude files used since n.
(If n < 1900 then n = n days, else n = YYYYMMDD date).
Hence the /MIN and /MAX options can be applied here. Since we do not want to copy any files, use the /L option to list all the items that would be copied without the switch, then parse the returned list by a for /F loop, which holds the actual deletion command del in the body:
set "TARGETDIR=."
set "FILES=*.pdf"
for /F "tokens=*" %%F in ('
robocopy "%TARGETDIR%" "%TARGETDIR%" "%FILES%" ^
/L /IS /FP /NC /NS /NDL /NP /NJH /NJS ^
/MIN:0 /MAX:3071
') do (
ECHO del "%%F"
)
After having tested, remove the upper-case ECHO from the script to actually delete files.
Besides /MIN, /MAX and /L, there are several other options defined in the robocopy command line, most of which care for the needed output, namely a simple list of full paths of matching files, without any extra information like headers, footers or summaries.
The source and destination directories are both set to our target directory. Normally this would not work, of course (you cannot copy files onto themselves), but since /L is stated, the file list is generated, but only if the switch /IS is given too (meaning that "same files" are to be regarded).
There isn't a switch in the Del command which will delete files based on filesize.
I am not sure how to do this from DOS, but this is something we have used in the past; it uses VBScript, so you would call this filesoversize.vbs.
Dim strPath
Dim lngSize
Dim fso
Dim fold
Dim files
Dim file
lngSize = 10000 ' The threshold in bytes (this is 100k)
strPath = "C:\temp\" 'The folder you want to start in
Set fso = CreateObject("scripting.filesystemobject")
Set fold = fso.GetFolder(strPath)
Set files = fold.files
For Each file In files
If file.Size <= lngSize Then file.Delete True
Next

Resources