Need help with a script for batch processing... I'm a NOOB

Started by flexdiggity1524, December 05, 2023, 04:14:23 PM

Previous topic - Next topic

flexdiggity1524

Hope this post finds everyone well. I'm a NOOB and haven't the slightest idea how to go about creating a script for batch processing videos, in spite of having gone through the forum and the documentation. It's all beyond my current level of understanding. Hence this request for help.

Desired Outcome: process a folder containing a bunch of "mp4" videos where the only change is to shift the audio output of every video by 800ms. I would like to keep the "mp4" format and every other aspect of the video, including original filename, intact. The processed videos can be output to a subfolder called "processed" for simplicity.

In case it matters I'm on a Windows 11 machine and I run Avidemux 2.8.1.

Any guidance with the above would be much appreciated. Thanks in advance for your valuable time.

Regards,
Alex

eumagga0x2a

Quote from: flexdiggity1524 on December 05, 2023, 04:14:23 PMI would like to keep the "mp4" format and every other aspect of the video, including original filename, intact.

Well, stuff like chapters, subtitle tracks and important metadata like display aspect ratio (DAR) won't survive. Chapters and subtitle tracks are entirely unsupported, display aspect ratio can be set in muxer configuration but not extracted from the source video container. Pixel aspect ratio / storage aspect ratio (SAR) can be extracted from VUI (video usability information) part of codec extradata and DAR derived from SAR, but one cannot rely on VUI being set or if set, to agree with the info from container.

Assuming we don't need to handle these complications and all source videos have at most one audio track, the following script should automate the above procedure:


#PY  <- Needed to identify #

adm = Avidemux()
gui = Gui()
ext = "mp4"
sep = "\\"

def processVideo(vidin, outdir):
    if not adm.loadVideo(vidin):
        return 0
    adm.audioClearTracks()
    if adm.audioTotalTracksCount() <= 0:
        return 0
    adm.audioAddTrack(0)
    adm.audioCodec(0, "copy")
    adm.audioSetShift(0, 1, 800)
    adm.videoCodec("Copy")
    adm.setContainer("MP4", "muxerType=0", "optimize=0", "forceAspectRatio=False", "aspectRatio=1", "displayWidth=1280", "rotation=0", "clockfreq=0")
    return adm.save(outdir + sep + basename(vidin))

# -------- select input directory --------
inputFolder = gui.dirSelect("Select source folder")
if inputFolder is None:
    gui.displayError("Error", "No source folder selected")
    return 0

# -------- read content --------
vidlist = get_folder_content(inputFolder, ext)
if vidlist is None:
    gui.displayError("Error", "No " + ext + " files found in \"" + inputFolder + "\"")
    return 0

# -------- select output directory --------
outputFolder = gui.dirSelect("Select output folder")
if outputFolder is None:
    gui.displayError("Error", "No output folder selected")
    return 0

if(inputFolder == outputFolder):
    gui.displayError("Error","Output folder cannot be the same as the input one")
    return 0

success = 0

for video in vidlist:
    success += processVideo(video, outputFolder)

if not success:
    gui.displayInfo("Warning", "No video files processed")
elif success == 1:
    gui.displayInfo("Finished", "One video out of " + str(len(vidlist)) + " processed")
else:
    gui.displayInfo("Finished", str(success) + " videos out of " + str(len(vidlist)) + " processed")

return success

Quote from: flexdiggity1524 on December 05, 2023, 04:14:23 PMI run Avidemux 2.8.1.

You might want to consider updating to the latest 2.8.2 nightly (please note that MinGW compiled "win64" NSIS-packaged builds are currently broken, the latest ZIP-packaged cross-builds should be fine as are "vsWin64" native builds). At least the script above was tested with the current 2.8.2 code.

flexdiggity1524

@eumagga0x2a, First, thank you!!! I'm a little tied up at the moment and will not be able to test out the script you've provided right away. I will do so early next week and report back.

Quote from: eumagga0x2a on December 05, 2023, 08:28:40 PMAssuming we don't need to handle these complications and all source videos have at most one audio track...

I'm pretty sure that your assumptions are correct, including the one about a single audio track. I was manually opening each video in the Avidemux window through drag/drop, manually toggling the Shift checkbox and inputting the 800ms value under Audio Output, manually selecting the MP4 Muxer option under Output Format and then saving. I was not to my knowledge using any of the other options you mentioned.

