1

I am trying to copy attributes for *.mkv *.mp4 video files. I have found a way to copy file attributes, but it is quite slow as it is calling Powershell to use it commands.

powershell ^(ls '!orig-file!'^).CreationTime = ^(ls '%%I'^).CreationTime
powershell ^(ls '!orig-file!'^).LastWriteTime = ^(ls '%%I'^).LastWriteTime
powershell ^(ls '!orig-file!'^).LastAccessTime = ^(ls '%%I'^).LastAccessTime
...................
%%I equals new-file for each FOR loop iteration

Is there a way to do this faster without Powershell?

Otherwise, I assume it would be possible to only call Powershell once and copy all 3 or more attributes in 1 line? I'am assuming that reading each file attribute individually is the only way to copy, but I would prefer all attributes if there are any more to copy.

EDIT: FULL CODE: (with all remarked/commented lines removed) ( Batch Convert Videos audio from one format to another )

@ECHO OFF
SETlocal

SET drive="~dp0"
SET string=%CD%  

SETlocal EnableExtensions EnableDelayedExpansion
SET "drive=G:"
SET ^"Exclude-AlltheseFolders=#snapshot,output^"
SET "Convert_Audio1=Y"
SET "Convert_TO=aac"
SET "Codec1=eac3"
SET "Codec2=flac"
SET "Codec3=dts" &SET "Codec4=pcm_s24le" &SET "Codec5=aac" &SET "Codec6=ac3"
SET "KeepExtraAudio_NoOrig=Y"
SET "KeepOrigAudio=N"
SET "OverWriteFiles=N"
SET "ExtractTime=Y"
SET "time_to_start_from=00:00:10" &SET "duration_to_capture=00:00:15"
SET "TempFilePath=C:\Windows\Temp\ffmpeg-CountingScript-AllFilesInCurrentDirlist%DT%--%HH%.%MM%.%SS%.txt"

IF !ExtractTime! == Y ( SET "FFmpgextractTime= -ss !time_to_start_from! -t !duration_to_capture!")
ECHO THIS IS JMK1
IF !OverWriteFiles! == Y (SET "OverWrite=-y ") ELSE (SET "OverWrite=-n ")
IF !Convert_Audio1! == Y (
    IF !Convert_TO! == aac ( set "Convert_TO=libfdk_aac" )
    SET "FFmpgConvertStream1=-c:a:0 !Convert_TO! -b:a:0 640k -disposition:a:0 default"
    IF !KeepExtraAudio_NoOrig! == Y (
        SET "FFmpgXtraStream=-c:a copy"
    )
    IF !KeepOrigAudio! == Y (
        SET "KeepOrigAudio_ffMpg=-map 0:a:0? -c:a:0 copy"
        SET "KeepExtraAudio_NoOrig=Y"
    )
) ELSE (
    SET "ConvertCodecs=1" 
    SET "KeepOrigAudio_ffMpg=-c:a:0 copy"
    SET "KeepExtraAudio_NoOrig=Y"
)
SET "delm1=^\^>" /c:"^\^<"
SET ^"Exclude-Final=/c:"\^<!Exclude-AlltheseFolders!\^>"^"
SET ^"Exclude-Final=!Exclude-Final:,=%delm1%!^"

dir *.mkv *.mp4 /A-D-H /B /S |findstr /R %Exclude-Final% /v /i>%TempFilePath%
Echo:   These are the folders being Excluded:           "!Exclude-AlltheseFolders!"
SET "FINALCOMMAND="