Quote from: eumagga0x2a on December 05, 2023, 08:28:40 PMYou might want to consider updating to the latest 2.8.2 nightly...

I will make sure to update to the latest nightly build prior to testing the above script. Thanks again for your time, patience and consideration!

Rgds,
Alex

flexdiggity1524

#3
Quote from: eumagga0x2a on December 05, 2023, 08:28:40 PM...the following script should automate the above procedure:

@eumagga0x2a,

First, thanks again for your assistance! The script you sent above worked as expected. I have a follow-up question.

Since my goal was to automate repetitive tasks, I wanted to go a step or two further. With the help of ChatGPT - again I'm a NOOB - I added a function that would return a more specific output filename based on date and time values extracted from the input filename.

At first I noticed that the tinypy implementation within Avidemux does not appear to support python's integer division operator, namely "//". Is this a known issue? I got around that by using math.floor() along with the float-division operator, namely "/". I'm fairly certain that the function and its logic are correct, as I have tested the same extensively within online-python[dot]com. However, when I use Avidemux's Scripting Shell, I'm getting unexpected results. It appears the issue is with the calculation. Is this a known issue?

I will post the code for the function and some test data along with expected vs actual results below.

Rgds,
Alex

flexdiggity1524

#4
Quote from: flexdiggity1524 on December 11, 2023, 04:37:56 PMI will post the code for the function and some test data along with expected vs actual results below.

For your reference, I'm providing below the function in question:

#import math
def processVideoFilename(filename):
    # Extract date and time components
    components = filename.split(' - ')
    date_str = components[0].strip()
    time_component = components[2].split('.')[0].strip().split('-')
    hour_str = time_component[0].strip()
    minute_str = time_component[1].strip()
    second_str = time_component[2].strip()

    # Convert date and time strings to integers
    year = int(date_str[0:4])
    month = int(date_str[4:6])
    day = int(date_str[6:8])
    hour = int(hour_str)
    minute = int(minute_str)
    second = int(second_str)

    # Calculate day of the week using Zeller's Algorithm
    if (month < 3):
        month += 12
        year -= 1
    c = math.floor(year / 100)
    year = year % 100
    h = (math.floor(c / 4) - 2 * c + year + math.floor(year / 4) + math.floor(13 * (month + 1) / 5) + day - 1) % 7
    day_of_week = (h + 7) % 7

    # Check if it's Saturday (6) or Sunday (0)
    if day_of_week == 0:
        # Check the time range and return the corresponding class title
        if (10 * 60 + 57) <= (hour * 60 + minute) <= (11 * 60 + 56):
            return date_str + " - AfroCuban L1 - Arara.mp4"
        elif (11 * 60 + 57) <= (hour * 60 + minute) <= (12 * 60 + 56):
            return date_str + " - Mambo Shines.mp4"
        elif (13 * 60 + 57) <= (hour * 60 + minute) <= (14 * 60 + 56):
            return date_str + " - Salsa L2 PW.mp4"
        elif (14 * 60 + 57) <= (hour * 60 + minute) <= (15 * 60 + 56):
            return date_str + " - Salsa L2 FW.mp4"
        elif (15 * 60 + 57) <= (hour * 60 + minute) <= (16 * 60 + 56):
            return date_str + " - Salsa L1 PW.mp4"
        elif (16 * 60 + 57) <= (hour * 60 + minute) <= (17 * 60 + 56):
            return date_str + " - Salsa L1 FW.mp4"
        else:
            return date_str + " - Private Coaching - .mp4"
    elif day_of_week == 6:
        # Check the time range and return the corresponding class title
        if (10 * 60 + 57) <= (hour * 60 + minute) <= (11 * 60 + 56):
            return date_str + " - AfroCuban L1 - Arara.mp4"
        elif (11 * 60 + 57) <= (hour * 60 + minute) <= (12 * 60 + 56):
            return date_str + " - Mambo PW.mp4"
        elif (13 * 60 + 57) <= (hour * 60 + minute) <= (14 * 60 + 56):
            return date_str + " - Salsa L2 PW.mp4"
        elif (14 * 60 + 57) <= (hour * 60 + minute) <= (15 * 60 + 56):
            return date_str + " - Salsa L2 FW.mp4"
        elif (15 * 60 + 57) <= (hour * 60 + minute) <= (16 * 60 + 56):
            return date_str + " - Salsa L1 PW.mp4"
        elif (16 * 60 + 57) <= (hour * 60 + minute) <= (17 * 60 + 56):
            return date_str + " - Salsa L1 FW.mp4"
        else:
            return date_str + " - Private Coaching - .mp4"
    else:
        return date_str + " - Private Coaching - .mp4"