SETlocal EnableExtensions DisableDelayedExpansion
SET "ProgramFolder=C:\Program Files\FFmpeg-v2020\bin"
SET "ProbeOptions=-v quiet -select_streams a:0 -show_entries "stream^^=codec_name" -of json"
SET "FilesFound=0" & SET "FilesEncoded=0" & SET "output="
for /F "delims=" %%I in (%TempFilePath%) do (
    SET "output=%drive%%%~pI%%~nxI" & SET folder=%drive%%%~pI & SET "filename=%%~nxI" & SET /A FilesFound+=1
    SETlocal EnableExtensions EnableDelayedExpansion
    IF exist "%drive%%%~pIoutput\%%~nxI" (SET "Convert_Audio1=N") ELSE SET "Convert_Audio1=Y"
    IF "!Convert_Audio1!" == "Y" (
        SET "AudioCodec=" & SET "ConvertCodecs="
        for /F "eol={ tokens=1,2 delims=,:[ ]{} " %%B in ('""%ProgramFolder%\ffprobe.exe" %ProbeOptions% "%%I""') do (
            IF "%%~B" == "codec_name" (
                IF not defined AudioCodec (
                    SET "AudioCodec=%%~C"
                )
                IF "%%~C" == "%Codec1%" (SET "ConvertCodecs=1"
            ) else IF "%%~C" == "%Codec2%" (SET "ConvertCodecs=1" & ECHO Codec is: %%~C
            ) else IF "%%~C" == "%Codec3%" (SET "ConvertCodecs=1" & ECHO Codec is: %%~C
            ) else IF "%%~C" == "%Codec4%" (SET "ConvertCodecs=1" & ECHO Codec is: %%~C
            ) else IF "%%~C" == "%Codec5%" (SET "ConvertCodecs=1" & ECHO Codec is: %%~C
            ) else IF "%%~C" == "%Codec6%" (SET "ConvertCodecs=1" & ECHO Codec is: %%~C
            )
        )
    )
)

IF !ConvertCodecs! == 1 (
    ECHO [91m==!TIME!================[0m!Codec1! [94min[0m- %%I [91m=========[0m
)

IF !ConvertCodecs! == 1 (
    IF /I "!output!" == "%%I" (
        SET "output=%~dp0output\!filename!"
        MKDIR "%~dp0output\"
    ) ELSE (
        MKDIR "%drive%%%~pI"
    )
        SET "FINALCOMMAND=ffmpeg !OverWrite!-hide_banner%FFmpgextractTime% -loglevel quiet -hwaccel auto -stats -i "%%I" -map 0:v -map_metadata 0 -movflags use_metadata_tags -map 0:a? -map 0:s:0? -c:s:0 copy -c:v copy %FFmpgXtraStream% %FFmpgConvertStream1% %KeepOrigAudio_ffMpg% "!output!" "
        ECHO this is the command1: !FINALCOMMAND!
        !FINALCOMMAND!
        ECHO [91m=====[0mCOMPLETE[91m===============[0m .
        IF not %FilesEncoded% == 0 ECHO     This one was a failure. Count Encoded so far: "%FilesEncoded%"
        IF not errorlevel 1 SET /A FilesEncoded+=1
        powershell ^(ls '!output!'^).CreationTime = ^(ls '%%I'^).CreationTime
        powershell ^(ls '!output!'^).LastWriteTime = ^(ls '%%I'^).LastWriteTime
        powershell ^(ls '!output!'^).LastAccessTime = ^(ls '%%I'^).LastAccessTime

        for /F "delims=" %%p in ("!ConvertCodecs!") do (
            ECHO THIS IS P: %%p
            ENDLOCAL
            SET /A FilesEncoded=%%p+FilesEncoded
        )

    ) ELSE ( ECHO ##NOT PROCESSING##: !TIME! - %%I ## & ECHO [91m=====--[0mFILE ALREADY EXISTS! [91m======================[0m Next File [91m===========================[0m )
    )
)

IF %FilesFound% == 1 ( SET "PluralS=" ) else SET "PluralS=s"
ECHO [91m***************************************************************************************[0m
ECHO Re-encoded %FilesEncoded% of %FilesFound% video file%PluralS%.
ECHO [91m***************************************************************************************[0m
endlocal
endlocal
endlocal
exit /b
hsgg4
  • 31
  • 1
  • 5
  • Why use cmd.exe at all? PowerShell is much, much better. – Bill_Stewart Jan 19 '23 at 20:30
  • 1
    Can you add the `%%I` loop part of your batch script? You could at least combine all 3 powershell commands into one with semicolons `;` – Cpt.Whale Jan 19 '23 at 22:04
  • I am only familiar with batch and it seems easier. The '%%I' is way too long to include. What would the combined powershell comand look like? Is this is: ` powershell ^(ls '!output!'^).CreationTime = ^(ls '%%I'^).CreationTime | ^(ls '!output!'^).LastWriteTime = ^(ls '%%I'^).LastWriteTime ` – hsgg4 Jan 19 '23 at 22:25
  • 1
    Don't include it, and we cannot help you with it. The choice is yours. However, I will tell you that the task cannot be done using built-in utilities and batch file commands alone, it will need to call on another scripting language and/or a third party utility. – Compo Jan 19 '23 at 22:32
  • Ok, Full code added. My goal overall was to make the batch more efficient and faster. – hsgg4 Jan 19 '23 at 22:39
  • 1
    I have removed a boat load of lines from your full code, i.e. those which have broken labels and remarks. This was necessary to make your code more readable, and less likely to turn off potential helpers. – Compo Jan 20 '23 at 10:32
  • 1
    "I am only familiar with batch and it seems easier." In the long run, time spent learning PowerShell will definitely be worth it and will pay off. My recommendation is to make the leap. – Bill_Stewart Jan 20 '23 at 16:51

1 Answers1

1

The following copies the specified attributes using a single powershell.exe call:

  • Note the use of gi, a built-in alias of the Get-Item cmdlet.

    • ls - on Windows - is (unfortunately) a built-in alias of the Get-ChildItem cmdlet (whose use would work here too, and whose PowerShell-idiomatic alias is gci); however, such aliases - named for other shells' / platforms' built-in commands / utilities are best avoided - see the bottom section of this answer for more information.
  • Enclosing the entire (implied) -Command argument in "..." obviates the need for ^-escaping of individual chars.

powershell "$target = gi '!orig-file!'; $source = gi '%%i'; 'CreationTime', 'LastWriteTime', 'LastAccessTime' | foreach { $target.$_ = $source.$_ }"

Note that the above could still fail in the following cases:

  • If a file name happens to contain ' itself; if so, use \""...\"" (sic) instead of '...'

  • If a file name happens to contain [ and ]; if so, use gi -LiteralPath instead of just gi, which implies gi -Path.

mklement0
  • 382,024
  • 64
  • 607
  • 775