Here some test data with expected results and the actual results from Avidemux's Scripting Shell - all of which are incorrect:

# Test data
input01 = "20231209 - OBS - 10-57-33.mp4"
input02 = "20231210 - OBS - 12-23-33.mp4"
input03 = "20231202 - OBS - 14-54-33.mp4"
input04 = "20231203 - OBS - 15-12-33.mp4"
input05 = "20231216 - OBS - 16-45-33.mp4"
input06 = "20231217 - OBS - 17-07-33.mp4"
input07 = "20231209 - OBS - 09-23-15.mp4"
input08 = "20231214 - OBS - 12-23-15.mp4"

result01 = processVideoFilename(input01)
result02 = processVideoFilename(input02)
result03 = processVideoFilename(input03)
result04 = processVideoFilename(input04)
result05 = processVideoFilename(input05)
result06 = processVideoFilename(input06)
result07 = processVideoFilename(input07)
result08 = processVideoFilename(input08)

print(result01)
print(result02)
print(result03)
print(result04)
print(result05)
print(result06)
print(result07)
print(result08)

# Results from Avidemux's Scripting Shell, all are incorrect
#20231209 - Private Coaching - .mp4
#20231210 - AfroCuban L1 - Arara.mp4
#20231202 - AfroCuban L1 - Arara.mp4
#20231203 - AfroCuban L1 - Arara.mp4
#20231216 - AfroCuban L1 - Arara.mp4
#20231217 - AfroCuban L1 - Arara.mp4
#20231209 - Private Coaching - .mp4
#20231214 - Private Coaching - .mp4

# Expected results confirmed on online-python[dot]com
#20231209 - AfroCuban L1 - Arara.mp4
#20231210 - Mambo Shines.mp4
#20231202 - Salsa L2 PW.mp4
#20231203 - Salsa L2 FW.mp4
#20231216 - Salsa L1 PW.mp4
#20231217 - Salsa L1 FW.mp4
#20231209 - Private Coaching - .mp4
#20231214 - Private Coaching - .mp4

Any assistance or advice that you can provide with the above would be much appreciated.

Rgds,
Alex

UPDATE: I found one of the culprits. My if statements were not evaluating correctly. When rewriting the if statements using and almost everything worked as expected. For example:

#'Faulty' IF statement
if (10 * 60 + 57) <= (hour * 60 + minute) <= (11 * 60 + 56):

#Working IF statement
if (10 * 60 + 57) <= (hour * 60 + minute) and (hour * 60 + minute) <= (11 * 60 + 56):

I suppose this is due to the inherent limitations of tinypy.

Still one of my test data entries from above, input01 is still incorrect.

# Test data
input01 = "20231209 - OBS - 10-57-33.mp4"
result01 = processVideoFilename(input01)
print(result01)

# Expected
#20231209 - AfroCuban L1 - Arara.mp4

# Actual
#20231209 - Private Coaching - .mp4

It appears to be a calculation error.

eumagga0x2a

#5
The bug is probably in int():

I am not sure whether it is a bug or a feature, but int() interprets leading zero in the argument string as a hint that the string represents an octal number with values >= 8 being out of range. Please try

days = ["01", "02", "03", "04", "05", "06", "07", "08", "09"]
for day in days:
    print(str(int(day)) + " vs " + str(int(day, 10)) + "\n")

to see the difference.

I have no idea how to fix it, but this should give you a clear clue how to work around it.

flexdiggity1524

#6
Quote from: eumagga0x2a on December 11, 2023, 07:01:15 PMint(variable, 10)

@eumagga0x2a,

YES!!! That did the trick. I now have a fully automated windows desktop shortcut that runs Avidemux with the --run script.py parameter. This is going to save me hours over the next several months.

This was a pretty cool learning experience. Thanks again for your attention, time and effort.

Rgds,
Alex

eumagga0x2a